feat:2.6.2 增加数字点连线、数物填写等涂色卡
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
"lineRecognition/lineRecognition",
|
||||
"gridReasoning/gridReasoning",
|
||||
"codeConnect/codeConnect",
|
||||
"dotConnect/dotConnect",
|
||||
"colorShapeMatch/colorShapeMatch"
|
||||
],
|
||||
"independent": false
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -87,10 +87,19 @@ export const FOCUS_FUNCTION_TYPES: FocusFunctionType[] = [
|
||||
id: 'code-connect',
|
||||
page: 'codeConnect',
|
||||
title: '译码连线',
|
||||
desc: '按照数字顺序,将数字对应的颜色连起来',
|
||||
desc: '按照数字顺序,将数字对应的颜色连线',
|
||||
icon: '🔢',
|
||||
img: '/assets/focusEntrance/code-connect.png',
|
||||
},
|
||||
// 数字点连线
|
||||
{
|
||||
id: 'dot-connect',
|
||||
page: 'dotConnect',
|
||||
title: '数字点连线',
|
||||
desc: '按数字顺序连点',
|
||||
icon: '🔗',
|
||||
img: '/assets/focusEntrance/dot-connect.png',
|
||||
},
|
||||
// 格子仿画
|
||||
{
|
||||
id: 'grid-drawing-3x3',
|
||||
|
||||
@@ -46,11 +46,19 @@ export const MATH_FUNCTION_TYPES: MathFunctionType[] = [
|
||||
{
|
||||
id: 'number-object-match',
|
||||
page: 'numberObjectMatch',
|
||||
title: '数物对应',
|
||||
title: '数物连线',
|
||||
desc: '连线相同数量的物品和数字',
|
||||
icon: '🔗',
|
||||
img: '/assets/mathEntrance/number-object-match.png',
|
||||
},
|
||||
{
|
||||
id: 'number-object-fill',
|
||||
page: 'numberObjectMatch',
|
||||
title: '数物填写',
|
||||
desc: '数一数物品数量,填写对应的数字',
|
||||
icon: '✏️',
|
||||
img: '/assets/mathEntrance/number-object-fill.png',
|
||||
},
|
||||
{
|
||||
id: 'counting-select',
|
||||
page: 'countingSelect',
|
||||
|
||||
@@ -88,7 +88,7 @@ createFocusPage({
|
||||
const selectedShape = selectedShapes[groupIndex];
|
||||
|
||||
// 每一行独立选择2种或3种颜色
|
||||
const colorCount = Math.random() < 0.5 ? 2 : 3; // 随机选择2种或3种
|
||||
const colorCount = 2;
|
||||
const shuffledColors = [...allColors].sort(
|
||||
() => Math.random() - 0.5,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"navigationBarTitleText": "数字点连线",
|
||||
"navigationBarBackgroundColor": "#FFD719",
|
||||
"homeButton": true,
|
||||
"backgroundColor": "#F6F6F6",
|
||||
"enablePullDownRefresh": false,
|
||||
"usingComponents": {
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
|
||||
"draw-ad": "../../components/draw-ad/draw-ad"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
import DotConnectDraw from '../shared/service/dotConnectDraw';
|
||||
import { createFocusPage } from '../shared/common/focusPageMixin';
|
||||
import { DotConnectData } from '../shared/service/dotConnectDraw';
|
||||
import { getRandomUniqueNumberColors } from '../../constants/colors';
|
||||
|
||||
createFocusPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as DotConnectDraw | null,
|
||||
dotConnectData: null as DotConnectData | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '数字点连线',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'dot-connect';
|
||||
this.setData({
|
||||
functionId,
|
||||
});
|
||||
this.initPageInfo(functionId, '数字点连线');
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new DotConnectDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {
|
||||
title: this.data.pageTitle,
|
||||
functionId: this.data.functionId,
|
||||
},
|
||||
onCanvasReady: () => {
|
||||
// 初始随机生成
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.dotConnectData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(this.dotConnectData);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成不走回头路的数字序列
|
||||
* @param length 序列长度
|
||||
* @returns 数字序列数组
|
||||
*/
|
||||
generateNonBacktrackingSequence(length: number): number[] {
|
||||
// 3x3 网格中数字的位置映射
|
||||
// 1: (0,0), 2: (1,0), 3: (2,0)
|
||||
// 4: (0,1), 5: (1,1), 6: (2,1)
|
||||
// 7: (0,2), 8: (1,2), 9: (2,2)
|
||||
const getPosition = (num: number): [number, number] => {
|
||||
const row = Math.floor((num - 1) / 3);
|
||||
const col = (num - 1) % 3;
|
||||
return [row, col];
|
||||
};
|
||||
|
||||
// 检查从位置A到位置B再到位置C是否走回头路
|
||||
const isBacktrack = (
|
||||
posA: [number, number],
|
||||
posB: [number, number],
|
||||
posC: [number, number],
|
||||
): boolean => {
|
||||
// 如果C等于A,说明走回头路了
|
||||
return posC[0] === posA[0] && posC[1] === posA[1];
|
||||
};
|
||||
|
||||
const sequence: number[] = [];
|
||||
const used = new Set<number>();
|
||||
|
||||
// 随机选择起始数字
|
||||
const startNum = Math.floor(Math.random() * 9) + 1;
|
||||
sequence.push(startNum);
|
||||
used.add(startNum);
|
||||
|
||||
let prevPos = getPosition(startNum);
|
||||
let prevPrevPos: [number, number] | null = null;
|
||||
|
||||
// 生成剩余的数字
|
||||
while (sequence.length < length) {
|
||||
const candidates: number[] = [];
|
||||
for (let num = 1; num <= 9; num++) {
|
||||
if (used.has(num)) continue;
|
||||
|
||||
const currentPos = getPosition(num);
|
||||
// 如果有前前一个位置,检查是否走回头路
|
||||
if (prevPrevPos) {
|
||||
if (isBacktrack(prevPrevPos, prevPos, currentPos)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
candidates.push(num);
|
||||
}
|
||||
|
||||
if (candidates.length === 0) {
|
||||
// 如果没有候选数字,随机选择一个未使用的数字
|
||||
const remaining = Array.from(
|
||||
{ length: 9 },
|
||||
(_, i) => i + 1,
|
||||
).filter((n) => !used.has(n));
|
||||
if (remaining.length === 0) break;
|
||||
const nextNum =
|
||||
remaining[Math.floor(Math.random() * remaining.length)];
|
||||
sequence.push(nextNum);
|
||||
used.add(nextNum);
|
||||
prevPrevPos = prevPos;
|
||||
prevPos = getPosition(nextNum);
|
||||
} else {
|
||||
// 随机选择一个候选数字
|
||||
const nextNum =
|
||||
candidates[Math.floor(Math.random() * candidates.length)];
|
||||
sequence.push(nextNum);
|
||||
used.add(nextNum);
|
||||
prevPrevPos = prevPos;
|
||||
prevPos = getPosition(nextNum);
|
||||
}
|
||||
}
|
||||
|
||||
return sequence;
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
// 生成1-9的数字颜色映射(每个数字使用随机颜色)
|
||||
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
const colors = getRandomUniqueNumberColors(9);
|
||||
const colorMap = numbers.map((number, index) => ({
|
||||
number,
|
||||
color: colors[index],
|
||||
}));
|
||||
|
||||
// 生成9个组的题目
|
||||
const groups: Array<{
|
||||
sequence: number[];
|
||||
}> = [];
|
||||
|
||||
for (let i = 0; i < 9; i++) {
|
||||
// 生成不走回头路的数字序列,长度在4-6之间
|
||||
const sequenceLength = Math.floor(Math.random() * 3) + 4; // 4, 5, 或 6
|
||||
const sequence =
|
||||
this.generateNonBacktrackingSequence(sequenceLength);
|
||||
groups.push({
|
||||
sequence,
|
||||
});
|
||||
}
|
||||
|
||||
this.dotConnectData = {
|
||||
colorMap,
|
||||
groups,
|
||||
};
|
||||
|
||||
this.drawCanvas();
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: false, adType: 'focusDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
@@ -0,0 +1 @@
|
||||
@import '../../base/baseDrawPage.wxss';
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* 数字点连线绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
|
||||
/**
|
||||
* 数字点连线数据
|
||||
*/
|
||||
export interface DotConnectData {
|
||||
/** 参考区域:数字到颜色的映射(1-9) */
|
||||
colorMap: Array<{
|
||||
number: number; // 数字(1-9)
|
||||
color: string;
|
||||
}>;
|
||||
/** 内容区域:9个组的题目 */
|
||||
groups: Array<{
|
||||
/** 数字序列 */
|
||||
sequence: number[];
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字点连线绘制服务
|
||||
*/
|
||||
class DotConnectDraw extends BaseDrawService {
|
||||
dotConnectData: DotConnectData | null = null;
|
||||
|
||||
constructor(
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) {
|
||||
super(canvas, ctx, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制数字点连线内容
|
||||
*/
|
||||
async draw(dotConnectData: DotConnectData) {
|
||||
if (
|
||||
!dotConnectData ||
|
||||
!dotConnectData.colorMap ||
|
||||
!dotConnectData.groups
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dotConnectData = dotConnectData;
|
||||
this.prepareDraw();
|
||||
await this.drawHeaderAndDivider();
|
||||
await this.drawContent({
|
||||
canvas: this.canvas,
|
||||
ctx: this.ctx,
|
||||
dotConnectData: this.dotConnectData,
|
||||
canvasWidth: this.canvasWidth,
|
||||
startY: this.currentY,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制内容区域
|
||||
*/
|
||||
private async drawContent(params: {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
dotConnectData: DotConnectData;
|
||||
canvasWidth: number;
|
||||
startY: number;
|
||||
}): Promise<void> {
|
||||
const { ctx, dotConnectData, canvasWidth } = params;
|
||||
let { startY } = params;
|
||||
|
||||
startY = startY + 25;
|
||||
|
||||
const margin = 40; // 左右边距
|
||||
|
||||
// ========== 上部参考区域:3x3 九宫格 ==========
|
||||
const referenceStartY = startY;
|
||||
const referenceCellSize = 38; // 每个方格的大小
|
||||
const referenceGridWidth = referenceCellSize * 3; // 网格总宽度(3列)
|
||||
const referenceGridHeight = referenceCellSize * 3; // 网格总高度(3行)
|
||||
|
||||
// 计算参考区域的起始X坐标(居中)
|
||||
const referenceStartX =
|
||||
margin + (canvasWidth - margin * 2 - referenceGridWidth) / 2;
|
||||
|
||||
// 使用drawGridLines绘制网格(3行3列)
|
||||
this.drawGridLines(
|
||||
referenceStartX,
|
||||
referenceStartY,
|
||||
referenceCellSize,
|
||||
referenceCellSize,
|
||||
3,
|
||||
3,
|
||||
);
|
||||
|
||||
// 绘制数字(1-9),每个数字使用对应的颜色
|
||||
for (let row = 0; row < 3; row++) {
|
||||
for (let col = 0; col < 3; col++) {
|
||||
const number = row * 3 + col + 1; // 1-9
|
||||
const cellX = referenceStartX + col * referenceCellSize;
|
||||
const cellY = referenceStartY + row * referenceCellSize;
|
||||
|
||||
// 查找数字对应的颜色
|
||||
const colorInfo = dotConnectData.colorMap.find(
|
||||
(m) => m.number === number,
|
||||
);
|
||||
const color = colorInfo?.color || '#000';
|
||||
|
||||
// 绘制数字(使用对应颜色)
|
||||
ctx.fillStyle = color;
|
||||
ctx.font = 'bold 24px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(
|
||||
number.toString(),
|
||||
cellX + referenceCellSize / 2,
|
||||
cellY + referenceCellSize / 2,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 虚线分割 ==========
|
||||
const dividerY = referenceStartY + referenceGridHeight + 20;
|
||||
this.drawDashedDivider(dividerY, margin);
|
||||
|
||||
// ========== 下部内容区域:9个组 ==========
|
||||
const contentStartY = dividerY + 20;
|
||||
const dotSpacing = 35; // 圆点间距
|
||||
const dotRadius = 6; // 圆点半径
|
||||
const boxPadding = 20; // 框内边距
|
||||
const boxWidth = dotSpacing * 2 + boxPadding * 2; // 框的宽度(3个圆点,2个间距)
|
||||
const boxHeight = boxWidth; // 框的高度(正方形)
|
||||
const textHeight = 25; // 数字文本的高度
|
||||
const textBoxSpacing = 12; // 数字文本与框的间距
|
||||
const groupSpacingX = 60; // 一行中两组之间的间距
|
||||
const groupSpacingY = 30; // 行之间的间距
|
||||
|
||||
// 计算每组的总宽度和高度
|
||||
const groupWidth = boxWidth;
|
||||
const groupHeight = textHeight + textBoxSpacing + boxHeight;
|
||||
const totalGroupsWidth = 3 * groupWidth + 2 * groupSpacingX;
|
||||
const groupsStartX =
|
||||
margin + (canvasWidth - margin * 2 - totalGroupsWidth) / 2;
|
||||
|
||||
// 绘制9个组(三行三列)
|
||||
for (let row = 0; row < 3; row++) {
|
||||
for (let col = 0; col < 3; col++) {
|
||||
const groupIndex = row * 3 + col;
|
||||
const group = dotConnectData.groups[groupIndex];
|
||||
if (!group) continue;
|
||||
|
||||
const groupX =
|
||||
groupsStartX + col * (groupWidth + groupSpacingX);
|
||||
const groupY =
|
||||
contentStartY + row * (groupHeight + groupSpacingY);
|
||||
|
||||
// 绘制数字序列(在框正上方,居中)
|
||||
const sequenceText = group.sequence.join('-');
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.font = '18px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'bottom';
|
||||
const textX = groupX + groupWidth / 2;
|
||||
const textY = groupY + textHeight;
|
||||
ctx.fillText(sequenceText, textX, textY);
|
||||
|
||||
// 绘制虚线圆角框
|
||||
const boxX = groupX;
|
||||
const boxY = groupY + textHeight + textBoxSpacing;
|
||||
this.drawRoundedRect(boxX, boxY, boxWidth, boxHeight, {
|
||||
isDashed: true,
|
||||
color: '#999',
|
||||
lineWidth: 1,
|
||||
radius: 10,
|
||||
});
|
||||
|
||||
// 计算框内圆点的起始位置(居中)
|
||||
const dotsStartX = boxX + boxPadding;
|
||||
const dotsStartY = boxY + boxPadding;
|
||||
|
||||
// 绘制3x3圆点网格
|
||||
for (let dotRow = 0; dotRow < 3; dotRow++) {
|
||||
for (let dotCol = 0; dotCol < 3; dotCol++) {
|
||||
const dotNumber = dotRow * 3 + dotCol + 1; // 1-9
|
||||
const dotX = dotsStartX + dotCol * dotSpacing;
|
||||
const dotY = dotsStartY + dotRow * dotSpacing;
|
||||
|
||||
// 查找数字对应的颜色
|
||||
const colorInfo = dotConnectData.colorMap.find(
|
||||
(m) => m.number === dotNumber,
|
||||
);
|
||||
const dotColor = colorInfo?.color || '#000';
|
||||
|
||||
// 绘制圆点(黑色边框)
|
||||
this.drawDot(
|
||||
ctx,
|
||||
dotX,
|
||||
dotY,
|
||||
dotRadius,
|
||||
dotColor,
|
||||
'#000',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DotConnectDraw;
|
||||
@@ -8,6 +8,7 @@
|
||||
"toy-button": "../../ui/button/button",
|
||||
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
|
||||
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
|
||||
"draw-ad": "../../components/draw-ad/draw-ad"
|
||||
"draw-ad": "../../components/draw-ad/draw-ad",
|
||||
"math-type-selector": "../../components/math-type-selector/math-type-selector"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,33 @@ createMathPage({
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
} as CanvasDataState,
|
||||
showTypeSelector: true, // 控制是否显示类型选择器
|
||||
currentMode: 'match', // 'match' 连线 或 'fill' 填写
|
||||
currentModeName: '连线',
|
||||
typeActions: [
|
||||
{ name: '连线', value: 'match' },
|
||||
{ name: '填写', value: 'fill' },
|
||||
],
|
||||
} as CanvasDataState & {
|
||||
showTypeSelector: boolean;
|
||||
currentMode: string;
|
||||
currentModeName: string;
|
||||
typeActions: Array<{ name: string; value: string }>;
|
||||
},
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
onLoad(options: { id?: string; mode?: string }) {
|
||||
const functionId = options.id || 'number-object-match';
|
||||
this.initPageInfo(functionId, '数物对应');
|
||||
const isFill = functionId === 'number-object-fill';
|
||||
const currentMode = options.mode || (isFill ? 'fill' : 'match');
|
||||
this.setData({
|
||||
currentMode,
|
||||
currentModeName: currentMode === 'fill' ? '填写' : '连线',
|
||||
typeActions: [
|
||||
{ name: '连线', value: 'match' },
|
||||
{ name: '填写', value: 'fill' },
|
||||
],
|
||||
});
|
||||
},
|
||||
|
||||
onReady() {
|
||||
@@ -51,7 +73,7 @@ createMathPage({
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(this.matchData);
|
||||
await this.drawService.draw(this.matchData, this.data.currentMode);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
@@ -142,4 +164,15 @@ createMathPage({
|
||||
|
||||
this.drawCanvas();
|
||||
},
|
||||
|
||||
/** 选择类型 */
|
||||
onSelectType(event: any) {
|
||||
const { name, value } = event.detail;
|
||||
this.setData({
|
||||
currentMode: value,
|
||||
currentModeName: name,
|
||||
});
|
||||
// 重新生成数据
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: false, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: true, currentModeName: currentModeName, typeActions: typeActions, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -43,13 +43,13 @@ class NumberObjectMatchDraw extends BaseDrawService {
|
||||
/**
|
||||
* 绘制数物对应内容
|
||||
*/
|
||||
async draw(matchData: NumberObjectMatchData) {
|
||||
if (
|
||||
!matchData ||
|
||||
!matchData.gridImages ||
|
||||
!matchData.bottomImages ||
|
||||
!matchData.bottomNumbers
|
||||
) {
|
||||
async draw(matchData: NumberObjectMatchData, mode: string = 'match') {
|
||||
if (!matchData || !matchData.gridImages || !matchData.bottomImages) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 连线模式需要 bottomNumbers,填写模式不需要
|
||||
if (mode === 'match' && !matchData.bottomNumbers) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ class NumberObjectMatchDraw extends BaseDrawService {
|
||||
matchData: this.matchData,
|
||||
canvasWidth: this.canvasWidth,
|
||||
startY: this.currentY,
|
||||
mode: mode,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,8 +75,9 @@ class NumberObjectMatchDraw extends BaseDrawService {
|
||||
matchData: NumberObjectMatchData;
|
||||
canvasWidth: number;
|
||||
startY: number;
|
||||
mode?: string; // 'match' 连线 或 'fill' 填写
|
||||
}): Promise<void> {
|
||||
const { canvas, ctx, matchData, canvasWidth } = params;
|
||||
const { canvas, ctx, matchData, canvasWidth, mode = 'match' } = params;
|
||||
let { startY } = params;
|
||||
|
||||
startY = startY + 25;
|
||||
@@ -140,8 +142,127 @@ class NumberObjectMatchDraw extends BaseDrawService {
|
||||
this.drawDashedDivider(dividerY, margin);
|
||||
|
||||
// ========== 绘制下面区域 ==========
|
||||
if (mode === 'fill') {
|
||||
// 填写模式:两行四列网格
|
||||
await this.drawFillMode({
|
||||
canvas,
|
||||
ctx,
|
||||
matchData,
|
||||
canvasWidth,
|
||||
dividerY,
|
||||
margin,
|
||||
});
|
||||
} else {
|
||||
// 连线模式:保持原有逻辑
|
||||
await this.drawMatchMode({
|
||||
canvas,
|
||||
ctx,
|
||||
matchData,
|
||||
canvasWidth,
|
||||
dividerY,
|
||||
margin,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制填写模式
|
||||
*/
|
||||
private async drawFillMode(params: {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
matchData: NumberObjectMatchData;
|
||||
canvasWidth: number;
|
||||
dividerY: number;
|
||||
margin: number;
|
||||
}): Promise<void> {
|
||||
const { canvas, ctx, matchData, canvasWidth, dividerY, margin } =
|
||||
params;
|
||||
|
||||
const bottomStartY = dividerY + 30;
|
||||
const bottomImageSize = 75; // 下面图片大小
|
||||
const imagePadding = 10; // 图片内边距
|
||||
const bottomBoxSize = bottomImageSize + imagePadding * 2; // 图片框大小
|
||||
const gridRows = 2; // 两行
|
||||
const gridCols = 4; // 四列
|
||||
const cellSize = bottomBoxSize; // 格子大小
|
||||
|
||||
// 计算网格总宽度
|
||||
const gridWidth = cellSize * gridCols;
|
||||
const gridStartX = margin + (canvasWidth - margin * 2 - gridWidth) / 2; // 居中
|
||||
|
||||
// 绘制网格线
|
||||
this.drawGridLines(
|
||||
gridStartX,
|
||||
bottomStartY,
|
||||
cellSize,
|
||||
cellSize,
|
||||
gridCols,
|
||||
gridRows,
|
||||
);
|
||||
|
||||
// 第一行:绘制图片
|
||||
for (let i = 0; i < matchData.bottomImages.length; i++) {
|
||||
const item = matchData.bottomImages[i];
|
||||
const col = i;
|
||||
const boxX = gridStartX + col * cellSize;
|
||||
const boxY = bottomStartY;
|
||||
const imageX = boxX + imagePadding;
|
||||
const imageY = boxY + imagePadding;
|
||||
|
||||
// 加载并绘制图片
|
||||
let image: any = null;
|
||||
try {
|
||||
const imagePath = `/mathPages/shared/assets/${item.folder}/${item.imageIndex}.png`;
|
||||
image = await getImage(canvas, imagePath);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`加载图片失败: ${item.folder}/${item.imageIndex}`,
|
||||
error,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (image) {
|
||||
// 计算图片高度(等比例缩放)
|
||||
// @ts-ignore - 微信小程序图片对象有 width 和 height 属性
|
||||
const scaledHeight =
|
||||
(image.height / image.width) * bottomImageSize;
|
||||
|
||||
// 绘制图片(居中)
|
||||
const drawImageX =
|
||||
imageX + (bottomImageSize - bottomImageSize) / 2;
|
||||
const drawImageY =
|
||||
imageY + (bottomImageSize - scaledHeight) / 2;
|
||||
ctx.drawImage(
|
||||
image,
|
||||
drawImageX,
|
||||
drawImageY,
|
||||
bottomImageSize,
|
||||
scaledHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 第二行:空着,让小朋友填写数字(只绘制网格线,不绘制内容)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制连线模式
|
||||
*/
|
||||
private async drawMatchMode(params: {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
matchData: NumberObjectMatchData;
|
||||
canvasWidth: number;
|
||||
dividerY: number;
|
||||
margin: number;
|
||||
}): Promise<void> {
|
||||
const { canvas, ctx, matchData, canvasWidth, dividerY, margin } =
|
||||
params;
|
||||
const bottomStartY = dividerY + 30;
|
||||
const bottomImageSize = 50; // 下面图片大小
|
||||
const imagePadding = 10; // 图片内边距
|
||||
const bottomBoxSize = bottomImageSize + imagePadding * 2; // 图片框大小
|
||||
const bottomSpacing = 40; // 图片间距
|
||||
const bottomRowSpacing = 80; // 两行间距
|
||||
|
||||
Reference in New Issue
Block a user