canvas 2d 绘图(一)
约定原则:手绘画图原则
- 画布 一张无线大的能作画的布,默认以左上角顶点位置为原始坐标
- 画笔 一只能够变换任何颜色,任意粗细程度的画笔
- 填充 在绘制的范围内填充任何颜色,包含渐变和透明
- 图形 圆形/矩形/路径
- 路径记忆 将上次创建过的路径保存起来,下次继续使用
- 变形 添加一层自定义的透明画布进行绘画
- 画布快照 对画布进行编号,拿起上一次遍过号的画布来绘画
准备工作
获取dom
var canvas = document.getElementById("myCanvas")
生成作画空间
var ctx = canvas.getContext("2d")
设置画笔
// 设定画笔颜色 (单位为前端常用的正常色值)
ctx.strokeStyle = "orange"
// 设定线条宽度 (单位为像素)
ctx.lineWidth = 10
// 设置线条末端样式
var lineCap = ['butt','round','square']
ctx.lineCap = lineCap[i];
// 设定线条与线条间接合处的样式
var lineJoin = ['round', 'bevel', 'miter'];
ctx.lineJoin = lineJoin[i]
// 限制当两条线相交时交接处最大长度
ctx.miterLimit = 8.8;
// 设置当前虚线样式 setLineDash数组指定线段与间隙的交替, lineDashOffset属性设置起始偏移量.
ctx.setLineDash([4, 2]);
ctx.lineDashOffset = 0;
// 返回一个包含当前虚线样式,长度为非负偶数的数组
ctx.getLineDash() // [4, 2]
填充
// 设置填充颜色 (单位为前端常用的正常色值)
ctx.fillStyle = "rgba(255,165,0,1)";
// 设定渐变样式 (线性渐变 => 表示渐变的起点 (x1,y1) 与终点 (x2,y2)
var lineargradient = ctx.createLinearGradient(0,0,150,150);
// 上色 (第一个参数表示0.0 与 1.0 之间的数值,第二个参数表示对应位置的颜色)
lineargradient.addColorStop(0,'white');
lineargradient.addColorStop(1,'black');
// 渐变样式能够添加到fillStyle,也能添加到strokeStyle
ctx.fillStyle = lineargradient;
ctx.strokeStyle = lineargradient;
// 设定渐变样式 (径向渐变 => 前三个定义一个以 (x1,y1) 为原点,半径为 r1 的圆,后三个参数则定义另一个以 (x2,y2) 为原点,半径为 r2 的圆。
var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
radgrad.addColorStop(0, '#A7D30C');
radgrad.addColorStop(0.9, '#019F62');
radgrad.addColorStop(1, 'rgba(1,159,98,0)');
// 图案填充 (需要保证img已经加载完毕)
var ptrn = ctx.createPattern(img, 'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0, 0, 150, 150);
// 阴影 (x方向/y方向/阴影模糊程度/阴影颜色)
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 2;
ctx.shadowColor = "rgba(0, 0, 0, 0.5)";
// 填充规则 (该填充规则根据某处在路径的外面或者里面来决定该处是否被填充)
ctx.fill("evenodd"); // nonzero为默认值
图形
// 矩形 (参数为起点,距离)
fillRect(x,y,width,height)
strokeRect(x,y,width,height)
clearRect(x,y,width,height)
// 路径
beginPath() // 新建一条路径
closePath() // 闭合路径
moveTo(x,y) // 移动画笔到某个位置
lineTo(x,y) // 从上一个点开始划线到目标点
// 圆形 (画一个以x,y为圆心的以radius为半径的圆弧/圆,从startAngle开始到endAngle结束,按照anticlockwise给定的方向(默认为顺时针)来生成)
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise)
// 绘制
stroke() // 通过线条来绘制图形轮廓
fill() // 通过填充路径生成实心的图形
// 绘制二次贝塞尔曲线,cp1x,cp1y为一个控制点,x,y为结束点 (注意绘制贝塞尔曲线之前确定有一个起始点)
quadraticCurveTo(cp1x, cp1y, x, y)
// 绘制三次贝塞尔曲线,cp1x,cp1y为控制点一,cp2x,cp2y为控制点二,x,y为结束点 (注意绘制贝塞尔曲线之前确定有一个起始点)
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
路径记忆
// 将对应的线路路径保存起来
new Path2D(); // 空的Path对象
new Path2D(path); // 克隆Path对象
new Path2D(d); // 从SVG建立Path对象
变形
// 温馨提示:在执行变形操作之前,使用ctx.save保存格式快照
translate(x,y) // 移动canvas的原点位置
rotate(angle) // 旋转canvas的角度, 参数为顺时针以弧度为单位的值
scale(x,y) // 缩放画布的水平和垂直单位,默认值为 1
// 最后一个方法允许对变形矩阵直接修改。
// 这个方法会将当前的变形矩阵重置为单位矩阵,然后用相同的参数调用 transform 方法。如果任意一个参数是无限大,那么变形矩阵也必须被标记为无限大,否则会抛出异常。从根本上来说,该方法是取消了当前变形,然后设置为指定的变形,一步完成。
transform(a, b, c, d, e, f)
画布快照
// 使用场景,当需要临时大量改变绘制格式的时候,而之后还要恢复绘制格式的情况下使用
// 保存绘制格式快照
ctx.save()
// 返回上一次绘制格式快照
ctx.restore()
综合场景
// 获取画布地图
var c=document.getElementById("myCanvas");
// 获取2d生成能力
var cxt=c.getContext("2d");
// 设置填充颜色
cxt.fillStyle="#FF0000";
// 设置矩形坐标
cxt.fillRect(0,0,150,75);
// 设置画笔起点
cxt.moveTo(10,10);
// 连接线条到150,50坐标
cxt.lineTo(150,50);
// 从150,50连接线条到10,50坐标
cxt.lineTo(10,50);
// 结束
cxt.stroke();
// 设置开始路径
cxt.beginPath();
// 设置圆的大小
cxt.arc(70,18,15,0,Math.PI*2,true);
// 结束路径
cxt.closePath();
// 填充
cxt.fill();
// 设置渐变背景坐标
var grd=cxt.createLinearGradient(0,0,175,50);
// 设定起点颜色
grd.addColorStop(0,"#FF0000");
// 设定第2起点颜色
grd.addColorStop(1,"#00FF00");
// 填充颜色
cxt.fillStyle=grd;
// 填充到对应的坐标
cxt.fillRect(0,0,175,50);
// 画布设置出图片
var img=new Image()
// new出的图片设置src
img.src="flower.png"
// 绘画从0开始
cxt.drawImage(img,0,0);
canvas 2d 绘图(二)
约定原则:手绘画图原则
- 裁剪
- 合成
- 像素操作
- 动画
- 事件 (点击区域)
- 性能优化
裁剪
// 将当前正在构建的路径转换为当前的裁剪路径 (默认情况下,canvas 有一个与它自身一样大的裁切路径(也就是没有裁切效果))
// 设定矩形 -> 裁剪 -> 填充 => 只显示矩形这一部分的图形
ctx.rect(50, 50, 20, 10);
ctx.clip();
ctx.fillRect(0, 0, 100, 100);
合成
// 这个属性设定了在画新图形时采用的遮盖策略,其值是一个标识12种遮盖方式的字符串
// 通常用于新图形和旧图形相交部分,做自定义处理
ctx.globalCompositeOperation = "difference";
// 下面两个矩形相交的地方,会随着globalCompositeOperation的设置而变更
ctx.fillStyle = "blue";
ctx.fillRect(10, 10, 100, 100);
ctx.fillStyle = "red";
ctx.fillRect(50, 50, 100, 100);
像素操作
// 创建一个ImageData对象
const myImageData = ctx.createImageData(width, height);
// 得到场景像素数据 (参数数据算法和矩形算法一致)
const myImageData = ctx.getImageData(left, top, width, height);
// 获取某一点的rgba像素值
const pixel = ctx.getImageData(0, 0, 1, 1);
const data = pixel.data;
const rgba = `rgba(${data[0]}, ${data[1]}, ${data[2]}, ${data[3] / 255})`; // rgba(67,73,35,1)
// 写入像素数据
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); // 获取整个画布的像素数据
const data = imageData.data;
for (var i = 0; i < data.length; i += 4) { // 改变像素数据
data[i] = 255 - data[i]; // red
data[i + 1] = 255 - data[i + 1]; // green
data[i + 2] = 255 - data[i + 2]; // blue
}
ctx.putImageData(imageData, 0, 0); // 写入像素数据到原本的画布 (这里是将像素反相)
// 缩放 (canvas为原画布), Math.abs(x - 5), Math.abs(y - 5)为获取原画布的x,y起点,10/10表示起点之后的距离,0 0 表示新画布的起点,200 200表示画布的宽高
zoomctx.drawImage(canvas,
Math.abs(x - 5), Math.abs(y - 5),
10, 10, 0, 0, 200, 200);
// 反锯齿属性 (默认为true)
zoomctx.imageSmoothingEnabled = true;
// 保存图片
canvas.toDataURL('image/png') // 默认设定。创建一个PNG图片。
canvas.toDataURL('image/jpeg', quality) // 创建一个JPG图片。你可以有选择地提供从0到1的品质量,1表示最好品质,0基本不被辨析但有比较小的文件大小。
// 你也可以从画布中创建一个Blob对像。
canvas.toBlob(callback, type, encoderOptions) // 这个创建了一个在画布中的代表图片的Blob对像。
// callback 回调函数,可获得一个单独的Blob对象参数。
// type DOMString类型,指定图片格式,默认格式为image/png。
// encoderOptions 可选
// Number类型,值在0与1之间,当请求图片格式为image/jpeg或者image/webp时用来指定图片展示质量。如果这个参数的值不在指定类型与范围之内,则使用默认值,其余参数将被忽略。
动画
// 每隔一段时间执行
window.setInterval() / window.clearInterval()
// 过一段时间执行一次
window.setTimeout() / window.clearTimeout()
// 浏览器重绘之前调用指定的回调函数更新动画
window.requestAnimationFrame() / window.cancelAnimationFrame()
事件
1. 通过设定指定区域,然后判断鼠标点击的位置是否在制定区域从而是否触发动画 (适用于矩形)
2. 先将需要执行点击事件的图形通过离屏绘制出来,然后添加到画布,并且记录距离画布的left/top
点击事件的时候,获取到对应的x,y点,然后使用x-left,y-top , 得到离屏绘制的图形范围内相对应的位置,然后根据这个位置在离屏绘制的图形中判断data的alpha值
window.onload = function () {
// 主画布
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
canvas.width = dw
canvas.height = dh
// 创建一个离屏画布
const { canvas: newCanvas, ctx: newCtx } = created()
// 再创建一个
const { canvas: oldCanvas, ctx: oldCtx } = created()
// 保存对应的ctx到数组
const list = [newCtx, oldCtx]
// 主画布追加离屏画布
ctx.drawImage(newCanvas, -10, -10)
ctx.drawImage(oldCanvas, 100, 100)
// 监听click事件
canvas.addEventListener('click', () => {
// 获取对应的xy
const x = event.layerX
const y = event.layerY
// 循环离屏画布
for (let i = 0; i < list.length; i++) {
// 获取追加离屏画布时候的位置值
const addx = i === 0 ? -10 : 100
const addy = i === 0 ? -10 : 100
// 得到离屏画布的像素值(位置经过计算得到相对应的坐标点)
const pixel = list[1].getImageData(x - addx, y - addy, 1, 1);
const data = pixel.data;
// 判断当前像素点的alpha值是否为 0
if (data[3] !== 0) {
console.log('你点击的是离屏画布 ' + i);
}
}
})
}