Files
doodle-mini/miniprogram/chinesePages/shared/drawUtils.ts
T

138 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { GRID_COLORS } from '../../core/data/tracingStyles';
/**
* cnchar-data 1024×1024 坐标系
*/
export const CHAR_BOUNDS = {
minX: 0,
minY: -124,
maxX: 1024,
maxY: 900,
};
export const CHAR_WIDTH = CHAR_BOUNDS.maxX - CHAR_BOUNDS.minX;
export const CHAR_HEIGHT = CHAR_BOUNDS.maxY - CHAR_BOUNDS.minY;
export interface ScalingTransform {
xOffset: number;
yOffset: number;
scale: number;
}
export function getScalingTransform(
width: number,
height: number,
padding: number,
): ScalingTransform {
const availableWidth = width - 2 * padding;
const availableHeight = height - 2 * padding;
const scaleX = availableWidth / CHAR_WIDTH;
const scaleY = availableHeight / CHAR_HEIGHT;
const scale = Math.min(scaleX, scaleY);
const scaledWidth = CHAR_WIDTH * scale;
const scaledHeight = CHAR_HEIGHT * scale;
const centerX = padding + (availableWidth - scaledWidth) / 2;
const centerY = padding + (availableHeight - scaledHeight) / 2;
const xOffset = -CHAR_BOUNDS.minX * scale + centerX;
const yOffset = -CHAR_BOUNDS.minY * scale + centerY;
return { xOffset, yOffset, scale };
}
/**
* SVG 路径解析与绘制(支持 M/L/Q/Z 命令)
*/
export function drawSvgPath(ctx: RenderingContext, pathD: string) {
const commandRegex = /([MLQZ])([^MLQZ]*?)(?=[MLQZ]|$)/gi;
const commands: Array<{ cmd: string; coords: string }> = [];
let match;
while ((match = commandRegex.exec(pathD)) !== null) {
commands.push({ cmd: match[1].toUpperCase(), coords: match[2].trim() });
}
const parseCoords = (coords: string): number[] =>
coords
.split(/[\s,]+/)
.filter((part) => part.trim() !== '')
.map((part) => parseFloat(part));
for (const { cmd, coords } of commands) {
const v = parseCoords(coords);
switch (cmd) {
case 'M':
if (v.length >= 2) ctx.moveTo(v[0], v[1]);
break;
case 'L':
if (v.length >= 2) ctx.lineTo(v[0], v[1]);
break;
case 'Q':
if (v.length >= 4) ctx.quadraticCurveTo(v[0], v[1], v[2], v[3]);
break;
case 'Z':
ctx.closePath();
break;
}
}
}
const GRID_CROSS_DASH = [4, 3];
export interface DrawTianZiGridParams {
ctx: RenderingContext;
/** 田字格中心 x */
cx: number;
/** 田字格中心 y */
cy: number;
/** 田字格边长 */
size: number;
borderColor?: string;
middleLineColor?: string;
diagonalColor?: string;
/** 是否绘制对角线(X线),默认不绘制 */
showDiagonal?: boolean;
}
export function drawTianZiGrid({
ctx,
cx,
cy,
size,
borderColor = GRID_COLORS.border,
middleLineColor = GRID_COLORS.middleLine,
diagonalColor = GRID_COLORS.diagonal,
showDiagonal = false,
}: DrawTianZiGridParams) {
const half = size / 2;
// 外边框 — 实线
ctx.strokeStyle = borderColor;
ctx.lineWidth = 1;
ctx.setLineDash([]);
ctx.strokeRect(cx - half, cy - half, size, size);
// 十字线 — 虚线
ctx.strokeStyle = middleLineColor;
ctx.setLineDash(GRID_CROSS_DASH);
ctx.beginPath();
ctx.moveTo(cx, cy - half);
ctx.lineTo(cx, cy + half);
ctx.moveTo(cx - half, cy);
ctx.lineTo(cx + half, cy);
ctx.stroke();
// 对角线 — 虚线
if (showDiagonal) {
ctx.strokeStyle = diagonalColor;
ctx.setLineDash(GRID_CROSS_DASH);
ctx.beginPath();
ctx.moveTo(cx - half, cy - half);
ctx.lineTo(cx + half, cy + half);
ctx.moveTo(cx + half, cy - half);
ctx.lineTo(cx - half, cy + half);
ctx.stroke();
}
ctx.setLineDash([]);
}