抗锯齿
折线问题
通过 lineTo
实现自由画笔,本质是点与点连接的线段,无论点如何密集,都无法避免线段间的折线,快速移动下爆点率更低,线段更长,折线还会更明显。
1 2
| ctx.moveTo(beginPoint.x, beginPoint.y) ctx.lineTo(endPoint.x, endPoint.y)
|
Demo
1 2
| ctx.moveTo(beginPoint.x, beginPoint.y) ctx.quadraticCurveTo(ctrlPoint.x, ctrlPoint.y, endPoint.x, endPoint.y)
|
二次贝塞尔曲线需要三个点,起点、终点和控制点,控制点确定了曲线的方向和形状。以 [A, B, C, D, E, F]
六点为例,其关键点算法为,以 A
为起点,B
为控制点,B
, C
的中间点 B1
为终点,绘制二次贝塞尔曲线线段,接下来以 B1
为起点,C
为控制点,C
, D
的中间点 C1
为终点,依此类推,直到最后。
注:使用最后两个点的中间点作为终点,而不是最后一个点作为终点,可以使得曲线的转折更加平滑,更自然地过渡,而不会出现突然的转折。除了首尾点外,整个线条串过的均为中间点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const points = [] function handleDrawing(ev) { const curPoint = getPosition(ev) points.push(curPoint) if (points.length > 3) { const lastTwoPoints = points.slice(-2) const ctrlPoint = lastTwoPoints[0] const endPoint = { x: (lastTwoPoints[0].x + lastTwoPoints[1].x) / 2, y: (lastTwoPoints[0].y + lastTwoPoints[1].y) / 2 } drawLine(ctrlPoint, beginPoint, endPoint) beginPoint = endPoint } }
|
Demo
高分屏显示虚化
在 Retina 屏中,一个逻辑像素对应多个物理像素,如果逻辑像素不足,必然会导致显示虚化。在 Canvas 中,可以先按物理像素放大,再按逻辑像素缩小解决。
1 2 3 4 5 6 7 8
| const dpr = window.devicePixelRatio const retinaWidth = canvas.width * dpr const retinaHeight = canvas.height * dpr canvas.width = retinaWidth canvas.height = retinaHeight canvas.style.width = `${retinaWidth / dpr}px` canvas.style.height = `${retinaHeight / dpr}px` ctx.scale(dpr, dpr)
|
Demo