一.需求
在canvas画布上,假设我们绘制了一张图片,现在需要将该图片进行裁剪,并将裁剪的区域提取成图像,并将该图像转化为base64格式返回,要求编写这样一个脚本。
二.知识储备
HTML5画布中,Canvas的以下几个接口可能是我们所需要的:
1. clip:
该接口用于对当前的画布设置裁剪区域,在之后的绘制过程中,只有裁剪区域中的绘制能够生效,即能够呈现。
该方法需要先绘制一个封闭的路径,比如一个圆(arc),抑或是一个矩形(rect),接着在通过clip进行裁剪:
例:
cxt.rect(0, 0, 100, 100);
cxt.clip();
裁剪之后只能在裁剪区呈现绘制,如果想要去除clip的影响,则需要在clip裁剪动作之前先将画布的状态保存起来,比如:cxt.save(),当我们需要重新绘制时,便可以通过cxt.restore()恢复至clip之前的状态,我们就可以在clip之前的状态下进行绘制。不过需要提醒的是:虽然恢复了状态,但是在restore之前和clip之后受到影响的绘制仍会保持原样,具体可以自己测试以下。
2. drawImage:
该接口用于在canvas画布中的特定位置将图像、画布或者视频绘制在其上,其有三种语法:
语法1:通过设置图像的左上角在画布的位置(x, y)将图像按图像原本大小进行绘制
context.drawImage(img, x, y);
语法2:相较于语法1,此语法设置图片在画布上的宽高(图像的拉伸缩小)
context.drawImage(img, x, y, width, height)
语法3:相比于语法2,添加了四个参数,sx、sy设置对图像进行裁剪的区域左上角相对于图像的位置,swidth、sheight设置裁剪宽高
context.drawImage(img, sx, sy, swidth, sheight, x, y, width, height)
这里要注意的是,语法三中width和height是设定呈现于画布中的裁剪区的宽高。
3. getImageData:
该接口复制画布上指定矩形的像素数据,返回一个ImageData对象,该对象拷贝了画布指定矩形的像素数据。
语法:
var imgData = context.getImageData(x, y, width, height);
x、y是指开始复制的区域的左上角x坐标和y坐标;
width、height是指将要复制的矩形区域的宽高;
4. putImageData:
该接口将图像数据(从指定的ImageData对象)放回画布上。
语法:
context.putImageData(imgData, x, y, dirtyX, dirtyY, dirtyWidth, dirtyHeight);//后面四个参数是可选的
imgData: 要放回画布的ImageData对象;
x、y: ImageData对象左上角的x、y坐标;
dirtyWidth, dirtyHeight:相当于裁剪宽度和高度;
dirtyX、 dirtyY:相对于裁剪区的偏移位置;
5. toDataURL:
该接口用于将整个canvas画布图像转化为dataURL(base64)。
语法:
canvas.toDataURL(type, encoderOptions)
两个参数均为可选,第一个参数指定图片格式,第二个参数指定图片的质量,返回值为包含data URI的DOMString。
三.思路
根据上面的知识储备,我们设计一下脚本逻辑:
-
首先设置裁剪区;
-
将图片绘制在画布的左上角;
-
通过getImageData获取裁剪区的像素;
-
创建一个和裁剪区一样大小的canvas;
-
通过putImageData将裁剪区像素复制到新创建的canvas画布中;
-
通过新的canvas调用toDataURL获取base64格式图像;
四.编码
//获取canvas画布相应区域的图像信息,并返回其base64格式的图像
function getBase64Image(context, x1, y1, x2, y2) {
var dataImg = context.getImageData(x1, y1, x2, y2);
var canvas2 = document.createElement("canvas");
var context2 = canvas2.getContext("2d");
var width = Math.abs(x1 - x2);
var height = Math.abs(y1 - y2);
canvas2.width = width;
canvas2.height = height;
context2.putImageData(dataImg, 0, 0, 0, 0, width, height);
var res = canvas2.toDataURL('image/jpeg');
return res;
}
五.发现问题
我们在本地进行测试的时候,会报错:
为什么会报错呢?
为了阻止欺骗,浏览器会追踪 image data。当你把一个“跟canvas的域不同的”图片放到canvas上,这个canvas就成为 “tainted”(被污染的,脏的),浏览器就不让你操作该canvas 的任何像素。这对于阻止多种类型的XSS/CSRF攻击(两种典型的跨站攻击)是非常有用的。
因为本地没有服务器环境仍然会报错,没有域的概念,浏览器会将图片判定为跨域,从而报错。
那我们就将测试demo放到服务器中就行了吧,运行一下,果然是可以了,如下截图所示:(注意图片必须是位于同一服务器环境)
看见没有,裁剪的图我们通过base64格式将其展示在左下角。