1. 绘制直线
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
// 设置粗细
ctx.lineWidth = 3;
// 设置线条颜色
ctx.strokeStyle = 'red';
// 起点 终点 中间点
ctx.moveTo(100, 100);
ctx.lineTo(300, 300);
ctx.lineTo(300, 400);
ctx.stroke();
ctx.closePath();
2. 高清绘制
window.devicePixelRatio
像素比。表示设置的像素和实际像素的比例,如果值为2
表示实际像素是我们设置的2倍
,如果设置100
宽,实际就是200
宽,但我们编写页面用了10
像素会被放置在20
像素的容器中,页面就会显得模糊。
可以将画布按照像素比调整为和像素一致,再用style
调整回最初的大小。
const getPixelRatio = (context) => {
return window.devicePixelRatio || 1;
}
const ratio = getPixelRatio();
const oldWidth = canvas.width;
const oldHeight = canvas.height;
canvas.width = canvas.width * ratio;
canvas.height = canvas.height + ratio;
canvas.style.width = oldWidth + 'px';
canvas.style.height = oldHeight + 'px';
ctx.scale(ratio, ratio);
3. 坐标系绘制
// 提前设置相关属性
const ht = canvas.clientHeight
const wd = canvas.clientWidth
const pad = 20
const bottomPad = 20
const step = 100
const drawAxis = (options) => {
const { ht, wd, pad, bottomPad, step, ctx } = options
// 绘制坐标轴
ctx.beginPath()
ctx.lineWidth = 2
ctx.strokeStyle = 'lightblue'
ctx.moveTo(pad, pad)
ctx.lineTo(pad, ht * 1.5 - bottomPad)
ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad)
ctx.stroke()
ctx.closePath()
// 绘制 X 轴方向刻度
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = '#666'
for (let i = 1; i < Math.floor(wd * 1.5 / step); i++) {
ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad)
ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad - 10)
}
ctx.stroke()
ctx.closePath()
// 绘制 Y 轴方向刻度
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = '#666'
for (let i = 1; i < Math.floor(ht * 1.5 / step); i++) {
ctx.moveTo(pad, (ht * 1.5 - bottomPad) - (i * step))
ctx.lineTo(pad + 10, (ht * 1.5 - bottomPad) - (i * step))
}
ctx.stroke()
ctx.closePath()
}
drawAxis({
ht: ht,
wd: wd,
pad: pad,
bottomPad: bottomPad,
step: step,
ctx: ctx
})
4. 绘制矩形
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.lineWidth = 5;
// 边框颜色
ctx.strokeStyle = 'red';
// 填充颜色
ctx.fillStyle = 'green';
// 起点坐标,宽,高
ctx.rect(100, 100, 300, 200);
// 填充
ctx.fill();
// 描边
ctx.stroke();
ctx.closePath();
5. 直方图绘制
// 绘制坐标轴
ctx.beginPath()
ctx.lineWidth = 2
ctx.strokeStyle = 'lightblue'
ctx.moveTo(pad, pad)
ctx.lineTo(pad, ht * 1.5 - bottomPad)
ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad)
ctx.stroke()
ctx.closePath()
// 绘制 X 轴方向刻度
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = '#666'
for (let i = 1; i < Math.floor(wd * 1.5 / step); i++) {
ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad)
ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad + 10)
}
ctx.stroke()
ctx.closePath()
// 绘制 Y 轴方向刻度
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = '#666'
for (let i = 1; i < Math.floor(ht * 1.5 / step); i++) {
ctx.moveTo(pad, (ht * 1.5 - bottomPad) - (i * step))
ctx.lineTo(pad + 10, (ht * 1.5 - bottomPad) - (i * step))
}
ctx.stroke()
ctx.closePath()
}
drawAxis({
ht: ht,
wd: wd,
pad: pad,
bottomPad: bottomPad,
step: step,
ctx: ctx
})
// 绘制直方图
ctx.beginPath()
for (var i = 1; i < Math.floor(wd * 1.5 / step); i++) {
const height = Math.random() * 300 + 50
ctx.fillStyle = '#' + parseInt(Math.random() * 0xFFFFFF).toString(16)
ctx.fillRect((i * step), ht * 1.5 - bottomPad - height, 40, height)
}
ctx.closePath()
6. 绘制圆形
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.lineWidth = 5;
// 边框颜色
ctx.strokeStyle = 'red';
// 填充颜色
ctx.fillStyle = 'green';
// 起点坐标,半径,起点弧度,终点弧度, 顺时针还是逆时针
ctx.arc(100, 100, 100, 0, Math.PI * 2, false);
// 填充
ctx.fill();
// 描边
ctx.stroke();
ctx.closePath();
7. 饼图绘制
// 提前设置相关属性
const ht = canvas.clientHeight
const wd = canvas.clientWidth
const pad = 20
const bottomPad = 20
const step = 100
const drawAxis = (options) => {
const { ht, wd, pad, bottomPad, step, ctx } = options
// 绘制坐标轴
ctx.beginPath()
ctx.lineWidth = 2
ctx.strokeStyle = 'lightblue'
ctx.moveTo(pad, pad)
ctx.lineTo(pad, ht * 1.5 - bottomPad)
ctx.lineTo(wd * 1.5 - pad, ht * 1.5 - bottomPad)
ctx.stroke()
ctx.closePath()
// 绘制 X 轴方向刻度
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = '#666'
for (let i = 1; i < Math.floor(wd * 1.5 / step); i++) {
ctx.moveTo(pad + i * step, ht * 1.5 - bottomPad)
ctx.lineTo(pad + i * step, ht * 1.5 - bottomPad + 10)
}
ctx.stroke()
ctx.closePath()
// 绘制 Y 轴方向刻度
ctx.beginPath()
ctx.lineWidth = 1
ctx.strokeStyle = '#666'
for (let i = 1; i < Math.floor(ht * 1.5 / step); i++) {
ctx.moveTo(pad, (ht * 1.5 - bottomPad) - (i * step))
ctx.lineTo(pad + 10, (ht * 1.5 - bottomPad) - (i * step))
}
ctx.stroke()
ctx.closePath()
}
drawAxis({
ht: ht,
wd: wd,
pad: pad,
bottomPad: bottomPad,
step: step,
ctx: ctx
})
ctx.beginPath()
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.shadowBlur = 4
ctx.shadowColor = '#333'
ctx.fillStyle = '#5C1918'
ctx.moveTo(400, 300)
ctx.arc(400, 300, 100, -Math.PI / 2, -Math.PI / 4)
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.shadowBlur = 4
ctx.shadowColor = '#5C1918'
ctx.fillStyle = '#A32D29'
ctx.moveTo(400, 300)
ctx.arc(400, 300, 110, -Math.PI / 4, Math.PI / 4)
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.shadowBlur = 4
ctx.shadowColor = '#A32D29'
ctx.fillStyle = '#B9332E'
ctx.moveTo(400, 300)
ctx.arc(400, 300, 120, Math.PI / 4, Math.PI * 5 / 8)
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.shadowBlur = 4
ctx.shadowColor = '#B9332E'
ctx.fillStyle = '#842320'
ctx.moveTo(400, 300)
ctx.arc(400, 300, 130, Math.PI * 5 / 8, Math.PI)
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.shadowOffsetX = 0
ctx.shadowOffsetY = 0
ctx.shadowBlur = 4
ctx.shadowColor = '#842320'
ctx.fillStyle = '#D76662'
ctx.moveTo(400, 300)
ctx.arc(400, 300, 140, Math.PI, Math.PI * 3 / 2)
ctx.fill()
ctx.closePath()
8. 绘制文字
// 填充文字
ctx.fillStyle = 'origin';
ctx.font = 'bold 50px 微软雅黑'; // 字体大小
ctx.fillText('隐冬', 100, 100); // 坐标位置
// 描边文字
ctx.strokeStyle = 'red';
ctx.strokeText('隐冬', 100, 200);
// 对齐属性设置
// 水平方向
ctx.textAligin = 'center'; //left right start end center, left和start效果相同,right和end效果也相同。
// 垂直反向
ctx.textBaseline = 'middle'; // top bottom middle
ctx.fillText('隐冬', 400, 300);
9. 运动
canvas
绘制运动的思路很简单,使用前面提到的api
进行图案绘制,然后使用clearRect
方法将绘制的内容清除,再使用api
在不同的位置画出,这样就实现了动画。
这里使用drawCircle
方法绘制一个圆,函数接收远点坐标的半径,然后使用setInterval
定时器不停地调用这个方法就可以了。
const drawCircle = (x, y, r) => {
ctx.beginPath()
ctx.fillStyle = 'orange'
ctx.arc(x, y, r, 0, Math.PI * 2)
ctx.fill()
ctx.closePath()
}
// 配置属性
const wd = canvas.clientWidth * 1.5
const ht = canvas.clientHeight * 1.5
let x = y = 100
const r = 20
let xSpeed = 6
let ySpeed = 4
drawCircle(x, y, r)
setInterval(() => {
ctx.clearRect(0, 0, wd, ht) // 清空画布
if (x - r <= 0 || x + r >= wd) {
xSpeed = -xSpeed
}
if (y - r <= 0 || y + r >= ht) {
ySpeed = -ySpeed
}
x += xSpeed
y += ySpeed
drawCircle(x, y, r)
}, 20)
可以同时定义多个小球。比如定义20
个小球。还是很有意思的。一般canvas
的动画就是这样来实现的。