vscode中canvas没有提示的,可以加这个
/** @type {HTMLCanvasElement} */
const can = document.querySelector("#id名");
获取画布
//canvas
<canvas width="600" height="400" id="c1_box"></canvas>
//获取画笔,上下文对象
var c1 = document.getElementById('c1_box')
判断当前浏览器是否支持canvas
//判断是否有getContext
if (!c1.getContext){
console.log("当前浏览器不支持canvas□请下戟最新的浏览器");
}
获取画笔
var ctx = c1.getContext('2d')
绘制矩形
填充式:fi11Rect(位置x,位置y,宽度,高度)
//一体式写法
ctx.fillRect(100,100,200,200)
//拆开写法(beginPath和closePath可以完成路径的分段)
ctx.beginPath();// 路径开始
ctx.rect(100,100,200,200)
ctx.fill()
ctx.closePath();// 路径结束
路径式:strokeRect(X1,y1,矩形地度,矩形高度)
//一体式写法
ctx.strokeRect(100,100,200,100);
//拆开写法(beginPath和closePath可以完成路径的分段)
ctx.beginPath();// 路径开始
ctx.rect(100,100,200,200)
ctx.stroke()
ctx.closePath();// 路径结束
清除式:clearRect(X1,y1,宽度, 高度)
//间隔函数清除
var height = 0;
var time = setInterval(() => {
height++;
if (height > c1.clientHeight) {
clearInterval(time);
}
ctx.clearRect(0, 0, c1.clientWidth, value);
}, 10);
绘制圆弧
ctx.arc(圆形x,圆形y,半径,开始的角度,结束的角度,是否是顺时针(默认是顺时针false,逆时针true))
ctx.arc(200, 200, 50, 0, Math.PI);
//ctx.fill() //填充
ctx.stroke(); //路径
移动画笔
moveTo(moveTo() 方法把路径移动到画布中的指定点,不创建线条。)
//绘制笑脸示例
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.beginPath();
//绘制圆
ctx.arc(100, 100, 50, 0, Math.PI * 2, true);
ctx.moveTo(130, 100);
//绘制嘴巴
ctx.arc(100, 100, 30, 0, Math.PI);
ctx.moveTo(90, 80);
//绘制左眼
ctx.arc(80, 80, 10, 0, Math.PI, true);
ctx.moveTo(130, 80);
//绘制右眼
ctx.arc(120, 80, 10, 0, Math.PI, true);
ctx.stroke();
ctx.closePath();
</script>
绘制折线
lineTo(x,y)绘制一条从当前位置到指定x以及y位置的直线
//绘制三角形
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.lineTo(300, 300);
ctx.lineTo(400, 300);
ctx.lineTo(300, 200);
ctx.stroke();
ctx.closePath();
</script>
创建介于两个切线之间的弧/曲线
arcTo(x1, y1, x2, y2, radius),利用当前端点、端点1(x1,y1)和端点2(x2,y2)这三个点所形成的夹角
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.arcTo(200, 50, 200, 100, 50);
ctx.stroke();
ctx.closePath();
</script>
贝塞尔二次曲线
quadraticCurveTo(cpx,cpy,x,y);
cpx:贝塞尔控制点的 x 坐标。cpy:贝塞尔控制点的 y 坐标。x:结束点的 x 坐标。y:结束点的 y 坐标。
//气泡框绘制
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.quadraticCurveTo(250, 200, 250, 100);
ctx.quadraticCurveTo(250, 50, 350, 50);
ctx.quadraticCurveTo(450, 50, 450, 100);
ctx.quadraticCurveTo(450, 200, 350, 200);
ctx.quadraticCurveTo(350, 300, 300, 300);
ctx.quadraticCurveTo(350, 300, 300, 200);
ctx.stroke();
ctx.closePath();
</script>
贝塞尔三次曲线
bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y);
cp1x 第一个贝塞尔控制点的 x 坐标。cp1y 第一个贝塞尔控制点的 y 坐标。
cp2x 第二个贝塞尔控制点的 x 坐标。cp2y 第二个贝塞尔控制点的 y 坐标。
x 结束点的 x 坐标。y 结束点的 y 坐标。
//三次曲线绘制爱心
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.bezierCurveTo(380, 120, 430, 200, 300, 300);
// ctx.moveTo(300, 200);
// ctx.bezierCurveTo(220, 120, 170, 200, 300, 300);
ctx.bezierCurveTo(170, 200, 220, 120, 300, 200);
ctx.stroke();
ctx.closePath();
</script>
Path2D对象
Path2D()可使用最新版本的浏览器中的对象来缓存或记录这些绘图命令,可以快速播放路径。
主要用于复用封装好的canvas路径
//path2d封装爱心路径
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var heartPath = new Path2D();
heartPath.moveTo(300, 200);
heartPath.bezierCurveTo(380, 120, 430, 200, 300, 300);
heartPath.bezierCurveTo(170, 200, 220, 120, 300, 200);
ctx.stroke(heartPath);
</script>
使用svg路径
可以使用svg路径来初始化画布上的路径,。这可能使您可以传递路径数据并在SVG和canvas中重复使用它们。
//svg绘制正方形路径。M指的是移动到指定点,h指的是水平绘制(horizontal的缩写),v指的是垂直绘制(vertical的缩写)
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var squrePath = new Path2D("M 10 10 h 100 v 100 h -100 z");
ctx.stroke(squrePath);
</script>
颜色样式控制
fillStye 填充颜色设置 ,格式支持:fillStyle = "#FF00FF";fillStyle = "rgba(255,150,200,1)";fillStyle = "skyblue";
strokeStyle 线条颜色设置,格式同上
globalAlpha 全局透明度
示例如下:
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.globalAlpha = 0.2;
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.fillStyle = "#FF00FF";
ctx.fillRect(200, 50, 200, 100);
ctx.closePath();
ctx.beginPath();
ctx.moveTo(200, 200);
ctx.lineTo(200, 300);
ctx.lineTo(300, 300);
ctx.lineTo(200, 200);
ctx.strokeStyle = "rgba(255,150,200,1)";
ctx.strokeStyle = "red";
ctx.stroke();
ctx.fillStyle = "skyblue";
ctx.fill();
ctx.closePath();
</script>
线性渐变
createLinearGradient(x0,y0,x1,y1);
x0 渐变开始点的 x 坐标,y0 渐变开始点的 y 坐标,x1 渐变结束点的 x 坐标,y1 渐变结束点的 y 坐标
addColorStop() 方法规定 gradient 对象中的位置和颜色。
addColorStop(stop,color);
stop 介于 0.0 与 1.0 之间的值,表示渐变中开始与结束之间的位置。
color 在结束位置显示的 CSS 颜色值。
requestAnimationFrame()
window.requestAnimationFrame() 的用法与 setTimeout() 很相似,只是不需要设置时间间隔而已
requestAnimationFrame() 会定时的执行一个回调函数,这个时间大概是 60次/s
也就是说大概每秒 60 帧这样子(为什么是 60 ,因为主流的液晶显示器的刷新频率就是 60Hz)
//粉色刷新条效果
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
console.log(c1.getContext, "c1");
var ctx = c1.getContext("2d");
let index = 0;
function render() {
ctx.clearRect(0, 0, 600, 400);
index += 0.01;
if (index > 1) {
index = 0;
}
ctx.beginPath();
var linearGradient = ctx.createLinearGradient(150, 150, 350, 250);
linearGradient.addColorStop(0, "red");
linearGradient.addColorStop(index, "pink");
linearGradient.addColorStop(1, "blue");
ctx.fillStyle = linearGradient;
ctx.fillRect(150, 150, 200, 100);
ctx.closePath();
requestAnimationFrame(render);
}
requestAnimationFrame(render);
</script>
径向渐变
createRadialGradient(x0,y0,r0,x1,y1,r1);
x0 渐变的开始圆的 x 坐标。y0 渐变的开始圆的 y 坐标。r0 开始圆的半径。
x1 渐变的结束圆的 x 坐标。y1 渐变的结束圆的 y 坐标。r1 结束圆的半径。
//径向渐变绘制3D球
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
console.log(c1.getContext, "c1");
var ctx = c1.getContext("2d");
let ballGradient = ctx.createRadialGradient(150, 150, 10, 200, 200, 100);
ballGradient.addColorStop(0, "pink");
ballGradient.addColorStop(1, "red");
ctx.fillStyle = ballGradient;
ctx.arc(200, 200, 100, 0, Math.PI * 2);
ctx.fill();
</script>
圆锥渐变
createConicGradient(角度,位置x,位置y)
//圆锥渐变绘制示例
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var coneGradient = ctx.createConicGradient(0, 300, 200);
coneGradient.addColorStop(0, "red");
coneGradient.addColorStop(0.5, "yellow");
coneGradient.addColorStop(1, "blue");
ctx.fillStyle = coneGradient;
ctx.fillRect(0, 0, 600, 400);
</script>
pattern样章填充样式
createPattern(image,"repeat|repeat-x|repeat-y|no-repeat");
image 规定要使用的模式的图片、画布或视频元素。(可以是image对象,也可以是canvas对象)
repeat 默认。该模式在水平和垂直方向重复。repeat-x 该模式只在水平方向重复。
repeat-y 该模式只在垂直方向重复。no-repeat 该模式只显示一次(不重复)。
//pattern图片填充
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var moneyImg = new Image();
moneyImg.src = "./imgs/money.png";
moneyImg.onload = function () {
var moneyPattern = ctx.createPattern(moneyImg, "repeat");
ctx.fillStyle = moneyPattern;
ctx.fillRect(0, 0, 600, 400);
};
</script>
线段设置
线条宽度:lineWidth=number;(number 当前线条的宽度,以像素计)
设置或返回线条末端线帽的样式:lineCap="butt|round|square";(butt 默认,向线条的每个末端添加平直的边缘。round 向线条的每个末端添加圆形线帽。square 向线条的每个末端添加正方形线帽。)
设置或返回所创建两条线交汇时边角的类型:lineJoin="bevel|round|miter";(bevel 创建斜角。round 创建圆角。miter 默认。创建尖角。)
设置或返回最大斜接长度:miterLimit=number;(number 正数。规定最大斜接长度。如果斜接长度超过 miterLimit 的值,边角会以 lineJoin 的 "bevel" 类型来显示。)
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.moveTo(50, 50);
ctx.lineTo(70, 100);
ctx.lineTo(90, 50);
ctx.lineTo(110, 100);
ctx.lineTo(130, 50);
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.lineJoin = "bevel";
ctx.miterLimit = 2;
ctx.stroke();
</script>
虚线样式
setLineDash(segments);(segments 一个数组。一组描述交替绘制线段和间距(坐标空间单位)长度的数字
如果传递的 segments 数组元素的数量是奇数, 数组的元素会被复制并重复例如, [5, 15, 25] 会变成 [5, 15, 25, 5, 15, 25])
设置虚线偏移量:lineDashOffset = value;(value偏移量是 float 精度的数字。初始值为 0.0。)
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
let index = 0;
var render = function () {
ctx.clearRect(0, 0, 600, 400);
if (index > 400) {
index = 0;
}
index += 1;
ctx.moveTo(50, 50);
ctx.lineTo(100, 100);
ctx.lineTo(150, 50);
ctx.lineTo(200, 100);
ctx.lineTo(250, 50);
ctx.lineWidth = 1;
ctx.setLineDash([10, 5]);
ctx.lineDashOffset = index;
ctx.stroke();
requestAnimationFrame(render);
};
requestAnimationFrame(render);
</script>
阴影
shadowColor 设置或返回用于阴影的颜色。
shadowBlur 设置或返回阴影的模糊级别。
shadowOffsetX 设置或返回阴影到形状的水平距离。
shadowOffsetY 设置或返回阴影到形状的垂直距离。
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.beginPath();
ctx.moveTo(300, 200);
ctx.bezierCurveTo(380, 120, 430, 200, 300, 300);
ctx.bezierCurveTo(170, 200, 220, 120, 300, 200);
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.shadowColor = "red";
ctx.shadowBlur = 5;
ctx.stroke();
ctx.closePath();
</script>
绘制图片的三种模式
第一种绘制图片的方式,参数1为图片对象,参数2为将图片谊染到画布的水平位置,参数3将图片谊染到画布的垂直位置:
ctx.drawImage(img,0,0);
第二种绘制图片,参数1为图片对象,参数2为将图片谊染到画布的水平位置,参数3将图片谊染到画布的垂直位置,参数4将图片缩放到对应的宽度,参数5将图片缩放到对
ctx.drawImage(img,0,0,600,400);
第三种绘制图片的方式,mg参数后面的四个参数分别为源图片上面你要裁剪的像素起点位置和矩形的宽高,后面四个参数分别为画布的位置和要渲染的额矩形宽高
ctx.drawImage(img, 122, 225, 225, 225, 0, 0, 600, 400);
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var dogImg = new Image();
dogImg.src = "./imgs/dog.jpeg";
dogImg.onload = function () {
ctx.drawImage(dogImg, 122, 225, 225, 225, 0, 0, 600, 400);
};
</script>
绘制动态视频并添加水印
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var cat_video = document.createElement("video");
cat_video.src = "./imgs/cat.mp4";
var btn = document.querySelector("#btn");
btn.addEventListener("click", () => {
if (cat_video.paused) {
cat_video.play();
render();
} else {
cat_video.pause();
}
});
let image = new Image();
image.src = "./imgs/baidu_logo.png";
function render() {
ctx.clearRect(0, 0, 600, 400);
ctx.drawImage(cat_video, 0, 0, 600, 400);
ctx.drawImage(image, 400, 350, 200, 50);
requestAnimationFrame(render);
}
</script>
绘制文字
文字 大小,字体
ctx.font = "48px serif";
填充渲染文字
fillText(text,x,y,maxWidth);
text 规定在画布上输出的文本。
x 开始绘制文本的 x 坐标位置(相对于画布)。y 开始绘制文本的 y 坐标位置(相对于画布)。
maxWidth 可选。允许的最大文本宽度,以像素计。
ctx.fillText("Hello Canvas", 300, 200, 100);
strokeText(text,x,y,maxWidth);
同上
文本对齐
textAlign="center|end|left|right|start";
textAlign 属性根据锚点,设置或返回文本内容的当前对齐方式。
基线对齐
textBaseline="alphabetic|top|hanging|middle|ideographic|bottom";
textBaseline 属性设置或返回在绘制文本时的当前文本基线。默认alphabetic。
预测量文本宽度
当你需要获得更多的文本细节时,下面的方法可以给你测量文本的方法。
measureText()
将返回一个 TextMetrics对象的宽度、所在像素,这些体现文本特性的属性。
下面的代码段将展示如何测量文本来获得它的宽度:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var text = ctx.measureText("foo"); // TextMetrics object
text.width; // 16;
}
位移,缩放,旋转变换
移动 translate(x, y)
translate方法位移坐标系,接受两个参数。*x *是左右偏移量,y 是上下偏移量
缩放scale(x, y)
scale方法可以缩放画布的水平和垂直的单位(拉伸坐标系)。两个参数都是实数,可以为负数,x 为水平缩放因子,y 为垂直缩放因子
旋转rotate(angle)
这个方法只接受一个参数:旋转的角度 (angle),它是顺时针方向的,以弧度为单位的值。
旋转的中心点始终是 canvas 的原点,如果要改变它,我们需要用到 translate方法。
矩阵transform变换(了解)
transform(a, b, c, d, e, f)
这个方法是将当前的变形矩阵乘上一个基于自身参数的矩阵,如下面的矩阵所示:
这个函数的参数各自代表如下:
a (m11)水平方向的缩放
b(m12)竖直方向的倾斜偏移
c(m21)水平方向的倾斜偏移
d(m22)竖直方向的缩放
e(dx)水平方向的移动
f(dy)竖直方向的移动
//以下代码代表坐标系不变,位移x100,y200
transform(1, 0, 0, 1, 100, 200);
合成图像
globalCompositeOperation="source-over";
globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有的)的图像上。
源图像 = 您打算放置到画布上的绘图。
目标图像 = 您已经放置在画布上的绘图。
属性值
值 |
描述 |
source-over |
默认。在目标图像上显示源图像。 |
source-atop |
在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 |
source-in |
在目标图像中显示源图像。只有目标图像之内的源图像部分会显示,目标图像是透明的。 |
source-out |
在目标图像之外显示源图像。只有目标图像之外的源图像部分会显示,目标图像是透明的。 |
destination-over |
在源图像上显示目标图像。 |
destination-atop |
在源图像顶部显示目标图像。目标图像位于源图像之外的部分是不可见的。 |
destination-in |
在源图像中显示目标图像。只有源图像之内的目标图像部分会被显示,源图像是透明的。 |
destination-out |
在源图像之外显示目标图像。只有源图像之外的目标图像部分会被显示,源图像是透明的。 |
lighter |
显示源图像 + 目标图像。 |
copy |
显示源图像。忽略目标图像。 |
xor |
使用异或操作对源图像与目标图像进行组合。 |
//刮刮卡源码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
position: absolute;
z-index: 10;
}
.reward {
width: 600px;
height: 400px;
position: absolute;
text-align: center;
line-height: 400px;
font-size: 30px;
font-weight: 700;
}
</style>
</head>
<body>
<div class="reward" id="reward">谢谢惠顾!</div>
<canvas width="600" height="400" id="c1_box"></canvas>
</body>
</html>
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var img_bg = new Image();
img_bg.src = "./imgs/zzc.jpeg";
img_bg.onload = () => {
ctx.drawImage(img_bg, 0, 0, 600, 400);
};
var isClean = false;
c1.onmousedown = () => {
isClean = true;
};
c1.onmouseup = () => {
isClean = false;
};
c1.onmousemove = (e) => {
ctx.beginPath();
if (isClean) {
var x = e.pageX;
var y = e.pageY;
ctx.globalCompositeOperation = "destination-out";
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.fill();
}
ctx.closePath();
};
var random_number = Math.random();
if (random_number < 0.1) {
document.querySelector("#reward").innerHTML =
"恭喜你获得一台IPONRE PRO 14大奖";
}
</script>
裁剪
clip() 方法从原始画布中剪切任意形状和尺寸。
提示:一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。
//裁剪爱心区域
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var heartPath = new Path2D();
heartPath.moveTo(300, 200);
heartPath.bezierCurveTo(380, 120, 430, 200, 300, 300);
heartPath.bezierCurveTo(170, 200, 220, 120, 300, 200);
ctx.clip(heartPath);
var img = new Image();
img.src = "./imgs/dog.jpeg";
img.onload = () => {
ctx.drawImage(img, 0, 0, 600, 400);
};
</script>
状态的保存和恢复
save():用来保存Canvas的状态。save以后,能够调用Canvas的平移、放缩、旋转、错切、裁剪等操做。
restore():用来恢复Canvas以前保存的状态。防止save后对Canvas执行的操做对后续的绘制有影响。blog
save和restore要配对使用(restore能够比save少,但不能多),若是restore调用次数比save多,会引起Error。(先进后出)
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
console.log(c1.getContext, "c1");
var ctx = c1.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 100, 100);
ctx.save();
ctx.fillStyle = "blue";
ctx.fillRect(100, 100, 100, 100);
ctx.save();
ctx.fillStyle = "yellow";
ctx.fillRect(200, 200, 100, 100);
ctx.save();
ctx.restore();
ctx.fillRect(300, 300, 100, 100);
ctx.restore();
ctx.fillRect(400, 400, 100, 100);
ctx.restore();
ctx.fillRect(500, 500, 100, 100);
</script>
像素操作
getImageData() 方法返回 ImageData 对象,该对象拷贝了画布指定矩形的像素数据。
对于 ImageData 对象中的每个像素,都存在着四方面的信息,即 RGBA 值:
R - 红色(0-255)
G - 绿色(0-255)
B - 蓝色(0-255)
A - alpha 通道(0-255; 0 是透明的,255 是完全可见的)
color/alpha 信息以数组形式存在(4个为一组),并存储于 ImageData 对象的data属性中。
getImageData(x,y,width,height);
x 开始复制的左上角位置的 x 坐标(以像素计)。
y 开始复制的左上角位置的 y 坐标(以像素计)。
width 要复制的矩形区域的宽度。
height 要复制的矩形区域的高度。
putImageData(imgData,x,y,dirtyX,dirtyY,dirtyWidth,dirtyHeight);
putImageData() 方法将图像数据(从指定的 ImageData 对象)放回画布上。
imgData 规定要放回画布的 ImageData 对象。
x ImageData 对象左上角的 x 坐标,以像素计。
y ImageData 对象左上角的 y 坐标,以像素计。
dirtyX 可选。水平值(x),以像素计,在画布上放置图像的位置。
dirtyY 可选。水平值(y),以像素计,在画布上放置图像的位置。
dirtyWidth 可选。在画布上绘制图像所使用的宽度。
dirtyHeight 可选。在画布上绘制图像所使用的高度。
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
var dogImg = new Image();
dogImg.src = "./imgs/dog.jpeg";
dogImg.onload = function () {
ctx.drawImage(dogImg, 122, 225, 225, 225, 0, 0, 600, 400);
let imageData = ctx.getImageData(0, 0, 600, 400);
for (var i = 0; i < imageData.data.length; i += 4) {
var avg =
(imageData.data[i] + imageData.data[i + 1] + imageData.data[i + 2]) / 3;
imageData.data[i] = avg;
imageData.data[i + 1] = avg;
imageData.data[i + 2] = avg;
imageData.data[i + 3] = 255;
}
// ctx.putImageData(imageData, 0, 0);
ctx.putImageData(imageData, 0, 0, 300, 200, 300, 200);
};
</script>
高级封装绘制元素
isPointInPath(x,y);如果指定的点位于当前路径中,isPointInPath() 方法返回 true,否则返回 false。
x 要测试的 x 坐标。
y 要测试的 y 坐标。
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
class Heart {
constructor(x, y) {
this.x = x;
this.y = y;
this.color = "red";
this.heartPath = new Path2D();
this.heartPath.moveTo(this.x, this.y);
this.heartPath.bezierCurveTo(
this.x + 80,
this.y - 80,
this.x + 130,
this.y,
this.x,
this.y + 100
);
this.heartPath.bezierCurveTo(
this.x - 130,
this.y,
this.x - 80,
this.y - 80,
this.x,
this.y
);
c1.onmousemove = (e) => {
let x = e.pageX;
let y = e.pageY;
let isIn = ctx.isPointInPath(this.heartPath, x, y);
if (isIn) {
this.color = "blue";
} else {
this.color = "red";
}
};
}
draw() {
ctx.save();
ctx.fillStyle = this.color;
ctx.fill(this.heartPath);
ctx.restore();
}
}
const heart = new Heart(300, 200);
function render() {
ctx.clearRect(0, 0, c1.offsetWidth, c1.offsetHeight);
heart.draw();
requestAnimationFrame(render);
}
render();
</script>
canvas对象转换为base64位编码
toDataURL(type, encoderOptions),将canvas对象转换为base64位编码
type指定转换为base64编码后图片的格式,如:image/png、image/jpeg、image/webp等等,默认为image/png格式;
encoderOptions用于设置转换为base64编码后图片的质量,取值范围为0-1,超出取值范围用默认值0.92代替;
//画板案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 1px solid #000;
}
.button_active {
background-color: skyblue;
}
</style>
</head>
<body>
<canvas width="1400" height="600" id="c1_box"></canvas>
<hr />
<button id="boldLine">粗线条</button>
<button id="thinLine">细线条</button>
<button id="save">保存签名</button>
<input type="color" id="color" />
<button id="rubber">橡皮擦</button>
<button id="none">清空画布</button>
</body>
</html>
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
ctx.lineJoin = "round";
//是否作画
var isDraw = false;
// 获取元素
let boldLine = document.querySelector("#boldLine");
let thinLine = document.querySelector("#thinLine");
let save = document.querySelector("#save");
let color = document.querySelector("#color");
let rubber = document.querySelector("#rubber");
let none = document.querySelector("#none");
c1.onmousedown = (e) => {
ctx.beginPath();
isDraw = true;
let x = e.pageX - c1.offsetLeft;
let y = e.pageY - c1.offsetTop;
ctx.moveTo(x, y);
};
c1.onmousemove = (e) => {
if (isDraw) {
let x = e.pageX - c1.offsetLeft;
let y = e.pageY - c1.offsetTop;
ctx.lineTo(x, y);
ctx.stroke();
}
};
c1.onmouseleave = (e) => {
isDraw = false;
ctx.closePath();
};
c1.onmouseup = (e) => {
isDraw = false;
ctx.closePath();
};
boldLine.onclick = () => {
ctx.globalCompositeOperation = "source-over";
boldLine.classList.add("button_active");
thinLine.classList.remove("button_active");
rubber.classList.remove("button_active");
ctx.lineWidth = 20;
};
thinLine.onclick = () => {
ctx.globalCompositeOperation = "source-over";
thinLine.classList.add("button_active");
boldLine.classList.remove("button_active");
rubber.classList.remove("button_active");
ctx.lineWidth = 2;
};
rubber.onclick = () => {
rubber.classList.add("button_active");
thinLine.classList.remove("button_active");
boldLine.classList.remove("button_active");
ctx.lineWidth = 30;
ctx.globalCompositeOperation = "destination-out";
};
none.onclick = () => {
ctx.clearRect(0, 0, c1.width, c1.height);
};
color.onchange = () => {
ctx.strokeStyle = color.value;
};
save.onclick = () => {
const downHref = c1.toDataURL();
let download_a = document.createElement("a");
download_a.setAttribute("download", "个性签名");
download_a.href = downHref;
download_a.click();
};
</script>
案例时钟
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
canvas {
border: 1px solid #ccc;
}
</style>
</head>
<body>
<canvas width="800" height="600" id="c1_box"></canvas>
</body>
</html>
<script>
/** @type {HTMLCanvasElement} */
var c1 = document.getElementById("c1_box");
var ctx = c1.getContext("2d");
function render() {
ctx.clearRect(0, 0, c1.width, c1.height);
ctx.save();
ctx.translate(400, 300);
ctx.rotate(-Math.PI / 2);
ctx.save();
for (var i = 0; i < 12; i++) {
ctx.beginPath();
ctx.moveTo(170, 0);
ctx.lineTo(190, 0);
ctx.lineWidth = 8;
ctx.strokeStyle = "gray";
ctx.stroke();
ctx.closePath();
ctx.rotate((Math.PI * 2) / 12);
}
ctx.restore();
ctx.save();
for (var i = 0; i < 60; i++) {
ctx.beginPath();
ctx.moveTo(180, 0);
ctx.lineTo(190, 0);
ctx.lineWidth = 2;
ctx.strokeStyle = "gray";
ctx.stroke();
ctx.closePath();
ctx.rotate((Math.PI * 2) / 60);
}
ctx.restore();
ctx.save();
var time = new Date();
var hour = time.getHours();
var min = time.getMinutes();
var sec = time.getSeconds();
hour = hour > 12 ? hour - 12 : hour;
//秒针
ctx.rotate(((Math.PI * 2) / 60) * sec);
ctx.beginPath();
ctx.moveTo(180, 0);
ctx.lineTo(-30, 0);
ctx.lineWidth = 2;
ctx.strokeStyle = "red";
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.save();
//分针
ctx.rotate(((Math.PI * 2) / 60) * min + ((Math.PI * 2) / 60 / 60) * sec);
ctx.beginPath();
ctx.moveTo(150, 0);
ctx.lineTo(-30, 0);
ctx.lineWidth = 2;
ctx.strokeStyle = "#888";
ctx.stroke();
ctx.closePath();
ctx.restore();
//时针
ctx.rotate(
((Math.PI * 2) / 12) * hour +
((Math.PI * 2) / 12 / 60) * min +
((Math.PI * 2) / 60 / 60) * sec
);
ctx.beginPath();
ctx.moveTo(100, 0);
ctx.lineTo(-30, 0);
ctx.lineWidth = 5;
ctx.strokeStyle = "#000";
ctx.stroke();
ctx.closePath();
ctx.restore();
requestAnimationFrame(render);
}
render();
</script>