基础
HTML5新标签 canvas
-
获取元素 const canvas = document.getElementById("canvas");
-
获得上下文对象 const ctx = canvas.getContext("2d");
绘制折线图
ctx.beginPath();//开始一个新的路径 ctx.moveTo(100,100);//把绘图的 起始点 移动到该坐标 ctx.lineTo(200,200);//从上一个点到该坐标画一条线 ctx.lineTo(300,100);//从上一个点到该坐标画一条线 // ctx.closePath();//闭合路径,连接到起始点 ctx.stroke();//绘制当前定义的路径 // ctx.fill();//填充当前路径 不连接起点的话自动帮我们闭合
绘制矩形
画矩形边框
步骤
-
ctx.rect(x,y,width,height) ---> x,y 左上角坐标
-
ctx.stroke() 绘制路径
或者使用复合写法
-
ctx.strokeRect(x,y,width,height)
填充矩形
-
ctx.rect(x,y,w,h)
-
ctx.fill()
或者使用复合写法
-
ctx.fillRect(x,y,w,h)
橡皮擦
-
ctx.clearRect(x,y,w,h)
-
把位于矩形区域内所有的绘制删除,设置为透明
贝塞尔曲线
二次贝塞尔曲线
-
ctx.quadraticCurveTo(cp1x,cp1y,x,y)
-
cp1x,cp1y:控制点坐标
-
x,y结束点坐标
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> #canvas{ background-color: pink; } </style> </head> <body> <canvas id="canvas" width="500" height="500"></canvas> <script> const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); ctx.beginPath(); ctx.moveTo (100,100); ctx.quadraticCurveTo(150,0,200,100); ctx.stroke() </script> </body> </html>
三次贝塞尔曲线
-
ctx.bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)
文字
-
实心文字 ctx.fillText(text,x,y[,maxWidth])
-
描边文字 ctx.strokeText(text,x,y[,maxWidth])
-
text是文本的内容,xy是文字的起点坐标,maxWidth是文本最大宽度,超过这个宽度文字会被压缩
文字样式
-
ctx.font = "30px bond 家族"
-
ctx.textAlign = "center || left || right"
center :文本中心在 (x,y) ;left 文本左边框在(x,y);right:文本右边框在(x,y)
-
基线对齐 ctx.textBaseLine = "top || middle || hanging ||bottom"
图片
创建一个图片
const img = new image()
设置图片路径
img.src = "xxx/xxx"
绘制图片
-
ctx.drawImage(img对象,x,y,w, h);
//img.src是异步加载的,下载图片需要时间,所以需要在图片加载完成后再绘制 img.onload = function () { // 在图片加载完成后绘制 ctx.drawImage(img,0, 0, 300, 300); // 调整图片尺寸以适应画布 };
如果要图片进行裁剪
上边的 drawImage 要多四个参数,对应原图片裁剪的区域
-
ctx.drawImage(img,sx,sy,sW,sH,dx,dy,dW,dH)
-
裁剪:s参数:
样式
-
填充颜色:ctx.fillStyle = ""
-
轮廓颜色:ctx.strokeStyle = ""
-
默认黑色
线段样式
-
ctx.lineWidth = ""线段宽度
-
ctx.lineCap ="butt || round || square"线帽形状
-
ctx.lineJoin = "round || belev || miter(默认)"
-
虚线长度:ctx.setLineDash([实线长度,间隙长度])
样式的保存
-
ctx.save(),保存save代码上面的样式配置,存到一个栈中
-
ctx.resotre(),取出栈顶样式的配置
变形
-
移动坐标系!不是移动画板:ctx.translate(X,Y)
-
旋转坐标系:ctx.rotate( 弧度角度 )
-
缩放图形:ctx.scale(x,y)
案例
1、小画板
2025/4/11
示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
:root{
--color1:red;
--color2:green;
--color3:hwb(60 27% 22%);
}
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}
body{
background: url("../imgs/flowers.jpg") no-repeat;
}
.nav{
width: 100%;
height: 30px;
/* background-color: red; */
padding-left: 140px;
margin-top:5px ;
}
.name{
float: left;
width: 100px;
height: 100%;
background:linear-gradient(to right, #ff7e5f, #feb47b);
text-align: center;
line-height: 30px;
font-size: 14px;
border-radius:5px ;
margin-right:10px ;
}
button{
float: left;
border: none;
height:100% ;
width:100px;
outline: none;
background:linear-gradient(to right, #ff7e5f, #feb47b);
text-align: center;
font-size: 14px;
border-radius:5px ;
margin-right: 10px
}
.container{
width: 1000px;
height: 80vh;
background-color: hwb(60 69% 19%);
margin: 40px auto;
border-radius: 20px;
}
.top{
width: 100%;
height: 40px;
background-color: lch(86.45% 21.48 86.02);
border-radius:20px 20px 0 0 ;
padding: 8px 0 0 15px ;
}
.dot{
float: left;
width: 26px;
height: 26px;
border-radius: 50%;
margin-right: 10px;
}
.dot:nth-child(1){
background-color: var(--color1);
}
.dot:nth-child(2){
background-color: var(--color2);
}
.dot:nth-child(3){
background-color: var(--color3);
}
.painting{
width: 100%;
height: calc(100% - 40px);
border-radius: 0 0 20px 20px;
background-color: hsl(45, 57%, 86%);
}
</style>
</head>
<body>
<div class="nav">
<div class="name">画板</div>
<button class="reset">重置</button>
<button class="save">保存</button>
</div>
<div class="container">
<div class="top">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
<div class="painting">
<canvas class="canvas" style="border-radius: inherit;"></canvas>
</div>
</div>
<script>
const painting = document.querySelector(".painting");
const paintingRect = painting.getBoundingClientRect();
const canvas = document.querySelector(".canvas");
const ctx = canvas.getContext("2d");
const dots = document.querySelectorAll(".dot");
dots.forEach((dot, index) => {
dot.onclick = () => {
// console.log(index);
switch (index) {
case 0:
ctx.strokeStyle = "red";
break;
case 1:
ctx.strokeStyle = "green";
break;
case 2:
ctx.strokeStyle = "hwb(60 27% 22%)";
break;
}
}
})
//重置
document.querySelector('.reset').onclick = (e) => {
e.preventDefault();
ctx.clearRect(0, 0, paintingRect.width, paintingRect.height);
ctx.fillStyle = "hsl(45, 57%, 86%)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
//保存
document.querySelector('.save').onclick = (e) => {
e.preventDefault();
const img = canvas.toDataURL('image/png');
const a = document.createElement('a');
a.href = img;
a.download = '画板';
a.target='_blank';
a.click();
}
canvas.width = paintingRect.width;
canvas.height = paintingRect.height;
//必须在设置canvas的宽高后 才能使用成功
ctx.fillStyle = "hsl(45, 57%, 86%)";
ctx.fillRect(0, 0, canvas.width, canvas.height);
let startPoint = { x: undefined, y: undefined }; // 起始点
let isDraw = false; // 是否绘画
ctx.strokeStyle = "red";
canvas.onmousedown = e => {
startPoint = { x: e.offsetX, y: e.offsetY }; // 起始点
isDraw = true; // 开始绘画
}
canvas.onmousemove = e => {
// console.log('移动了');
let x = e.offsetX;
let y = e.offsetY;
newPoint = { x: e.offsetX, y: e.offsetY }; // 新的起始点
if (isDraw) { // 开始绘画
drawing(newPoint.x, newPoint.y, startPoint.x, startPoint.y); // 调用绘画函数
}
}
canvas.onmouseup = e => {
// console.log('抬起了');
isDraw = false; // 停止绘画
// 重置后重新绘制背景
}
let drawing = (x, y, startX, startY) => {
ctx.beginPath();
ctx.lineWidth = 2; // 线宽
ctx.lineCap = "round"; // 线帽
ctx.moveTo(startX, startY); // 初始坐标
ctx.lineTo(x, y); // 结束坐标
ctx.stroke(); // 绘制
startPoint = { x: x, y: y }; // 起始点
ctx.closePath()
}
</script>
</body>
</html>
2、小时钟
示例:
function animate(time) {
const now = new Date();
const sec = now.getSeconds();
const min = now.getMinutes();
const hour = now.getHours() % 12; // 12小时制
const ctx = document.getElementById('canvas').getContext('2d');
// 清除画布
ctx.clearRect(0, 0, 600, 600);
// 保存初始状态
ctx.save();
ctx.translate(300, 300); // 将坐标原点移到画布中心
ctx.rotate(-Math.PI / 2); // 将坐标原点旋转90度
// 绘制时钟刻度
ctx.strokeStyle = '#000';
ctx.lineWidth = 5;
ctx.lineCap = 'round';
// 绘制小时刻度
ctx.save();
for (let i = 0; i < 12; i++) {
ctx.beginPath();
ctx.moveTo(100, 0);
ctx.lineTo(120, 0);
ctx.stroke();
ctx.rotate(Math.PI / 6); // 每次旋转30度
}
ctx.restore();
// 绘制分钟刻度
ctx.save();
ctx.lineWidth = 3;
for (let i = 0; i < 60; i++) {
if (i % 5 !== 0) { // 跳过小时刻度的位置
ctx.beginPath();
ctx.moveTo(110, 0);
ctx.lineTo(120, 0);
ctx.stroke();
}
ctx.rotate(Math.PI / 30); // 每次旋转6度
}
ctx.restore();
// 绘制时针
ctx.save();
ctx.lineWidth = 14;
ctx.rotate(
hour * (Math.PI / 6) +
(min * Math.PI / 360) +
(sec * Math.PI / 21600)
);
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(80, 0);
ctx.stroke();
ctx.restore();
// 绘制分针
ctx.save();
ctx.lineWidth = 10;
ctx.rotate(
(min * Math.PI / 30) +
(sec * Math.PI / 1800)
);
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(100, 0);
ctx.stroke();
ctx.restore();
// 绘制秒针
ctx.save();
ctx.strokeStyle = 'red';
ctx.lineWidth = 2;
ctx.rotate(sec * Math.PI / 30);
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(120, 0);
ctx.stroke();
ctx.restore();
// 恢复初始状态
ctx.restore();
// 动画循环
window.requestAnimationFrame(animate);
}
// 启动动画
window.requestAnimationFrame(animate);