你不知道的canvas

780 阅读5分钟

最近在利用canvas开发图片水印(海报)服务的项目,在开发过程中遇到一些问题,目前都已解决,这些问题让我走了不少弯路,所以现在把这些问题以及解决方案罗列出来,方便和我一样遇到这些问题的小伙伴可以更快的解决问题!

1.canvas裁剪

当时项目需要切圆、矩形裁剪、缩放等功能,主要在切圆这块花了不少功夫,关键技术点是clip。 官方解释: clip() 方法从原始画布中剪切任意形状和尺寸。 提示: 一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。

解决方案

/**
 * 裁圆
 * @param ctx 上下文
 * @param config 图片参数
 * @param process 处理参数
 */
const handleCircle = (ctx, config, process) => {
  const { img, x, y, w, h } = config;
  const { r, cx, cy } = process;
  ctx.save(); // 保存当前ctx的状态
  ctx.beginPath();
  ctx.arc(x + r, y + r, r, 0, 2 * Math.PI); // 画出圆
  ctx.stroke();
  ctx.closePath();
  ctx.clip(); // 裁剪上面的圆形
  ctx.drawImage(img, cx - r, cy - r, 2 * r, 2 * r, x, y, 2 * r, 2 * r); // 在刚刚裁剪的园上画图
  ctx.restore(); // 还原状态
};

ctx.arc(x,y,r,start,end)主要是绘制出一条弧线;

  • (x,y): 代表绘制弧线的圆心;
  • r: 代表半径;
  • start: 代表弧线开始点角度;
  • end: 代表弧线结束点角度;`

例:ctx.arc(x + r, y + r, r, 0, 2 * Math.PI);代表以(x+r,y+r)为圆形,r为半径,绘制一个圆

ctx.drawImage(img,cx,cy,cwidth,cheight,x,y,width,height)主要作用是画图;

  • img: 当前绘制的图像;
  • cx: 可选,以当前图片左上角为坐标原点,cx代表裁剪的x坐标;
  • cy: 可选,以当前图片左上角为坐标原点,cy代表裁剪y坐标;
  • cwidth: 可选,裁剪的宽度;
  • cheight: 可选,裁剪的高度;
  • x: 绘制图像的x坐标;
  • y: 绘制图像的y坐标;
  • width: 可选,绘制的宽度;
  • height: 可选,绘制的高度;

小结: arc是绘制一个圆路径,drawImage作用是选取图像中某块矩形区域,将其填充到绘制的圆形路径中去,要保证圆是该矩形的内切圆,注意: arc中的第1、2的参数和drawImage的第6、7的参数多个半径,记住这点,大功告成。


/**
 * 裁矩形
 * @param ctx 上下文
 * @param config 图片参数
 * @param process 处理参数
 */
const handleCrop = (ctx, config, process) => {
  const { img, x, y, w, h } = config;
  const { cx, cy, cw, ch } = process;
  ctx.save(); // 保存当前ctx的状态
  ctx.beginPath();
  ctx.drawImage(img, cx, cy, cw, ch, x, y, cw, ch); // 在刚刚裁剪的矩形上画图
  ctx.restore(); // 还原状态
};

小结: 和上面一样,裁剪矩形主要用到drawImage方法,注意点: 需要注意裁剪的高宽和显示的高宽要保证一致。

拓展


const pattern = ctx.createPattern(img, "no-repeat");
ctx.save();
ctx.beginPath();
ctx.moveTo(x + br, y); // 移动到左上角的点
ctx.lineTo(x + w - br, y);
ctx.arc(
x + w - br,
y + br,
br,
2 * Math.PI * (3 / 4),
2 * Math.PI * (4 / 4)
);
ctx.lineTo(x + w, y + h - br);
ctx.arc(x + w - br, y + h - br, br, 0, 2 * Math.PI * (1 / 4));
ctx.lineTo(x + br, y + h);
ctx.arc(
x + br,
y + h - br,
br,
2 * Math.PI * (1 / 4),
2 * Math.PI * (2 / 4)
);
ctx.lineTo(x, y + br);
ctx.arc(x + br, y + br, br, 2 * Math.PI * (2 / 4), 2 * Math.PI * (3 / 4));
ctx.fillStyle = pattern;
ctx.fill();
ctx.closePath();
ctx.clip();

此方法也可以裁剪圆,通过绘制直线加绘制弧线组合得到一个圆或圆形矩形,但pattern来填充圆形,图像内容不好控制,不建议使用。

2.canvas绘制图像不清晰

主要思想: 1.首先讲画布的大小进行按照设备进行放大devicePixelRatio属性进行相应的调整,设个属性是表示浏览器通常会使用几个像素点进行渲染我们设定的1像素(通常会使用2像素进行渲染)。 2.然会将画好的图像使用scale()进行相应的放大 这样就可以将图像的模糊问题进行解决了。

const canvas = document.getElementById("canvas")
ctx = canvas.getContext("2d");  
const devicePixelRatio = window.devicePixelRatio || 1;
const backingStoreRatio = ctx.webkitBackingStorePixelRatio ||  
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio || 
ctx.oBackingStorePixelRatio || 
ctx.backingStorePixelRatio || 1;
const ratio = devicePixelRatio /backingStoreRatio;

其中设置canvas.width,canvas.height不代表canvas 的css宽高,当不设置css宽高则使用canvas宽高来替代css宽高,通过canvas.style.width,canvas.style.height设置css的宽高。

3.canvas文本位置

当利用canvas绘制文本时,x,y的坐标并不是像图像一样,以图像的左上角为起点,text的位置是以文本的基线来作为起点标准的,canvas中有textAlign和baseLine,x,y则是以这两者基线来定位的。

textAlign

textAlign的属性值有

  • start:(默认)表示文本以x坐标为起始点开始绘制;
  • end:表示文本以x坐标为结束点开始绘制
  • center:表示文本以x坐标为中心位置开始绘制
  • left:表示文本以x坐标左对齐开始绘制;
  • right:表示文本以x坐标有右对齐开始绘制; 到此为止,大家用过的一定会觉得start和left效果一样,end和right也是如此,那么他们之间的区别是什么呢? 这就要和direction属性有关系了,默认direction是ltr,表示文本从左向右流动,此时start和left效果一致,end和right一致; 当设置direction为rtl,表示文本从右向左流动,此时start和right效果一致,end和left一致; 但一般direction是实验性属性,一般不建议使用。

以上只是项目开发中遇到的3点canvas相关的问题,有错误之处欢迎小伙伴指出,觉得还不错的话,希望可以留下你们大大的赞👍谢谢!!!