以美图的图片处理为例子,学习JS中对图片的处理。处理图片时需要使用
canvas
使用
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('图片解析失败'))
图片缩放最常见的场景是图片的压缩,在保证清晰的前提下合理的缩小图片尺寸,能大大的降低图片的大小。
新建一个canvas画布,将宽高设置为需要压缩的尺寸,要注意的是需要保证图片的比例,所以canvas的尺寸是通过计算得出的。
// 创建图片
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
: 绘制的宽度和高度,宽高比例并不锁定,可使图片变形;
然后使用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);
我们常用的图片上传功能,我们使用的是原生的<input type="file">
标签,此时获取到的是File格式的图片,图片的格式各异且尺寸很大,我们应该压缩处理后再使用。
三种方式,一种方式就是上面提到的,利用canvas
来实现,另一种是利用fileReader
实现,还有一种是使用createObjectURL
// 利用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;
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)
就如同上面说的一样,先通过drawImage
绘制图片,然后通过toDataURL
转为base64格式
要注意的是,图片的EXIF信息中的方向值会影响图片的展示,在IOS会出现图片的宽高和图片的方向不匹配的问题,需要特殊处理:
- 可以使用exif.js来获取图片信息中的Orientation属性,利用canvas的绘制来矫正
- 可以使用canvasResize.js来解决File到base64的一系列问题。
通过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)
参考这个吧,弄旋转弄了半天也没成,没兴趣了,有时间再说。
水印有几种方式可以生成,第一种就是使用canvas,第二种是通过SVG,第三种是通过NodeJS
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();
// 生成水印容器div
const 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) { //CMD
module.exports = makeWaterMark;
} else if (typeof define == 'function' && define.amd) { // AMD
define(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节点的删除
相比Canvas,SVG有更好的浏览器兼容性,使用SVG生成水印的方式与Canvas的方式类似,只是base64Url的生成方式换成了SVG。
(function () {
// svg 实现 watermark
function __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'
具体参考这篇文章吧。
浏览器给图片添加水印就是通过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) { //CMD
module.exports = __picWM;
} else if (typeof define == 'function' && define.amd) { // AMD
define(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这个库。
参考这篇文章不能说的秘密——前端也能玩的图片隐写术
可以将水印内容通过加密来防止水印的造假,比如使用md5加密
// MD5加密库 utility
const utils = require('utility')
// 加盐MD5
exports.md5 = function (content) {
const salt = 'microzz_asd!@#IdSDAS~~';
return utils.md5(utils.md5(content + salt));
以美图的图片处理为例子,学习JS中对图片的处理。处理图片时需要使用canvas1 图片的跨域使用canvas处理图片,首先需要加载图片,如果是在线图片需要针对图片进行跨域的处理。具体的处理方法是:在图片服务器上设置跨域头,并且在前端加载图片之前将&lt;img&gt;标签的crossOrigin设置为*这样做的原因是: 尽管不通过CORS就可以在画布中使用图片,但是这会...
在实际业务开发中,经常会遇到上传图片时要添加上自己网站水印,这个水印各种类型都有,这里主要讲下我在开发中,客户的需要在上传图片时添加上当前录入表单的一些信息,方便后期直接查看图片追溯信息。
先放下最终实现的上传成功后缩略图和预览图:
实现该功能主要有2种:原生的canvas实现和使用html2canvas库。当前项目是基于element-ui的一个vue项目,提供思路,代码仅供参考。
原生的canvas
其思路就是File对象–>img DOM元素–>转为canvas–>画水印图
1:、获取图片base64数据,使用js Image对象加载图片 (可以直接加载base64数据)
2、 使用canvas写入图片,再canvas 绘制水印文本 ,具体算法见下文。 【参考: canvas绘制文本】
3、canvas输出添加水印后的base64数据
一、Imag
常见场景的是点击按钮,生成一个悬浮的截图,图片可以保存在手机相册里,也可以分享出去。
如果canvas基础较好,开发时间足够,就可以自己用canvas去画,可以完成开发。
但是用js插件,可以更快实现这种需求。
html2canvas
官网: http://html2canvas.hertzen.com/
实现流程:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "h
关于实现页面截图常用的几个js插件库
canvas2image.jshtml2canvas.jsconvertImgToBase64.js废话不多说,直接上demo代码index.html:
<!doctype html>
<meta charset="utf-8" />
<script src="canvas2image.js"></script>
<style>
function blobToImg(blob) {
return new Promise((resolve, reject) => {
let reader = new FileReader()
reader.addEventListener('load', () => {
let img = new Image()
img.src = reader.result
img.addEv