JS中图片的处理与合成(生成水印)
以美图的图片处理为例子,学习JS中对图片的处理。处理图片时需要使用canvas
1 图片的跨域
使用canvas
处理图片,首先需要加载图片,如果是在线图片需要针对图片进行跨域的处理。具体的处理方法是:
在图片服务器上设置跨域头,并且在前端加载图片之前将<img>
标签的crossOrigin
设置为*
这样做的原因是:
尽管不通过
CORS
就可以在画布中使用图片,但是这会污染画布。一旦画布被污染,你就无法读取其数据。例如,你不能再使用画布的toBlob()
,toDataURL()
或getImageData()
方法,调用它们会抛出安全错误。(MDN Web docs)
还要注意的是:
crossOrgin
只有在线上图片时才设置,本地图片或者是base64图片不能设置,否在某些系统会报错,导致图片加载失败- 当项目为本地包环境时,例如内置于App中时,
crossOrigin
值无效,webview的安全机制会导致无论该值设置与否,都会报跨域的错误。解决办法是:需要将所有图片转换成base64才能正确绘制; 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格式的图片,
其中:
type
: 图片格式, 一般可以使用image/png
或者image/jpeg
, 当图片不包含透明时,建议使用jpeg
,可使导出的图片大小减小很多;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会出现图片的宽高和图片的方向不匹配的问题,需要特殊处理:
- 可以使用exif.js来获取图片信息中的Orientation属性,利用canvas的绘制来矫正
- 可以使用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中图片的处理与合成(生成水印)相关推荐
- Js中调用调用打印和自动生成条形码
要做到这种效果,打印界面和使用code128自动生成条形码 首先在html页面中引入打印和条形码的js <script src="jquery.PrintArea.js"&g ...
- js中图片显示用ajax,javascript - 前台用ajax上传图片,怎么让图片上传完成显示的缩略图片的时候显示分辨率大小...
以下是相关代码,现在就是上传上去想让显示以下分辨率,也就是图片的大小,请问应该怎么该才可以,求解答 前台上传文件代码 window.οnlοad=function(){ var type=docume ...
- js中图片获取src的正则
链接: JavaScript 正则表达式:http://www.runoob.com/js/js-regexp.html js正则匹配出所有图片及图片地址src的方法:http://www.jb51. ...
- js中图片加载失败,显示默认图片
js处理img标签加载图片失败,显示默认图片 1.第一种方法: 如果已经引入了jquery插件,就很好办.没有的话,如果实在需要,可以附上代码: $('img').error(function(){$ ...
- js中图片base64格式转文件对象
通常我们使用裁剪工具裁剪图片后输出的格式为base64格式,而有时需要将图片转为源文件. function dataURLtoFile(dataurl, filename) {//将base64转换为 ...
- 前台js中图片picture设置
图片设置上一页.下一页.切换.预加载等功能: //相册层 layer.photos = function(options, loop, key){var dict = {}; options = op ...
- js实现word生成书签_js生成word中图片处理方法
首先功能是要求前台导出word,但是前后台是分离的,图片存在后台,所以就存在跨域问题. 导出文字都是没有问题的(jquery.wordexport.js),但是导出图片就存在问题了: 图片是以链接形式 ...
- vue样式 引入图片_详解Vue.js中引入图片路径的几种方式
vue --version 3.6.3 记录总结一下的Vue中引入图片路径的几种书写方式 vue中静态资源的引入机制 Vue.js关于静态资源的官方文档 静态资源可以通过两种方式进行处理: 在 Jav ...
- asp生成带参数的二维码并合成推广海报图片,asp合并合成推广海报图片asp代码
最近做的一个项目中,客户要求用asp生成二维码,然后合并到一张背景图片上,合并生成一张推广海报来,可把我愁坏了,经过一个晚上的努力,成功了,下面把这个:asp生成带参数的二维码并合成推广海报,asp合 ...
最新文章
- Linux virtualenv, virtualenvwrapper, pip freeze
- Android Studio项目结构
- 切点、切面:@Aspect、@PointCut相关的个人总结
- delphi7存储过程传入数组_数据结构线性表之顺序存储 类的封装
- java基础之线程(1)
- esxi6.0开启网络UI管理界面
- Oracle 11.2.0.4.0 Dataguard部署和日常维护(6)-Active Dataguard篇
- php[6491]: segfault at * rip * rsp * error 6
- Sqlmap安装教程
- VMware 12 专业版永久许可证密钥
- Mahony 互补滤波
- 云流化像素流技术解决方案之虚拟仿真系统
- flutter 生成图片保存到手机相册
- 《嵌入式开发》实验项目
- 微信关注事件bug记录
- 怎么在电脑上登陆多个微信
- 面试官:请解释一下Twitter的前10行源代码
- ucos系统使用delay函数死机原因
- 华为内部存储转sd卡_华为手机内部存储软件怎么转到sd卡?
- ubuntu的使用经验to新手
热门文章
- 适合MacBook Pro 2021玩的游戏推荐
- shell url转义_url特殊字符转义及解决方法
- 快手查权重+抖音查权重+QQ查估值三合一软件工具【查询脚本+详细教程】
- RK3399 LINUX RTL8821CS移植
- html怎么连接外部音乐,mp3音乐外链接-怎么样连接外部音乐 爱问知识人
- oracle平均值语句,Oracle / PLSQL AVG函数
- 客户需要增加注音、繁体输入法,手写输入法
- 树莓派udev不能自动挂载_英伟达:抢占‘树莓派市场,发布基于云的自动驾驶仿真平台...
- Inkscape扩展:图案沿着路径和散布
- 苹果cms如何开启Redis高速缓存提升访问速度