内容简介:总结在使用canvas时遇到的一些通用效果实现与相关示例。使用
总结在使用canvas时遇到的一些通用效果实现与相关示例。
透底(剪裁)效果
使用 clip API
See the Pen Clip target area by yrq110 ( @yrq110 ) on CodePen .
示例中的蓝色小方块即为剪裁的区域,无透明效果的原始图案。
主要使用了如下三个元素:
-
save()
&restore()
save()
与restore()
是为了保留裁剪前的上下文状态,不然在需要交互(如移动透底框体)场景下,透底区域的图像就不会随着外部canvas内容的变化而改变了。 -
Path2D
region为一个
Path2D
对象,用于声明路径,可以直接在canvas2d上下文中使用。首先在region上添加目标区域矩形,其次添加整体区域矩形,则两者相交叉的部分即为目标的透底(剪裁)区域。之后在上下文上使用clip API应用该region区域即可对目标区域进行透底(裁剪)。
-
clip()
clip有两种填充模式:
nonzero
模式和evenodd
模式,默认为nonzero
。详情可以看张鑫旭的 这篇 文章了解。本例中使用的是
evenodd
模式(奇偶判断规则)。若想剪裁相关区域可以直接通过例子中targetArea
所代表的目标区域来进行剪裁
相关QA: https://stackoverflow.com/questions/7821384/html-canvas-clip-area-context-restore
笔画效果
在canvas上想实现一个可以自由绘制的画笔效果,需要两个步骤
- 监听鼠标事件得到坐标数据
- 使用处理后的坐标进行绘制
在第2步中有两种方法进行绘制:一种是在监听到鼠标事件并处理坐标后立即绘制,第二种是通过一个标志位(开关)在requestAnimationFrame所执行的函数中进行绘制,区别在于体验时的流畅度、执行的频率以及性能。
鼠标事件
See the Pen bhiKl by Juriy Zaytsev ( @kangax ) on CodePen .
设置开始与结束绘制的标志位
el.onmousedown = function(e) { isDrawing = true; points.push({ x: e.clientX, y: e.clientY }); }; el.onmouseup = function() { isDrawing = false; points.length = 0; };
在移动时通过标志点进行绘制,示例中为了使路径光滑使用了二次贝塞尔曲线函数
el.onmousemove = function(e) { if (!isDrawing) return; points.push({ x: e.clientX, y: e.clientY }); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); var p1 = points[0]; var p2 = points[1]; ctx.beginPath(); ctx.moveTo(p1.x, p1.y); for (var i = 1, len = points.length; i < len; i++) { var midPoint = midPointBtw(p1, p2); ctx.quadraticCurveTo(p1.x, p1.y, midPoint.x, midPoint.y); p1 = points[i]; p2 = points[i+1]; } ctx.lineTo(p1.x, p1.y); ctx.stroke(); };
鼠标事件 + requestAnimationFrame
同样根据判断鼠标事件中设置标志位绘制,不过是在requestAnimationFrame的动画关键帧函数中执行
See the Pen draw-stroke by yrq110 ( @yrq110 ) on CodePen .
路径坐标绘制
同样可以使用Path2D来绘制一个给定一系列坐标的轨迹线,需要先将这些坐标点转换成SVG路径,直接传入Path2D构造函数即可。笔者这里用了
svg-points
这个库来将坐标点转换成SVG路径。
let points = contour.map(p => { let [x, y] = p; return { x, y }; }); points[0].moveTo = true; let svgPath = toPath(points); ctx.beginPath(); var p = new Path2D(svgPath); ctx.stroke(p); ctx.closePath();
See the Pen draw-svg-path by yrq110 ( @yrq110 ) on CodePen .
图形或图像的透明效果
RGBA
在绘制时给填充或笔触颜色设置alpha值,就可以实现透明图形或图像的绘制。
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgba(255,0,255,0.2)'; ctx.fillRect(0, 0, 75, 75);
See the Pen Overlaying transparent shapes-4 by yrq110 ( @yrq110 ) on CodePen .
globalAlpha
在canvas context上设定 globalAlpha 属性可以用来设置之后图形或图像绘制在canvas上的alpha值。
ctx.globalAlpha = 0.5; ctx.fillStyle = '#FD0'; ctx.fillRect(0, 0, 75, 75); // 该矩形为半透明的
See the Pen Overlaying transparent shapes by yrq110 ( @yrq110 ) on CodePen .
层叠图形的加深效果
若设置globalAlpha后绘制层叠图形,那么层叠的部分会出现透明度加深的效果
See the Pen Overlaying transparent shapes by yrq110 ( @yrq110 ) on CodePen .
全局统一透明度
若想保持图形的透明度统一,可以采用加一层offscreen canvas(通过 document.createElement('canvas')
创建,非不在屏幕上绘制canvas)的方式。
offscreen层为不透明的,将其绘制到onscreen层,设置onscreen层的globalAlpha属性即可。
通过如下方式创建,不显示在屏幕上的canvas元素
let canvas = ; canvas.ctx = canvas.getContext('2d');
采用off-screen绘制方式,一方面降低重绘另一方面可以使用全局透明度进行上层canvas的透底效果
See the Pen Overlaying transparent shapes-3 by yrq110 ( @yrq110 ) on CodePen .
擦除操作
设置
globalCompositeOperation
属性进行绘制即可,会将绘制图形的形状未覆盖的部分保留。
ctx.globalCompositeOperation = "destination-out";
在下面这个例子中,绘制的路径形状会被去除,显示为背景的白色。
See the Pen erase-effect by yrq110 ( @yrq110 ) on CodePen .
图片绘制与导出
图片绘制
使用
drawImage API
进行绘制。
需要注意的是,表示图像源的第一个参数可以是多种类型:CSSImageValue, HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, OffscreenCanvas。
即在常见的场景中,可以将图像绘制到画布,也可以将另一个画布的内容绘制到该画布上。
通过调整表示尺寸的source与destination参数可以在绘制时进行一定比例的缩放。
跨域问题
若跨域使用图片资源,并在画布上进行如下三种操作时:
- 在canvas上下文中调用getImageData()
- 在<canvas>元素上调用toBlob()
- 在canvas对象上调用toDataURL()
会提示 Tainted canvases may not be exported
的错误,此时需要给Image对象设置一个 crossOrigin
属性来解决。
如下是个简单封装的加载image方法:
export const loadImage = imgPath => { return new Promise((resolve, reject) => { let img = new Image(); img.setAttribute("crossOrigin", "anonymous"); // to solve "Tainted canvases may not be exported" error img.onload = () => { resolve(img); }; img.onerror = e => { reject(new Error(e)); }; img.src = imgPath; }); };
图片导出成base64
let img = await loadImage(imagePath) let { width, height } = img; let canvas = document.createElement("canvas"); let ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height); let imageData = canvas.toDataURL("image/png"); console.log('imageData: ', imageData) // imageData: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAg4AAAIOCAYAAADQu4U5AAAgAElEQVR4Xu3dd5zdVZ3/8de5M5M6k4QmLqKsIhYgE4orsjbcddcVewklE5pYsLA2VBQRfooFFxXsNEWSCWBYy+qK7loQFREJGxKwF1BBBYEkM5M6c8/v8Z077EYI8J3J/Z45c+/r+6ee7/mc8zwn4Z3v/ZaAhwIKKKCAAgooUFIglGxnMwUUUEABBRRQAIO...
原始图像与mask遮罩合成
将mask层的alpha通道值赋予原始图像的alpha通道进行结果图的合成
// resultMaskData: { width, height, values } ... let resultMaskLayer = document.createElement("canvas"); let resultMaskCtx = resultMaskLayer.getContext("2d"); resultMaskLayer.width = img.width; resultMaskLayer.height = img.height; resultMaskCtx.drawImage(img, 0, 0); if (resultMaskData) { let { width: maskWidth, height: maskHeight, values } = resultMaskData; let maskData = resultMaskCtx.getImageData(0, 0, maskWidth, maskHeight); let size = maskWidth * maskHeight; for (let i = 0; i < size; i++) { if (values[i] !== 255) { maskData.data[(i + 1) * 4 - 1] = values[i]; } } resultMaskCtx.putImageData(maskData, 0, 0); }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 前端常用代码片段(四)
- 前端常用代码片段(六)
- vscode js 实用的代码片段
- Android 开发中的代码片段(1)
- 如何快速创建 Visual Studio 代码片段?
- 30 秒收集有用的 Java 8 代码片段
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Base64 编码/解码
Base64 编码/解码
RGB CMYK 转换工具
RGB CMYK 互转工具