以美图的图片处理为例子,学习JS中对图片的处理。处理图片时需要使用canvas

1 图片的跨域

使用canvas处理图片,首先需要加载图片,如果是在线图片需要针对图片进行跨域的处理。具体的处理方法是:

在图片服务器上设置跨域头,并且在前端加载图片之前将<img>标签的crossOrigin设置为*

这样做的原因是:

尽管不通过CORS就可以在画布中使用图片,但是这会污染画布。一旦画布被污染,你就无法读取其数据。例如,你不能再使用画布的toBlob(), toDataURL()getImageData()方法,调用它们会抛出安全错误。(MDN Web docs)

还要注意的是:

  1. crossOrgin只有在线上图片时才设置,本地图片或者是base64图片不能设置,否在某些系统会报错,导致图片加载失败
  2. 当项目为本地包环境时,例如内置于App中时,crossOrigin值无效,webview的安全机制会导致无论该值设置与否,都会报跨域的错误。解决办法是:需要将所有图片转换成base64才能正确绘制;
  3. crossOrigin值一定要在图片加载之前设置,即为<img>赋值src之前进行设置,否则无效。

我的图片是存储在七牛云的,七牛对图片的跨域进行了默认的处理:

所以,一个Promise话的图片加载函数:

function loadImage(src) {return new Promise((resolve, reject) = > {let img = new Image();// 在线图片设置crossOrigin跨域if (src.indexOf(src) === 0) {img.crossOrigin = '*';}img.src = src;img.onload = () = > {resolve(img)};img.onerror = () = > {reject(new Error('图片解析失败'))}})
}

2 图片的缩放

图片缩放最常见的场景是图片的压缩,在保证清晰的前提下合理的缩小图片尺寸,能大大的降低图片的大小。

新建一个canvas画布,将宽高设置为需要压缩的尺寸,要注意的是需要保证图片的比例,所以canvas的尺寸是通过计算得出的。

2.1 drawImage

// 创建图片
const src = 'http://pblesaqy5.bkt.clouddn.com/18-7-27/52991435.jpg';
const img = await loadImage(src);// 计算画布尺寸
const canvas = document.querySelector('#canvas');
const imgRatio = img.naturalWidth / img.naturalHeight;
const ctxWidth = 300;
const ctxHeight = ctxWidth / imgRatio;
canvas.width = ctxWidth;
canvas.height = ctxHeight;// 绘制图片
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, ctxWidth, ctxHeight);

ctx.drawImage(image, dx, dy, dw, dh)来讲图片画入canvas中,这个方法最多可以接收9个参数, 实现压缩,只需要使用其中的5个参数即可, 其余参数在其它部分使用到时再做详解:

  • image : 需要绘制的图片源,需要接收已经加载完成的HTMLImageElement,HTMLCanvasElement或者HTMLVideoElement;
  • dx / dy : 相对于画布左上角的绘制起始点坐标;
  • dw / dh : 绘制的宽度和高度,宽高比例并不锁定,可使图片变形;

2.2 toDataURL

然后使用canvas.toDataURL(type, quality):方法将图片转为base64格式的图片,

其中:

  1. type: 图片格式, 一般可以使用image/png或者image/jpeg, 当图片不包含透明时,建议使用jpeg,可使导出的图片大小减小很多;
  2. quality: 图片质量,可使用0~1之间的任意值;经过测试,该值设置成0.9时较为合适,可以有效减小图片文件大小且基本不影响图片清晰度,导出后的base64既为压缩后的图片
// 转为base64
const b64 = canvas.toDataURL('image/jpeg', 1);

3 将图片转为Base64

我们常用的图片上传功能,我们使用的是原生的<input type="file">标签,此时获取到的是File格式的图片,图片的格式各异且尺寸很大,我们应该压缩处理后再使用。

三种方式,一种方式就是上面提到的,利用canvas来实现,另一种是利用fileReader实现,还有一种是使用createObjectURL

3.1 fileReader

// 利用fileReader预览图片
function previewImage(inputEle, preview) {inputEle.onchange = function(e) {const file = e.target.files[0];if (!file) {preview.src = '';return}const fileReader = new FileReader();fileReader.readAsDataURL(file);fileReader.onload = t = > {preview.src = t.target.result;}}
}

3.2 createObjectURL

Window.URL属性返回一个对象,它提供了用于创建和管理对象URLs的静态方法。它也可以作为一个构造函数被调用来构造URL对象。

URL.createObjectURL()静态方法会创建一个DOMString,其中包含一个表示参数中给出的对象的URL。这个URL的生命周期和创建它的窗口中的document绑定。这个新的URL对象表示指定的File对象或Blob对象。

这是一个实验性的功能,浏览器兼容性如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mjSkudff-1574131427980)(http://pblesaqy5.bkt.clouddn.com/18-7-30/40640110.jpg)]

function previewImage2(inputEle, preview) {inputEle.onchange = function(e) {const file = e.target.files[0];if (!file) {preview.src = '';return}preview.src = window.URL.createObjectURL(file)}
}

3.3 使用canvas

就如同上面说的一样,先通过drawImage绘制图片,然后通过toDataURL转为base64格式

要注意的是,图片的EXIF信息中的方向值会影响图片的展示,在IOS会出现图片的宽高和图片的方向不匹配的问题,需要特殊处理:

  1. 可以使用exif.js来获取图片信息中的Orientation属性,利用canvas的绘制来矫正
  2. 可以使用canvasResize.js来解决File到base64的一系列问题。

4 图片的剪裁

通过drawImage来进行剪裁:

// 居中剪裁
function clipImage(img, ops) {// 图片原始尺寸const imgNaturalWidth = img.naturalWidth;const imgNaturalHeight = img.naturalHeight;// 剪切尺寸,默认值为原图狂傲let clippedWidth = ops.width || imgNaturalWidth;let clippedHeight = ops.height || imgNaturalHeight;let clippedRatio = clippedWidth / clippedHeight;// 居中剪裁的坐标let dx = (imgNaturalWidth - clippedWidth) / 2;let dy = (imgNaturalHeight - clippedHeight) / 2;// 创建画布,并设定尺寸为剪切后的尺寸let cvs = document.createElement('canvas');const ctxWidth = 300;const ctxHeight = ctxWidth / clippedRatio;cvs.width = ctxWidth;cvs.height = ctxHeight;// 绘制图片let ctx = cvs.getContext('2d');ctx.drawImage(img, dx, dy, clippedWidth, clippedHeight, 0, 0, ctxWidth, ctxHeight);// 返回base64图片return cvs.toDataURL('image/jpeg', 0.9)
}

5 合成

参考这个吧,弄旋转弄了半天也没成,没兴趣了,有时间再说。

6 水印

水印有几种方式可以生成,第一种就是使用canvas,第二种是通过SVG,第三种是通过NodeJS

6.1 canvas生成水印

makeWaterMark(content, {container = document.body,width = 250,height = 200,textAlign = 'center',textBaseline = 'middle',font = '20px 微软雅黑',fillStyle = 'rgba(184, 184, 184, 0.8)',angle = 30,zIndex = 11000,
} = {}) {// 创建画布const cvs = document.createElement('canvas');// 画布尺寸cvs.width = width;cvs.height = height;// 上下文const ctx = cvs.getContext('2d');// 文字样式ctx.textAlign = textAlign;ctx.textBaseline = textBaseline;ctx.font = font;ctx.fillStyle = fillStyle;// 文字旋转ctx.rotate(angle * Math.PI / 180);// 绘制文字ctx.fillText(content, width / 2, height / 2);// 生成base64图片编码const base64Url = cvs.toDataURL();// 生成水印容器divconst div = document.createElement('div');// CSS属性div.id = 'watermark';div.setAttribute('style', `position: fixed; top: 0; left: 0; width: 100 % ; height: 100 % ; z - index: $ {zIndex}; pointer - events: none; background - repeat: repeat; background - image: url('${base64Url}')`);// 插入容器container.insertBefore(div, null);return base64Url

导出这个方法的话可以使用UMD格式导出:

if (typeof module != 'undefined' && module.exports) { //CMDmodule.exports = makeWaterMark;
} else if (typeof define == 'function' && define.amd) { // AMDdefine(function() {return makeWaterMark;});
} else {window.makeWaterMark = makeWaterMark;
}

这里面还有一个问题,就是水印很容易在开发者工具中去除,这时候可以使用MutationObserver接口来进行处理.

MutationObserver接口提供了监视对DOM树所做更改的能力。MutationObserver用来监视DOM变动。DOM的任何变动,比如节点的增减、属性的变动、文本内容的变动,这个API都可以得到通知。

// 监控DOM变动
const MutationObserver = window.MutationObserver || window.webkitMutationObserver;
if (MutationObserver) {let mo = new MutationObserver(() = > {const watermark = document.querySelector('#watermark');if ((watermark && watermark.getAttribute('style') !== divStyle) || !watermark) {// 避免循环触发mo.disconnect();mo = null;this.makeWaterMark.apply(this, [].slice.call(arguments))}});// 监听对象const config = {attributes: true,childList: true,subtree: true};mo.observe(container, config)
}

将上面的代码应用到生成水印的方法中,就可以阻止DOM节点的删除

6.2 通过SVG生成水印

相比Canvas,SVG有更好的浏览器兼容性,使用SVG生成水印的方式与Canvas的方式类似,只是base64Url的生成方式换成了SVG。

(function () {// svg 实现 watermarkfunction __svgWM({ container = document.body,content = '请勿外传',width = '300px',height = '200px',opacity = '0.2',fontSize = '20px',zIndex = 1000} = {}) {const args = arguments[0];const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${width}"><text x="50%" y="50%" dy="12px" text-anchor="middle" stroke="#000000" stroke-width="1" stroke-opacity="${opacity}" fill="none" transform="rotate(-45, 120 120)" style="font-size: ${fontSize};">${content}</text></svg>`;const base64Url = `data: image / svg + xml;base64, ${window.btoa(unescape(encodeURIComponent(svgStr)))}`;const __wm = document.querySelector('.__wm');const watermarkDiv = __wm || document.createElement("div");// ...// 与 canvas 的一致// ...}
)();
__svgWM({content: 'QQMusicFE'
})

6.3 NodeJS生成水印

具体参考这篇文章吧。

6.4 图片水印

浏览器给图片添加水印就是通过canvas绘制图片,然后增加文字的形式完成的。

(function () {function __picWM({ url = '',textAlign = 'center',textBaseline = 'middle',font = "20px Microsoft Yahei",fillStyle = 'rgba(184, 184, 184, 0.8)',content = '请勿外传',cb = null,textX = 100,textY = 30} = {}) {const img = new Image();img.src = url;img.crossOrigin = 'anonymous';img.onload = function () {const canvas = document.createElement('canvas');canvas.width = img.width;canvas.height = img.height;const ctx = canvas.getContext('2d');ctx.drawImage(img, 0, 0);ctx.textAlign = textAlign;ctx.textBaseline = textBaseline;ctx.font = font;ctx.fillStyle = fillStyle;ctx.fillText(content, img.width - textX, img.height - textY);const base64Url = canvas.toDataURL();cb && cb(base64Url);}}if (typeof module != 'undefined' && module.exports) {  //CMDmodule.exports = __picWM;} else if (typeof define == 'function' && define.amd) { // AMDdefine(function () {return __picWM;});} else {window.__picWM = __picWM;}})();// 调用
__picWM({url: 'http://localhost:3000/imgs/google.png',content: 'QQMusicFE',cb: (base64Url) => {document.querySelector('img').src = base64Url},
});

可以使用NodeJS批量为图片添加水印,使用的gm这个库。

6.5 隐水印

参考这篇文章不能说的秘密——前端也能玩的图片隐写术

6.6 水印加密

可以将水印内容通过加密来防止水印的造假,比如使用md5加密

// MD5加密库 utility
const utils = require('utility')// 加盐MD5
exports.md5 =  function (content) {const salt = 'microzz_asd!@#IdSDAS~~';return utils.md5(utils.md5(content + salt));
}

参考

  • 知乎-JavaScript中的图片处理与合成(一)
  • W3school-HTML5 canvas drawImage()方法
  • MDN-Window.URL
  • MDN-URL.createObjectURL
  • JavaScript中的图片处理与合成(二)
  • 伯乐在线-网页水印生成解决方案

JS中图片的处理与合成(生成水印)相关推荐

  1. Js中调用调用打印和自动生成条形码

    要做到这种效果,打印界面和使用code128自动生成条形码 首先在html页面中引入打印和条形码的js <script src="jquery.PrintArea.js"&g ...

  2. js中图片显示用ajax,javascript - 前台用ajax上传图片,怎么让图片上传完成显示的缩略图片的时候显示分辨率大小...

    以下是相关代码,现在就是上传上去想让显示以下分辨率,也就是图片的大小,请问应该怎么该才可以,求解答 前台上传文件代码 window.οnlοad=function(){ var type=docume ...

  3. js中图片获取src的正则

    链接: JavaScript 正则表达式:http://www.runoob.com/js/js-regexp.html js正则匹配出所有图片及图片地址src的方法:http://www.jb51. ...

  4. js中图片加载失败,显示默认图片

    js处理img标签加载图片失败,显示默认图片 1.第一种方法: 如果已经引入了jquery插件,就很好办.没有的话,如果实在需要,可以附上代码: $('img').error(function(){$ ...

  5. js中图片base64格式转文件对象

    通常我们使用裁剪工具裁剪图片后输出的格式为base64格式,而有时需要将图片转为源文件. function dataURLtoFile(dataurl, filename) {//将base64转换为 ...

  6. 前台js中图片picture设置

    图片设置上一页.下一页.切换.预加载等功能: //相册层 layer.photos = function(options, loop, key){var dict = {}; options = op ...

  7. js实现word生成书签_js生成word中图片处理方法

    首先功能是要求前台导出word,但是前后台是分离的,图片存在后台,所以就存在跨域问题. 导出文字都是没有问题的(jquery.wordexport.js),但是导出图片就存在问题了: 图片是以链接形式 ...

  8. vue样式 引入图片_详解Vue.js中引入图片路径的几种方式

    vue --version 3.6.3 记录总结一下的Vue中引入图片路径的几种书写方式 vue中静态资源的引入机制 Vue.js关于静态资源的官方文档 静态资源可以通过两种方式进行处理: 在 Jav ...

  9. asp生成带参数的二维码并合成推广海报图片,asp合并合成推广海报图片asp代码

    最近做的一个项目中,客户要求用asp生成二维码,然后合并到一张背景图片上,合并生成一张推广海报来,可把我愁坏了,经过一个晚上的努力,成功了,下面把这个:asp生成带参数的二维码并合成推广海报,asp合 ...

最新文章

  1. Linux virtualenv, virtualenvwrapper, pip freeze
  2. Android Studio项目结构
  3. 切点、切面:@Aspect、@PointCut相关的个人总结
  4. delphi7存储过程传入数组_数据结构线性表之顺序存储 类的封装
  5. java基础之线程(1)
  6. esxi6.0开启网络UI管理界面
  7. Oracle 11.2.0.4.0 Dataguard部署和日常维护(6)-Active Dataguard篇
  8. php[6491]: segfault at * rip * rsp * error 6
  9. Sqlmap安装教程
  10. VMware 12 专业版永久许可证密钥
  11. Mahony 互补滤波
  12. 云流化像素流技术解决方案之虚拟仿真系统
  13. flutter 生成图片保存到手机相册
  14. 《嵌入式开发》实验项目
  15. 微信关注事件bug记录
  16. 怎么在电脑上登陆多个微信
  17. 面试官:请解释一下Twitter的前10行源代码
  18. ucos系统使用delay函数死机原因
  19. 华为内部存储转sd卡_华为手机内部存储软件怎么转到sd卡?
  20. ubuntu的使用经验to新手

热门文章

  1. 适合MacBook Pro 2021玩的游戏推荐
  2. shell url转义_url特殊字符转义及解决方法
  3. 快手查权重+抖音查权重+QQ查估值三合一软件工具【查询脚本+详细教程】
  4. RK3399 LINUX RTL8821CS移植
  5. html怎么连接外部音乐,mp3音乐外链接-怎么样连接外部音乐 爱问知识人
  6. oracle平均值语句,Oracle / PLSQL AVG函数
  7. 客户需要增加注音、繁体输入法,手写输入法
  8. 树莓派udev不能自动挂载_英伟达:抢占‘树莓派市场,发布基于云的自动驾驶仿真平台...
  9. Inkscape扩展:图案沿着路径和散布
  10. 苹果cms如何开启Redis高速缓存提升访问速度