-
Notifications
You must be signed in to change notification settings - Fork 1
Open
Description
以下实现均在仓库 👉 electron-screenshot,demo
图形绘制
箭头绘制
基本上就是三角函数计算,如图所示:
/** PI/6 */
const ARROW_ANGLE = Math.PI / 6;
export function drawArrow(
ctx: CanvasRenderingContext2D,
startPoint: Point,
endPoint: Point,
width: number,
fillStyle: CanvasFillStrokeStyles['fillStyle']
) {
const [x1, y1] = startPoint;
const [x2, y2] = endPoint;
const alpha = Math.atan((y1 - y2) / (x1 - x2));
/** EA点重叠时BD长度 */
const minArrowHeight = Math.abs(
(x2 - x1) / (Math.cos(alpha) * Math.cos(ARROW_ANGLE))
);
/** BD实际长度 */
const arrowHeight = Math.min(minArrowHeight, 6 + width * 2);
const d = x2 < x1 ? -1 : 1;
const [x3, y3] = [
x2 - Math.cos(alpha - ARROW_ANGLE) * arrowHeight * d,
y2 - Math.sin(alpha - ARROW_ANGLE) * arrowHeight * d,
];
const [x4, y4] = [
x2 - Math.cos(alpha + ARROW_ANGLE) * arrowHeight * d,
y2 - Math.sin(alpha + ARROW_ANGLE) * arrowHeight * d,
];
const [xa, ya] = [(x4 - x3) / 3, (y4 - y3) / 3];
const [x5, y5] = [x3 + xa, y3 + ya];
const [x6, y6] = [x4 - xa, y4 - ya];
const paths: Array<Point> = [
[x1, y1],
[x5, y5],
[x3, y3],
[x2, y2],
[x4, y4],
[x6, y6],
];
ctx.beginPath();
ctx.moveTo(x1, y1);
paths.slice(1).forEach((point) => ctx.lineTo(...point));
ctx.closePath();
ctx.fillStyle = fillStyle;
ctx.fill();
}笔刷效果
如果只是多个点以直线连接,当采集点不够密集,则会出现线条不平滑的现象,如下图所示:
用过 Photoshop 的我们都知道贝塞尔曲线的厉害,这里我们采用 quadraticCurveTo 来绘制贝塞尔曲线以达到实现平滑线条的目的。
绘制一条二次贝塞尔曲线,需要 起始点、控制点和终点。当只有两个点时直接连接,三个点以上则每次取三个点,第二个点为 控制点,第二个点和第三个点的中点为终点绘制;然后以这一步的控制点和中点以及下一个点来重复这个步骤,直到只剩最后一点,直接连接。
export function drawCurve(
ctx: CanvasRenderingContext2D,
path: Array<Point>,
lineWidth: number,
strokeStyle: CanvasFillStrokeStyles['strokeStyle']
) {
if (path.length < 2) return;
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeStyle;
ctx.lineCap = 'round';
ctx.beginPath();
let startPoint = path[0];
ctx.moveTo(...startPoint);
for (let i = 1; i < path.length - 1; i++) {
/** controlPoint, nextPoint */
const [[cx, cy], [nx, ny]] = path.slice(i, i + 2);
/** endPoint */
const [ex, ey] = [cx + (nx - cx) / 2, cy + (ny - cy) / 2];
ctx.quadraticCurveTo(cx, cy, ex, ey);
startPoint = [ex, ey];
}
ctx.lineTo(...path.slice(-1)[0]);
ctx.stroke();
ctx.closePath();
}绘制椭圆
这儿我们绘制一个矩形的内切椭圆,矩形由两对角的点(startPoint/endPoint)控制。由于 canvas 只有绘制正圆的 arc() 方法,所以得先对 canvas 进行缩放。
export function drawEllipse(
ctx: CanvasRenderingContext2D,
startPoint: Point,
endPoint: Point,
lineWidth: number,
strokeStyle: CanvasFillStrokeStyles['strokeStyle']
) {
const [[x1, y1], [x2, y2]] = [startPoint, endPoint];
const [r1, r2] = [x1 - x2, y1 - y2].map((n) => Math.abs(n / 2));
const [x0, y0] = [(x1 + x2) / 2, (y1 + y2) / 2];
const r = Math.max(r1, r2);
const [rx, ry] = [r1 / r, r2 / r];
ctx.save();
ctx.scale(rx, ry);
ctx.beginPath();
ctx.arc(x0 / rx, y0 / ry, r, 0, 2 * Math.PI);
ctx.closePath();
ctx.restore();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeStyle;
ctx.stroke();
}矩形和直线的绘制比较简单,这里就不做赘述了。马赛克功能的实现就 To Be Continued...
参考资料:
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels


