Canvas常用的几个API

767 阅读1分钟

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. 坐标系绘制

屏幕快照 2021-06-26 20.54.14.png

// 提前设置相关属性
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. 直方图绘制

屏幕快照 2021-06-26 20.57.30.png

// 绘制坐标轴
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. 饼图绘制

屏幕快照 2021-06-26 20.57.54.png

// 提前设置相关属性
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. 绘制文字

屏幕快照 2021-06-27 14.24.31.png

// 填充文字
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)

11111.gif

可以同时定义多个小球。比如定义20个小球。还是很有意思的。一般canvas的动画就是这样来实现的。

11111.gif