Files
doodle-mini/miniprogram/focusPages/shared/shapes/shapeSymbolShapes.ts
T
2025-12-17 15:30:14 +08:00

339 lines
10 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.
/**
* 图形符号配对专用的图形定义
* 所有图形保持大小一致
*/
export interface ShapeSymbolShape {
id: string;
name: string;
}
export const SHAPE_SYMBOL_SHAPES: ShapeSymbolShape[] = [
{ id: 'circle', name: '圆形' },
{ id: 'square', name: '正方形' },
{ id: 'triangle', name: '三角形' },
{ id: 'diamond', name: '菱形' },
{ id: 'trapezoid', name: '梯形' },
{ id: 'ellipse', name: '椭圆' },
{ id: 'star', name: '五角星' },
{ id: 'pentagon', name: '五边形' },
{ id: 'hexagon', name: '六边形' },
{ id: 'octagon', name: '八边形' },
{ id: 'cross', name: '十字形' },
{ id: 'rectangle', name: '长方形' },
{ id: 'sector', name: '扇形' },
{ id: 'heart', name: '心形' },
];
/**
* 绘制图形选项
*/
export interface DrawShapeOptions {
/** 填充颜色,如果为 null 则不填充 */
fillColor?: string | null;
/** 边框颜色,如果为 null 则不绘制边框 */
strokeColor?: string | null;
/** 边框宽度,默认为 1.5 */
strokeWidth?: number;
}
/**
* 绘制图形(支持边框和填充控制)
* 所有图形保持相同的大小
* @overload 旧的方式:drawShapeSymbolShape(ctx, shapeId, x, y, size, fillColor)
*/
export function drawShapeSymbolShape(
ctx: RenderingContext,
shapeId: string,
x: number,
y: number,
size: number,
fillColor: string,
options?: DrawShapeOptions,
): void;
/**
* 绘制图形(支持边框和填充控制)
* 所有图形保持相同的大小
* @overload 新的方式:drawShapeSymbolShape(ctx, shapeId, x, y, size, options)
*/
export function drawShapeSymbolShape(
ctx: RenderingContext,
shapeId: string,
x: number,
y: number,
size: number,
options: DrawShapeOptions,
): void;
/**
* 绘制图形(支持边框和填充控制)
* 所有图形保持相同的大小
*/
export function drawShapeSymbolShape(
ctx: RenderingContext,
shapeId: string,
x: number,
y: number,
size: number,
fillColorOrOptions: string | DrawShapeOptions,
options?: DrawShapeOptions,
): void {
ctx.save();
ctx.translate(x, y);
const radius = size / 2;
// 兼容旧的调用方式:drawShapeSymbolShape(ctx, shapeId, x, y, size, fillColor)
let fillColor: string | null = null;
let drawOptions: DrawShapeOptions = {};
if (typeof fillColorOrOptions === 'string') {
// 旧的方式:fillColor 是字符串
fillColor = fillColorOrOptions;
drawOptions = options || {};
} else {
// 新的方式:第一个参数是 options
drawOptions = fillColorOrOptions || {};
fillColor = drawOptions.fillColor || null;
}
// 设置填充和描边样式
if (fillColor) {
ctx.fillStyle = fillColor;
}
if (drawOptions.strokeColor) {
ctx.strokeStyle = drawOptions.strokeColor;
ctx.lineWidth = drawOptions.strokeWidth || 1.5;
}
// 统一的填充和描边处理函数
const drawPath = () => {
if (fillColor) {
ctx.fill();
}
if (drawOptions.strokeColor) {
ctx.stroke();
}
};
switch (shapeId) {
case 'circle':
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.PI * 2);
drawPath();
break;
case 'square':
// 正方形缩小为原来的0.9倍
const squareSize = size * 0.9;
const squareRadius = squareSize / 2;
if (fillColor) {
ctx.fillRect(
-squareRadius,
-squareRadius,
squareSize,
squareSize,
);
}
if (drawOptions.strokeColor) {
ctx.strokeRect(
-squareRadius,
-squareRadius,
squareSize,
squareSize,
);
}
break;
case 'triangle':
// 三角形缩小为原来的0.9倍
const triangleRadius = radius * 0.9;
ctx.beginPath();
ctx.moveTo(0, -triangleRadius);
ctx.lineTo(-triangleRadius, triangleRadius);
ctx.lineTo(triangleRadius, triangleRadius);
ctx.closePath();
drawPath();
break;
case 'diamond':
ctx.beginPath();
ctx.moveTo(0, -radius);
ctx.lineTo(radius, 0);
ctx.lineTo(0, radius);
ctx.lineTo(-radius, 0);
ctx.closePath();
drawPath();
break;
case 'trapezoid':
ctx.beginPath();
const topWidth = radius * 0.6;
ctx.moveTo(-topWidth, -radius * 0.7);
ctx.lineTo(topWidth, -radius * 0.7);
ctx.lineTo(radius, radius * 0.7);
ctx.lineTo(-radius, radius * 0.7);
ctx.closePath();
drawPath();
break;
case 'ellipse':
ctx.beginPath();
ctx.ellipse(0, 0, radius * 0.8, radius * 0.6, 0, 0, Math.PI * 2);
drawPath();
break;
case 'star':
// 五角星
ctx.beginPath();
for (let i = 0; i < 5; i++) {
const angle1 = (i * 2 * Math.PI) / 5 - Math.PI / 2;
const angle2 = ((i + 0.5) * 2 * Math.PI) / 5 - Math.PI / 2;
const r1 = radius;
const r2 = radius * 0.4;
const x1 = Math.cos(angle1) * r1;
const y1 = Math.sin(angle1) * r1;
const x2 = Math.cos(angle2) * r2;
const y2 = Math.sin(angle2) * r2;
if (i === 0) {
ctx.moveTo(x1, y1);
} else {
ctx.lineTo(x1, y1);
}
ctx.lineTo(x2, y2);
}
ctx.closePath();
drawPath();
break;
case 'pentagon':
ctx.beginPath();
for (let i = 0; i < 5; i++) {
const angle = (i * 2 * Math.PI) / 5 - Math.PI / 2;
const px = Math.cos(angle) * radius;
const py = Math.sin(angle) * radius;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
drawPath();
break;
case 'hexagon':
// 六边形
ctx.beginPath();
for (let i = 0; i < 6; i++) {
const angle = (i * 2 * Math.PI) / 6 - Math.PI / 2;
const px = Math.cos(angle) * radius;
const py = Math.sin(angle) * radius;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
drawPath();
break;
case 'octagon':
// 八边形
ctx.beginPath();
for (let i = 0; i < 8; i++) {
const angle = (i * 2 * Math.PI) / 8 - Math.PI / 2;
const px = Math.cos(angle) * radius;
const py = Math.sin(angle) * radius;
if (i === 0) ctx.moveTo(px, py);
else ctx.lineTo(px, py);
}
ctx.closePath();
drawPath();
break;
case 'cross':
// 十字形
const crossWidth = radius * 0.3; // 十字的宽度
if (fillColor) {
// 竖条
ctx.fillRect(-crossWidth, -radius, crossWidth * 2, size);
// 横条
ctx.fillRect(-radius, -crossWidth, size, crossWidth * 2);
}
if (drawOptions.strokeColor) {
// 竖条
ctx.strokeRect(-crossWidth, -radius, crossWidth * 2, size);
// 横条
ctx.strokeRect(-radius, -crossWidth, size, crossWidth * 2);
}
break;
case 'rectangle':
// 长方形(横向,宽度是高度的1.2倍)
const rectWidth = radius * 1.1;
const rectHeight = radius * 0.7;
if (fillColor) {
ctx.fillRect(
-rectWidth,
-rectHeight,
rectWidth * 2,
rectHeight * 2,
);
}
if (drawOptions.strokeColor) {
ctx.strokeRect(
-rectWidth,
-rectHeight,
rectWidth * 2,
rectHeight * 2,
);
}
break;
// 扇形
case 'sector':
// 扇形(90度扇形),圆心在格子中心点偏左二分之一的位置
const sectorCenterX = -radius / 2; // 向左偏移半径的一半
const sectorCenterY = 0;
ctx.beginPath();
ctx.moveTo(sectorCenterX, sectorCenterY);
ctx.arc(
sectorCenterX,
sectorCenterY,
radius,
-Math.PI / 4,
Math.PI / 4,
false,
);
ctx.lineTo(sectorCenterX, sectorCenterY);
ctx.closePath();
drawPath();
break;
case 'heart':
// 爱心路径(参考 shapes.ts 中的 SVG 路径)
// SVG路径:M50,35 C35,20 20,35 25,50 C30,65 50,80 50,80 C50,80 70,65 75,50 C80,35 65,20 50,35Z
// SVG中心为(50,50),高度约为60(从y=20到y=80
// 使用scale=60使心形高度约为size,与其他图形保持一致
const scale = 60;
function tx(px: number) {
return ((px - 50) / scale) * size;
}
function ty(py: number) {
return ((py - 50) / scale) * size;
}
ctx.beginPath();
ctx.moveTo(tx(50), ty(35));
ctx.bezierCurveTo(tx(35), ty(20), tx(20), ty(35), tx(25), ty(50));
ctx.bezierCurveTo(tx(30), ty(65), tx(50), ty(80), tx(50), ty(80));
ctx.bezierCurveTo(tx(50), ty(80), tx(70), ty(65), tx(75), ty(50));
ctx.bezierCurveTo(tx(80), ty(35), tx(65), ty(20), tx(50), ty(35));
ctx.closePath();
drawPath();
break;
default:
// 默认绘制圆形
ctx.beginPath();
ctx.arc(0, 0, radius, 0, Math.PI * 2);
drawPath();
}
ctx.restore();
}