canvas 接口说明

绘制canvas步骤
<canvas id="canvasContainer" width="600" height="400"></canvas>
// canvas通过属性设置宽高,如果通过style样式设置宽高会对画布造成比例压缩
// 1. 获取画布
const canvas = document.getElementById("canvasContainer");
// 2. 获取2d绘制上下文
const ctx = canvas.getContext("2d");
// 3. 开始创作吧
描边和填充
// stroke描边
// 例如画一个长方形
ctx.rect(100, 100, 200, 100);
ctx.stroke();
// fill填充
// 例如画一个填充的长方形
ctx.rect(100, 100, 200, 100);
ctx.fill();
路径
ctx.beginPath();
// 画画... 不会跟外面的影响
ctx.closePath();
清除
// 清除长方形
ctx.clearRect(x, y, width, height);
画圆弧
// 圆心、半径、开始角度、结束角度、默认顺时针false
ctx.arc(x, y, radius, startAngle, endAngle)
ctx.arc(x, y, radius, startAngle, endAngle, counterclockwise);
ctx.stroke();
// 画一个圆
ctx.arc(100, 100, 50, 0, Math.PI * 2);
// 根据三个点画圆切得到的圆弧
ctx.moveTo(x, y);
ctx.arcTo(x1, y1, x2, y2, radius);
ctx.stroke();
画线段
ctx.moveTo(x, y);
ctx.lineTo(x1, y1);
ctx.stroke();
贝塞尔曲线
// 二次贝塞尔曲线需要两个点
ctx.beginPath();
ctx.moveTo(50, 20);
ctx.quadraticCurveTo(230, 30, 50, 100);
ctx.stroke();
// 三次贝塞尔曲线需要三个点
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y);
ctx.stroke();
封装路径
const path = new Path2D();
path.moveTo(x, y);
// ...画画画
ctx.stroke(path);
绘制颜色
// 描边颜色
ctx.strokeStyle = "red";
ctx.stroke();
// 填充颜色
ctx.fillStyle = "blue";
ctx.fill();
// 线性渐变
const gradient = ctx.createLinearGradient(20, 0, 220, 0);
// 添加三个色标
gradient.addColorStop(0, "green");
gradient.addColorStop(0.5, "cyan");
gradient.addColorStop(1, "green");
// 设置填充样式并绘制矩形
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 200, 100);
// 径向渐变
const gradient = ctx.createRadialGradient(110, 90, 30, 100, 100, 70);
// 添加三个色标
gradient.addColorStop(0, "pink");
gradient.addColorStop(0.9, "white");
gradient.addColorStop(1, "green");
// 设置填充样式并绘制矩形
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 160, 160);
// 圆锥渐变
const gradient = ctx.createConicGradient(0, 100, 100); // 角度, x, y
// 添加五个色标
gradient.addColorStop(0, "red");
gradient.addColorStop(0.25, "orange");
gradient.addColorStop(0.5, "yellow");
gradient.addColorStop(0.75, "green");
gradient.addColorStop(1, "blue");
// 设置填充样式并绘制矩形
ctx.fillStyle = gradient;
ctx.fillRect(20, 20, 200, 200);
图案样式
const img = new Image();
img.src = "canvas_createpattern.png";
// 请确保在图像加载完成后再使用
img.onload = () => {
const pattern = ctx.createPattern(img, "repeat"); // 水平重复:repeat-x;垂直重复:repeat-y;不重复:no-repeat
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, 300, 300);
};
线条样式
lineWidth: 线条宽度
lineCap: 线段末端样式
lineJoin: 线段连接样式
miterLimit: 斜接限制比例
setLineDash(segments): 设置虚线长度以及虚线间隔
lineDashOffset: 虚线偏移值
阴影设置
shadowOffsetX: 水平阴影偏移
shadowOffsetY: 垂直阴影偏移
shadowColor: 阴影颜色
shadowBlur: 模糊值
绘制图像和视频
// 图片
const img = new Image();
img.src = "./test.png";
img.onload = function() {
// 1. 三个参数时,第二第三个是将图片绘制在画布的水平垂直坐标
drawImage(img, x, y)
// 2. 5个参数时,第二第三同上,第四第五个是将图片缩放的宽高
drawImage(img, x, y, w, h)
// 3. 9个参数时,img对象后四个参数是原始图片裁剪的x,y坐标以及宽高,后四个参数是绘制在画布上的x,y坐标以及缩放的宽高
drawImage(img, sx, sy, sw, sh, x, y, w, h);
}
// 视频
const video = document.getElementById("video");
const btn = document.getElementById("playBtn");
btn.onclick = function() {
if(video.paused) {
video.play();
render();
} else video.pause();
}
function render() {
ctx.drawImage(video, x, y, w, h);
requestAnimationFrame(render)
}
绘制文字
font: 字体
strokeText/fillText: 填充文字,参数:文本,x, y, w, h
textAlign: 对齐方式
textBaseline: 文本基线设置
direction: 方向设置
measureText: 预测文本信息
变换
// 位移
translate(x, y)
// 缩放
scale(x, y)
// 旋转
rotate(angle)
// 变换矩阵
transform: 六个参数
合成图像实现刮刮乐
<div id="ggk">中奖啦</div>
<canvas id="c1"></div>
const img = new Image();
img.src = "./bg.png";
img.onload = function() {
ctx.drawImage(img, 0, 0, 600, 400);
}
let isDraw = false;
canvas.onmousedown = function() {
isDraw = true;
}
canvas.onmousedown = function() {
isDraw = false;
}
canvas.onmousemove = function(e) {
if(isDraw) {
const x = e.pageX;
const y = e.pageY;
ctx.globalCompositeOperation = "destination-out";
ctx.arc(x, y, 20, 0, Math.PI * 2);
ctx.fill();
}
}
裁剪路径
clip()
clip(path)
状态保存和回复
// 存栈
save: 保存
restore: 回复
像素操作
// 获取图片像素信息
ctx.getImageData(x, y, w, h);
// 循环修改数据
for (let i = 0; i < imageData.data.length; i++) {
imageData.data[i] = 255 - imageData.data[i];
imageData.data[i + 1] = 255 - imageData.data[i + 1];
imageData.data[i + 2] = 255- imageData.data[i + 2];
imageData.data[i + 3] = 255 - imageData.data[i + 3];
}
// 重新渲染修改后的图片
ctx.putImageData(imageData, 0, 0)
封装绘制元素
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();
// 监听鼠标移动
c1.onmousemove = (e) => {
let x = e.offsetX;
let y = e.offsetY;
// 判断是否在当前元素上
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();
}
let heart = new Heart(100, 100);
function render() {
ctx.clearRect(0, 0, c1.widht, c1.height);
heart.draw();
requestAninmationFrame(render);
}
render()
}
canvas实现在线画笔
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas实现在线画笔</title>
<style>
canvas {
border: 1px solid black;
}
button.active {
color: white;
background-color: orange;
}
</style>
</head>
<body>
<canvas id="c1" width="800" height="600"></canvas>
<hr>
<button id="blodBtn" type="button">粗线条</button>
<button id="thinBtn" type="button">细线条</button>
<button id="saveBtn" type="button">保存签名</button>
<input type="color" id="color">
<button id="clearBtn" type="button">橡皮擦</button>
<button id="nullBtn" type="button">清空画布</button>
<script>
// 获取canvas元素和context
const canvas = document.querySelector("#c1");
const ctx = c1.getContext("2d");
// 设置连接圆润
ctx.lineJoin = "round";
// 设置末端圆润
ctx.lineCap = "round";
// 获取输入框和按钮
const colorInput = document.querySelector("#color");
const blodBtn = document.querySelector("#blodBtn");
const thinBtn = document.querySelector("#thinBtn");
const saveBtn = document.querySelector("#saveBtn");
const clearBtn = document.querySelector("#clearBtn");
const nullBtn = document.querySelector("#nullBtn");
// 设置允许绘制的变量
let isDraw = false;
canvas.onmousedown = function(e){
// 设置允许绘制
isDraw = true;
ctx.beginPath();
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
ctx.moveTo(x, y);
}
canvas.onmouseup = function(e){
isDraw = false;
ctx.closePath();
}
canvas.onmouseleave = function(e){
isDraw = false;
ctx.closePath();
}
canvas.onmousemove = function(e){
if(isDraw){
const x = e.pageX - canvas.offsetLeft;
const y = e.pageY - canvas.offsetTop;
ctx.lineTo(x, y);
ctx.stroke();
}
}
blodBtn.onclick = function() {
ctx.globalCompositeOperation = "source-over";
ctx.lineWidth = 20;
blodBtn.classList.add("active");
thinBtn.classList.remove("active");
clearBtn.classList.remove("active");
}
thinBtn.onclick = function() {
ctx.globalCompositeOperation = "source-over";
ctx.lineWidth = 2;
thinBtn.classList.add("active");
blodBtn.classList.remove("active");
clearBtn.classList.remove("active");
}
clearBtn.onclick = function() {
ctx.globalCompositeOperation = "destination-out";
ctx.lineWidth = 30;
clearBtn.classList.add("active");
thinBtn.classList.remove("active");
blodBtn.classList.remove("active");
}
nullBtn.onclick = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
saveBtn.onclick = function() {
const dataUrl = canvas.toDataURL();
const a = document.createElement("a");
a.href = dataUrl;
a.download = "signature.png";
a.click();
a.remove();
}
colorInput.onchange = function(e) {
ctx.strokeStyle = e.target.value;
}
</script>
</body>
</html>
canvas绘制时钟
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>canvas绘制时钟</title>
</head>
<body>
<canvas id="c1" width="800" height="600"></canvas>
<script>
// 获取canvas元素
const canvas = document.getElementById('c1');
const ctx = canvas.getContext('2d');
function render() {
// 清除
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 保存当前坐标位置和上下文对象的状态
ctx.save();
// 移动原点
ctx.translate(canvas.width / 2, canvas.height / 2);
ctx.rotate(-Math.PI / 2)
ctx.save();
for (let 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 (let 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();
// 获取当前时间
const date = new Date();
let hour = date.getHours();
let minute = date.getMinutes();
let second = date.getSeconds();
hour = hour > 12 ? hour - 12 : hour;
// 绘制秒针
ctx.rotate(Math.PI * 2 / 60 * second);
ctx.beginPath();
ctx.moveTo(-30, 0);
ctx.lineTo(190, 0);
ctx.lineWidth = 2;
ctx.strokeStyle = 'red';
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.save();
// 绘制分针
ctx.rotate(Math.PI * 2 / 60 * minute + 2 * Math.PI / 60 / 60 * second);
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(130, 0);
ctx.lineWidth = 4;
ctx.strokeStyle = '#888';
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.save();
// 绘制时针
ctx.rotate(Math.PI * 2 / 12 * hour + 2 * Math.PI / 12 / 60 * minute + 2 * Math.PI / 12 / 60 / 60 * second);
ctx.beginPath();
ctx.moveTo(-15, 0);
ctx.lineTo(110, 0);
ctx.lineWidth = 8;
ctx.strokeStyle = '#333';
ctx.stroke();
ctx.closePath();
ctx.restore();
ctx.restore();
requestAnimationFrame(render);
}
render();
</script>
</body>
</html>