内容简介:总结在使用canvas时遇到的一些通用效果实现与相关示例。使用
总结在使用canvas时遇到的一些通用效果实现与相关示例。
透底(剪裁)效果
使用 clip API
See the Pen Clip target area by yrq110 ( @yrq110 ) on CodePen .
示例中的蓝色小方块即为剪裁的区域,无透明效果的原始图案。
主要使用了如下三个元素:
-
save()&restore()save()与restore()是为了保留裁剪前的上下文状态,不然在需要交互(如移动透底框体)场景下,透底区域的图像就不会随着外部canvas内容的变化而改变了。 -
Path2Dregion为一个
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 代码片段
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。