初始提交:三玩法 Web 原型与设计文档
- 玩法一 数字一笔:规则库、求解器、26 关、竖屏触摸 Web 原型、测试 - 玩法二 连连气球:连消畅玩版核心、Web 原型、Cocos 球槽组件与导出脚本 - 玩法三 六边一笔:规则库、求解器、10 关 + 转墙实验包、Web 原型、测试 - 产品文档、UI 设计规范、各玩法 GDD / HANDOFF - 文档移除全部外部参考作引用;删除玩法二 v0 旧稿
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
// Painter 的 Canvas 2D 实现。Cocos 版本换成 Graphics + Label 即可,BoardView 不用改。
|
||||
import type { Rect } from '../src/layout';
|
||||
import type { Painter } from '../src/painter';
|
||||
|
||||
/**
|
||||
* 盘面字体栈,与 web/style.css 的 --font 完全一致(tests/theme.test.ts 校验)。
|
||||
* ui-rounded 让 iOS / macOS 直接拿到系统圆体;其余按平台回退到 PingFang / Noto。
|
||||
*/
|
||||
export const BOARD_FONT_FAMILY =
|
||||
'ui-rounded, "SF Pro Rounded", "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", system-ui, -apple-system, "Segoe UI", sans-serif';
|
||||
|
||||
export class CanvasPainter implements Painter {
|
||||
private readonly ctx: CanvasRenderingContext2D;
|
||||
|
||||
constructor(ctx: CanvasRenderingContext2D) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
private path(r: Rect, radius: number): void {
|
||||
const ctx = this.ctx;
|
||||
const rad = Math.max(0, Math.min(radius, r.w / 2, r.h / 2));
|
||||
ctx.beginPath();
|
||||
if (typeof ctx.roundRect === 'function') {
|
||||
ctx.roundRect(r.x, r.y, r.w, r.h, rad);
|
||||
} else {
|
||||
// 老 WebView 兜底
|
||||
const { x, y, w, h } = r;
|
||||
ctx.moveTo(x + rad, y);
|
||||
ctx.arcTo(x + w, y, x + w, y + h, rad);
|
||||
ctx.arcTo(x + w, y + h, x, y + h, rad);
|
||||
ctx.arcTo(x, y + h, x, y, rad);
|
||||
ctx.arcTo(x, y, x + w, y, rad);
|
||||
ctx.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
fillRoundRect(r: Rect, radius: number, color: string, alpha = 1): void {
|
||||
if (alpha <= 0 || r.w <= 0 || r.h <= 0) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.globalAlpha = Math.min(1, alpha);
|
||||
ctx.fillStyle = color;
|
||||
this.path(r, radius);
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
strokeRoundRect(r: Rect, radius: number, color: string, lineWidth: number, alpha = 1): void {
|
||||
if (alpha <= 0) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.globalAlpha = Math.min(1, alpha);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = lineWidth;
|
||||
this.path(r, radius);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
fillCircle(cx: number, cy: number, radius: number, color: string, alpha = 1): void {
|
||||
if (alpha <= 0) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.globalAlpha = Math.min(1, alpha);
|
||||
ctx.fillStyle = color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
/** 数字 800、汉字 700(docs/design.md 盘面字阶);不加粗时 500 */
|
||||
text(s: string, cx: number, cy: number, size: number, color: string, bold = false, alpha = 1): void {
|
||||
if (alpha <= 0) return;
|
||||
const ctx = this.ctx;
|
||||
ctx.globalAlpha = Math.min(1, alpha);
|
||||
ctx.fillStyle = color;
|
||||
ctx.font = `${bold ? (/^\d+$/.test(s) ? '800' : '700') : '500'} ${size}px ${BOARD_FONT_FAMILY}`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(s, cx, cy + size * 0.04);
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user