feat:优化练字贴
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon-success:before {
|
.toy-icon-success:before {
|
||||||
content: "\e650";
|
content: "\e650";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,13 +42,16 @@ page {
|
|||||||
.word-input-box {
|
.word-input-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: flex-start;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.operation-box {
|
.action-buttons-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,14 @@ Page({
|
|||||||
maxCol: 0 as number,
|
maxCol: 0 as number,
|
||||||
|
|
||||||
data: {
|
data: {
|
||||||
// 选中的汉字列表
|
// 最终的汉字列表(合并输入和选择的汉字)
|
||||||
words: [] as string[],
|
words: [] as string[],
|
||||||
|
// 来自文本框输入的汉字(每次输入替换)
|
||||||
|
inputWords: [] as string[],
|
||||||
|
// 来自弹窗选择的汉字(追加)
|
||||||
|
selectedWords: [] as string[],
|
||||||
|
// 输入框的值(用于清空)
|
||||||
|
inputValue: '',
|
||||||
showSelectWordPopup: false,
|
showSelectWordPopup: false,
|
||||||
showColorPopup: false,
|
showColorPopup: false,
|
||||||
currentKey: 0,
|
currentKey: 0,
|
||||||
@@ -33,9 +39,12 @@ Page({
|
|||||||
const hasShowPracticeDemo =
|
const hasShowPracticeDemo =
|
||||||
wx.getStorageSync('hasShowPracticeDemo') || false;
|
wx.getStorageSync('hasShowPracticeDemo') || false;
|
||||||
if (!hasShowPracticeDemo) {
|
if (!hasShowPracticeDemo) {
|
||||||
|
const demoWords = ['好', '好', '学', '习', '天', '天', '向', '上'];
|
||||||
this.setData(
|
this.setData(
|
||||||
{
|
{
|
||||||
words: ['好', '好', '学', '习', '天', '天', '向', '上'],
|
words: demoWords,
|
||||||
|
inputWords: demoWords,
|
||||||
|
selectedWords: [],
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
wx.setStorageSync('hasShowPracticeDemo', true);
|
wx.setStorageSync('hasShowPracticeDemo', true);
|
||||||
@@ -86,47 +95,68 @@ Page({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 输入组件回调
|
// 输入组件回调(所见即所得:替换文本框输入的汉字)
|
||||||
onConfirmInput(e: any) {
|
onConfirmInput(e: any) {
|
||||||
const value: string = (e && e.detail && e.detail.value) || '';
|
const value: string = (e && e.detail && e.detail.value) || '';
|
||||||
const text = (value || '').trim();
|
const text = (value || '').trim();
|
||||||
if (!text) return;
|
|
||||||
|
// 如果文本框为空,清空输入汉字
|
||||||
|
if (!text) {
|
||||||
|
const { selectedWords } = this.data as any;
|
||||||
|
const updatedWords = [...selectedWords];
|
||||||
|
this.setData(
|
||||||
|
{
|
||||||
|
inputWords: [],
|
||||||
|
words: updatedWords,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.renderPracticeContent().catch(console.error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.isChineseText(text)) {
|
if (!this.isChineseText(text)) {
|
||||||
wx.showToast({ title: '请输入汉字', icon: 'none' });
|
wx.showToast({ title: '请输入汉字', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { words } = this.data as any;
|
// 1. 解析输入的汉字
|
||||||
|
const newInputChars = this.splitToSingleChars(text);
|
||||||
// 1. 解析新输入的汉字
|
if (newInputChars.length === 0) {
|
||||||
const newChars = this.splitToSingleChars(text);
|
|
||||||
if (newChars.length === 0) {
|
|
||||||
wx.showToast({ title: '请输入有效汉字', icon: 'none' });
|
wx.showToast({ title: '请输入有效汉字', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查是否超过限制
|
const { selectedWords } = this.data as any;
|
||||||
if (words.length + newChars.length > 11) {
|
|
||||||
|
// 2. 检查是否超过限制(输入汉字 + 已选择的汉字)
|
||||||
|
if (newInputChars.length + selectedWords.length > 11) {
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: `最多只能添加 11 个字,当前已有 ${words.length} 个,输入了 ${newChars.length} 个`,
|
title: `最多只能添加 11 个字,当前已选择 ${selectedWords.length} 个,输入了 ${newInputChars.length} 个`,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 3000,
|
duration: 3000,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 直接追加新汉字
|
// 3. 替换输入汉字(不是追加),合并已选择的汉字
|
||||||
const updatedWords = [...words, ...newChars];
|
const updatedWords = [...newInputChars, ...selectedWords];
|
||||||
|
|
||||||
// 4. 更新数据并提示
|
// 4. 更新数据并提示
|
||||||
this.setData({ words: updatedWords }, () => {
|
this.setData(
|
||||||
this.renderPracticeContent().catch(console.error);
|
{
|
||||||
});
|
inputWords: newInputChars,
|
||||||
|
words: updatedWords,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.renderPracticeContent().catch(console.error);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// 5. 显示添加结果提示
|
// 5. 显示添加结果提示
|
||||||
wx.showToast({
|
wx.showToast({
|
||||||
title: `成功添加 ${newChars.length} 个汉字`,
|
title: `已更新输入汉字(${newInputChars.length} 个)`,
|
||||||
icon: 'none',
|
icon: 'none',
|
||||||
duration: 2000,
|
duration: 2000,
|
||||||
});
|
});
|
||||||
@@ -140,30 +170,90 @@ Page({
|
|||||||
this.setData({ showSelectWordPopup: false });
|
this.setData({ showSelectWordPopup: false });
|
||||||
},
|
},
|
||||||
onChangeWord(e: any) {
|
onChangeWord(e: any) {
|
||||||
const selectedWords = e.detail.selectedWords as string[];
|
const newSelectedWords = e.detail.selectedWords as string[];
|
||||||
const newWords = [...this.data.words, ...selectedWords];
|
const { inputWords } = this.data as any;
|
||||||
this.setData({ words: newWords, showSelectWordPopup: false }, () => {
|
|
||||||
if (newWords && newWords.length > 0) {
|
// 检查是否超过限制
|
||||||
this.renderPracticeContent().catch(console.error);
|
if (inputWords.length + newSelectedWords.length > 11) {
|
||||||
}
|
wx.showToast({
|
||||||
});
|
title: `最多只能添加 11 个字,当前输入 ${inputWords.length} 个,选择了 ${newSelectedWords.length} 个`,
|
||||||
|
icon: 'none',
|
||||||
|
duration: 3000,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合并输入汉字和选择的汉字(求并集)
|
||||||
|
const updatedWords = [...inputWords, ...newSelectedWords];
|
||||||
|
this.setData(
|
||||||
|
{
|
||||||
|
selectedWords: newSelectedWords,
|
||||||
|
words: updatedWords,
|
||||||
|
showSelectWordPopup: false,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
if (updatedWords && updatedWords.length > 0) {
|
||||||
|
this.renderPracticeContent().catch(console.error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 删除
|
// 删除(需要判断删除的是输入汉字还是选择的汉字)
|
||||||
deleteWordCard(e: any) {
|
deleteWordCard(e: any) {
|
||||||
const { key } = e.detail;
|
const { key } = e.detail;
|
||||||
const { words } = this.data as any;
|
const { inputWords, selectedWords } = this.data as any;
|
||||||
const next = words.filter((_: string, index: number) => index !== key);
|
|
||||||
this.setData({ words: next }, () =>
|
// 判断删除的是输入汉字还是选择的汉字
|
||||||
this.renderPracticeContent().catch(console.error),
|
if (key < inputWords.length) {
|
||||||
);
|
// 删除的是输入汉字
|
||||||
|
const nextInputWords = inputWords.filter(
|
||||||
|
(_: string, index: number) => index !== key,
|
||||||
|
);
|
||||||
|
const nextWords = [...nextInputWords, ...selectedWords];
|
||||||
|
this.setData(
|
||||||
|
{
|
||||||
|
inputWords: nextInputWords,
|
||||||
|
words: nextWords,
|
||||||
|
},
|
||||||
|
() => this.renderPracticeContent().catch(console.error),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// 删除的是选择的汉字
|
||||||
|
const selectedIndex = key - inputWords.length;
|
||||||
|
const nextSelectedWords = selectedWords.filter(
|
||||||
|
(_: string, index: number) => index !== selectedIndex,
|
||||||
|
);
|
||||||
|
const nextWords = [...inputWords, ...nextSelectedWords];
|
||||||
|
this.setData(
|
||||||
|
{
|
||||||
|
selectedWords: nextSelectedWords,
|
||||||
|
words: nextWords,
|
||||||
|
},
|
||||||
|
() => this.renderPracticeContent().catch(console.error),
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 清空
|
// 清空(清空所有来源的汉字:文本框、预览、弹窗选择)
|
||||||
clearWords() {
|
clearWords() {
|
||||||
this.setData({ words: [] }, () =>
|
// 清空所有汉字数据
|
||||||
this.renderPracticeContent().catch(console.error),
|
this.setData(
|
||||||
|
{
|
||||||
|
words: [],
|
||||||
|
inputWords: [],
|
||||||
|
selectedWords: [],
|
||||||
|
inputValue: '', // 清空输入框
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
this.renderPracticeContent().catch(console.error);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 关闭弹窗(如果打开)
|
||||||
|
if (this.data.showSelectWordPopup) {
|
||||||
|
this.setData({ showSelectWordPopup: false });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 随机生成(取基础分类,过滤为可用SVG字,最多11个)
|
// 随机生成(取基础分类,过滤为可用SVG字,最多11个)
|
||||||
|
|||||||
@@ -2,13 +2,29 @@
|
|||||||
<view class="wrapper">
|
<view class="wrapper">
|
||||||
<text class="wrapper-title">自定义练字贴</text>
|
<text class="wrapper-title">自定义练字贴</text>
|
||||||
<view class="word-input-box">
|
<view class="word-input-box">
|
||||||
<word-input bind:onConfirm="onConfirmInput" width="410rpx" />
|
<word-input bind:onConfirm="onConfirmInput" style="width: 100%;"/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view class="action-buttons-box">
|
||||||
<toy-button
|
<toy-button
|
||||||
type="green"
|
type="green"
|
||||||
bind:click="openSelectWordPopup"
|
bind:click="openSelectWordPopup"
|
||||||
width="200rpx">
|
height="80rpx"
|
||||||
|
width="410rpx"
|
||||||
|
icon="success"
|
||||||
|
icon-class-prefix="van-icon">
|
||||||
选择文字
|
选择文字
|
||||||
</toy-button>
|
</toy-button>
|
||||||
|
<toy-button
|
||||||
|
disabled="{{words.length <= 0}}"
|
||||||
|
type="white"
|
||||||
|
bind:click="clearWords"
|
||||||
|
width="220rpx"
|
||||||
|
height="80rpx"
|
||||||
|
icon="clear"
|
||||||
|
icon-class-prefix="toy-icon">
|
||||||
|
清空
|
||||||
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="word-card-box">
|
<view class="word-card-box">
|
||||||
@@ -27,28 +43,9 @@
|
|||||||
<empty-state
|
<empty-state
|
||||||
wx:if="{{!words.length}}"
|
wx:if="{{!words.length}}"
|
||||||
title="还没有添加文字"
|
title="还没有添加文字"
|
||||||
desc="请在上方输入文字或点击“选择文字”按钮,开始创建你的练字田字格吧!"
|
desc='请在上方输入文字或点击"选择文字"按钮,开始创建你的练字田字格吧!'
|
||||||
hint="提示:最多可以同时添加 11 个文字" />
|
hint="提示:最多可以同时添加 11 个文字" />
|
||||||
</view>
|
</view>
|
||||||
<view class="operation-box">
|
|
||||||
<toy-button
|
|
||||||
type="primary"
|
|
||||||
bind:click="refreshWords"
|
|
||||||
width="410rpx"
|
|
||||||
icon="refresh"
|
|
||||||
icon-class-prefix="toy-icon">
|
|
||||||
随机生成
|
|
||||||
</toy-button>
|
|
||||||
<toy-button
|
|
||||||
disabled="{{words.length <= 0}}"
|
|
||||||
type="white"
|
|
||||||
bind:click="clearWords"
|
|
||||||
width="200rpx"
|
|
||||||
icon="clear"
|
|
||||||
icon-class-prefix="toy-icon">
|
|
||||||
清空
|
|
||||||
</toy-button>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view id="previewWrapper" class="wrapper">
|
<view id="previewWrapper" class="wrapper">
|
||||||
@@ -88,7 +85,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<word-picker
|
<word-picker
|
||||||
show="{{showSelectWordPopup}}"
|
show="{{showSelectWordPopup}}"
|
||||||
selectedWords="{{words}}"
|
selectedWords="{{selectedWords}}"
|
||||||
max="11"
|
max="11"
|
||||||
bind:onClose="closeSelectWordPopup"
|
bind:onClose="closeSelectWordPopup"
|
||||||
bind:onChange="onChangeWord" />
|
bind:onChange="onChangeWord" />
|
||||||
|
|||||||
@@ -21,13 +21,21 @@ function drawSvgPathCommands({
|
|||||||
}: DrawSvgPathCommandsParams) {
|
}: DrawSvgPathCommandsParams) {
|
||||||
// console.log('drawSvgPathCommands 参数:', { pathD, offsetX, offsetY, scale });
|
// console.log('drawSvgPathCommands 参数:', { pathD, offsetX, offsetY, scale });
|
||||||
|
|
||||||
// 精度处理函数:避免浮点数精度问题
|
// 精度处理函数:避免浮点数精度问题(提高到6位小数精度,确保高精度)
|
||||||
const roundToPrecision = (num: number, precision: number = 2): number => {
|
const roundToPrecision = (num: number, precision: number = 6): number => {
|
||||||
return (
|
return (
|
||||||
Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision)
|
Math.round(num * Math.pow(10, precision)) / Math.pow(10, precision)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 像素对齐函数:优化对齐策略,提升平滑度
|
||||||
|
// 对于Fill+Stroke模式,使用更精细的对齐策略,避免过度对齐导致的锯齿
|
||||||
|
// 对齐到0.5像素可以获得更好的平滑效果
|
||||||
|
const alignToPixel = (num: number): number => {
|
||||||
|
// 对齐到0.5像素,而不是整数像素,可以获得更平滑的线条
|
||||||
|
return Math.round(num * 2) / 2;
|
||||||
|
};
|
||||||
|
|
||||||
// 新的解析方法:按命令片段解析
|
// 新的解析方法:按命令片段解析
|
||||||
// 使用正则表达式匹配从字母开头到下一个字母(或结尾)的片段
|
// 使用正则表达式匹配从字母开头到下一个字母(或结尾)的片段
|
||||||
const commandRegex = /([MLZ])([^MLZ]*?)(?=[MLZ]|$)/g;
|
const commandRegex = /([MLZ])([^MLZ]*?)(?=[MLZ]|$)/g;
|
||||||
@@ -60,9 +68,14 @@ function drawSvgPathCommands({
|
|||||||
const y = parseFloat(coordParts[1]);
|
const y = parseFloat(coordParts[1]);
|
||||||
|
|
||||||
if (!isNaN(x) && !isNaN(y) && isFinite(x) && isFinite(y)) {
|
if (!isNaN(x) && !isNaN(y) && isFinite(x) && isFinite(y)) {
|
||||||
// 计算最终坐标并处理精度
|
// 计算最终坐标:先进行高精度计算,再对齐到整数像素
|
||||||
const finalX = roundToPrecision(offsetX + x * scale);
|
// 使用高精度计算确保路径的准确性,然后对齐到像素避免亚像素渲染
|
||||||
const finalY = roundToPrecision(offsetY + y * scale);
|
const calculatedX = offsetX + x * scale;
|
||||||
|
const calculatedY = offsetY + y * scale;
|
||||||
|
const preciseX = roundToPrecision(calculatedX);
|
||||||
|
const preciseY = roundToPrecision(calculatedY);
|
||||||
|
const finalX = alignToPixel(preciseX);
|
||||||
|
const finalY = alignToPixel(preciseY);
|
||||||
|
|
||||||
// console.log(`${cmd}: 绘制到 (${finalX}, ${finalY}) [原始: (${x}, ${y})]`);
|
// console.log(`${cmd}: 绘制到 (${finalX}, ${finalY}) [原始: (${x}, ${y})]`);
|
||||||
drawFunction(finalX, finalY);
|
drawFunction(finalX, finalY);
|
||||||
@@ -85,8 +98,8 @@ function drawSvgPathCommands({
|
|||||||
L: (coords) =>
|
L: (coords) =>
|
||||||
parseAndDrawCoords(coords, 'L', (x, y) => ctx.lineTo(x, y)),
|
parseAndDrawCoords(coords, 'L', (x, y) => ctx.lineTo(x, y)),
|
||||||
Z: () => {
|
Z: () => {
|
||||||
// console.log('Z: 闭合路径');
|
// 关键修复:处理Z命令,闭合路径
|
||||||
// 注意:不要在这里调用 closePath(),因为 drawStrokes 中会统一处理
|
ctx.closePath();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -124,21 +137,40 @@ function drawStrokes({
|
|||||||
offsetY,
|
offsetY,
|
||||||
size,
|
size,
|
||||||
uptoInclusive,
|
uptoInclusive,
|
||||||
fillStyle,
|
fillStyle, // 填充颜色,用于 Fill + Stroke 模式
|
||||||
strokeStyle,
|
strokeStyle, // 描边颜色,用于绘制笔画
|
||||||
lineWidth,
|
lineWidth, // 线条宽度,控制笔画粗细
|
||||||
}: DrawStrokesParams) {
|
}: DrawStrokesParams) {
|
||||||
|
// 设置 Canvas 绘制质量(关键优化:提升线条流畅度)
|
||||||
|
ctx.imageSmoothingEnabled = true;
|
||||||
|
ctx.imageSmoothingQuality = 'high'; // 高质量平滑
|
||||||
|
|
||||||
|
// 设置路径绘制属性(关键:这些设置直接影响笔画粗细和流畅度)
|
||||||
|
// 注意:不使用 save/restore,与测试页面保持一致,避免状态管理问题
|
||||||
|
ctx.lineCap = 'round'; // 圆角端点,使笔画末端更自然
|
||||||
|
ctx.lineJoin = 'round'; // 圆角连接,使转折处更平滑
|
||||||
|
ctx.miterLimit = 10; // 斜接限制
|
||||||
|
|
||||||
|
// 优化渲染策略:使用 Fill + Stroke 模式
|
||||||
|
// 先 fill 填充内部,再 stroke 描边,stroke 宽度略小,避免双重渲染导致的毛糙
|
||||||
|
// 这样可以获得自然的笔画粗细,同时保持流畅度
|
||||||
|
|
||||||
// 模板中使用 54x54 的视窗尺寸
|
// 模板中使用 54x54 的视窗尺寸
|
||||||
const viewBoxSize = 54;
|
const viewBoxSize = 54;
|
||||||
|
|
||||||
// 添加内边距:在田字格四周预留空间,避免笔画贴边
|
// 添加内边距:在田字格四周预留空间,避免笔画贴边
|
||||||
const padding = size * 0.1; // 内边距为田字格大小的10%
|
const padding = size * 0.1; // 内边距为田字格大小的10%
|
||||||
const contentSize = size - padding * 2; // 实际绘制区域大小
|
const contentSize = size - padding * 2; // 实际绘制区域大小
|
||||||
const contentScale = contentSize / viewBoxSize; // 调整后的缩放比例
|
|
||||||
|
// 优化缩放比例:使用更精确的缩放,不进行四舍五入,保持原始精度
|
||||||
|
const contentScale = contentSize / viewBoxSize;
|
||||||
|
|
||||||
// 计算内容区域的起始位置(居中)
|
// 计算内容区域的起始位置(居中),优化对齐策略提升平滑度
|
||||||
const contentOffsetX = offsetX + padding;
|
const rawOffsetX = offsetX + padding;
|
||||||
const contentOffsetY = offsetY + padding;
|
const rawOffsetY = offsetY + padding;
|
||||||
|
// 对齐到0.5像素,而不是整数像素,可以获得更平滑的渲染效果
|
||||||
|
const contentOffsetX = Math.round(rawOffsetX * 2) / 2;
|
||||||
|
const contentOffsetY = Math.round(rawOffsetY * 2) / 2;
|
||||||
|
|
||||||
// console.log('drawStrokes 参数:', {
|
// console.log('drawStrokes 参数:', {
|
||||||
// strokesCount: strokes.length,
|
// strokesCount: strokes.length,
|
||||||
@@ -160,6 +192,10 @@ function drawStrokes({
|
|||||||
for (let s = 0; s <= uptoInclusive && s < strokes.length; s++) {
|
for (let s = 0; s <= uptoInclusive && s < strokes.length; s++) {
|
||||||
// console.log(`绘制第 ${s} 个笔画:`, strokes[s]);
|
// console.log(`绘制第 ${s} 个笔画:`, strokes[s]);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
||||||
|
// 记录路径是否包含Z命令(已闭合)
|
||||||
|
const hasClosePath = strokes[s].includes('Z');
|
||||||
|
|
||||||
drawSvgPathCommands({
|
drawSvgPathCommands({
|
||||||
ctx,
|
ctx,
|
||||||
pathD: strokes[s],
|
pathD: strokes[s],
|
||||||
@@ -167,13 +203,27 @@ function drawStrokes({
|
|||||||
offsetY: contentOffsetY,
|
offsetY: contentOffsetY,
|
||||||
scale: contentScale,
|
scale: contentScale,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 优化渲染策略:使用 Fill + Stroke 模式
|
||||||
|
// 先 fill 填充内部,再 stroke 描边,stroke 宽度略小,避免双重渲染导致的毛糙
|
||||||
|
// 如果路径没有Z命令,手动闭合(fill需要闭合路径)
|
||||||
|
if (!hasClosePath) {
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 先填充内部
|
||||||
ctx.fillStyle = fillStyle;
|
ctx.fillStyle = fillStyle;
|
||||||
ctx.strokeStyle = strokeStyle;
|
|
||||||
ctx.lineWidth = lineWidth;
|
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
|
// 2. 再描边(stroke宽度设为fill视觉宽度的70%,避免明显的双重渲染)
|
||||||
|
// 与测试页面保持一致:使用相同的stroke宽度计算方式
|
||||||
|
ctx.strokeStyle = strokeStyle;
|
||||||
|
const strokeWidth = Math.max(1.5, lineWidth * 0.7);
|
||||||
|
ctx.lineWidth = strokeWidth;
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.closePath();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注意:不使用 restore,与测试页面保持一致
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DrawTianZiGridParams {
|
interface DrawTianZiGridParams {
|
||||||
@@ -190,15 +240,15 @@ function drawTianZiGrid({
|
|||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
size,
|
size,
|
||||||
lineColor = '#e0e0e0',
|
lineColor = '#a8d5a8', // 浅绿色(中线)
|
||||||
boldColor = '#cccccc',
|
boldColor = '#7fb069', // 中等绿色(外框)
|
||||||
}: DrawTianZiGridParams) {
|
}: DrawTianZiGridParams) {
|
||||||
// 外框(逻辑像素)
|
// 外框(逻辑像素)- 使用中等绿色,清晰可见
|
||||||
ctx.strokeStyle = boldColor;
|
ctx.strokeStyle = boldColor;
|
||||||
ctx.lineWidth = 1; // 2/3≈1,逻辑像素
|
ctx.lineWidth = 1; // 2/3≈1,逻辑像素
|
||||||
ctx.strokeRect(x - size / 2, y - size / 2, size, size);
|
ctx.strokeRect(x - size / 2, y - size / 2, size, size);
|
||||||
|
|
||||||
// 中线(逻辑像素)
|
// 中线(逻辑像素)- 使用浅绿色,柔和护眼
|
||||||
ctx.strokeStyle = lineColor;
|
ctx.strokeStyle = lineColor;
|
||||||
ctx.lineWidth = 1; // 逻辑像素
|
ctx.lineWidth = 1; // 逻辑像素
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
@@ -211,8 +261,8 @@ function drawTianZiGrid({
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
|
|
||||||
// 对角线(淡,逻辑像素)
|
// 对角线(淡绿色,逻辑像素)- 使用很淡的绿色,提供辅助参考线
|
||||||
ctx.strokeStyle = '#eeeeee';
|
ctx.strokeStyle = '#d4f0d4'; // 很淡的绿色
|
||||||
ctx.lineWidth = 1;
|
ctx.lineWidth = 1;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x - size / 2, y - size / 2);
|
ctx.moveTo(x - size / 2, y - size / 2);
|
||||||
@@ -519,7 +569,8 @@ class WordDrawService extends BaseDrawService {
|
|||||||
|
|
||||||
// console.log(`预览格 [${rowIndex}, ${columnIndex}] 坐标: (${x}, ${y}), 笔画数: ${strokes.length}`);
|
// console.log(`预览格 [${rowIndex}, ${columnIndex}] 坐标: (${x}, ${y}), 笔画数: ${strokes.length}`);
|
||||||
|
|
||||||
// 绘制完整汉字(黑色,较粗)
|
// 绘制完整汉字(深灰色,Fill + Stroke模式)
|
||||||
|
// 参考一般练字贴:预览字使用深灰色,不是纯黑色,更柔和护眼
|
||||||
drawStrokes({
|
drawStrokes({
|
||||||
ctx,
|
ctx,
|
||||||
strokes,
|
strokes,
|
||||||
@@ -527,9 +578,9 @@ class WordDrawService extends BaseDrawService {
|
|||||||
offsetY: y,
|
offsetY: y,
|
||||||
size: cellSize,
|
size: cellSize,
|
||||||
uptoInclusive: strokes.length - 1,
|
uptoInclusive: strokes.length - 1,
|
||||||
fillStyle: 'rgb(0,0,0)', // 黑色填充
|
fillStyle: 'rgb(85,85,85)', // 深灰色填充(#555555),比#666666更深一点
|
||||||
strokeStyle: 'rgb(0,0,0)', // 黑色描边
|
strokeStyle: 'rgb(85,85,85)', // 深灰色描边
|
||||||
lineWidth: 1, // 较粗的线条(4/3≈1,逻辑像素)
|
lineWidth: 0.6, // Fill + Stroke 模式,线条宽度调小一点(逻辑像素)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,7 +615,8 @@ class WordDrawService extends BaseDrawService {
|
|||||||
|
|
||||||
// console.log(`练习格 [${rowIndex}, ${columnIndex}] 坐标: (${x}, ${y}), 笔画: ${strokeIndex + 1}/${strokes.length}`);
|
// console.log(`练习格 [${rowIndex}, ${columnIndex}] 坐标: (${x}, ${y}), 笔画: ${strokeIndex + 1}/${strokes.length}`);
|
||||||
|
|
||||||
// 绘制到指定笔画的汉字(灰色,中等粗细)
|
// 绘制到指定笔画的汉字(浅灰色,Fill + Stroke模式)
|
||||||
|
// 参考一般练字贴:练习字使用浅灰色,便于临摹
|
||||||
drawStrokes({
|
drawStrokes({
|
||||||
ctx,
|
ctx,
|
||||||
strokes,
|
strokes,
|
||||||
@@ -572,14 +624,14 @@ class WordDrawService extends BaseDrawService {
|
|||||||
offsetY: y,
|
offsetY: y,
|
||||||
size: cellSize,
|
size: cellSize,
|
||||||
uptoInclusive: strokeIndex,
|
uptoInclusive: strokeIndex,
|
||||||
fillStyle: '#ccc',
|
fillStyle: 'rgb(170,170,170)', // 浅灰色填充(#aaaaaa),参考练字贴颜色
|
||||||
strokeStyle: '#ccc',
|
strokeStyle: 'rgb(170,170,170)', // 浅灰色描边
|
||||||
lineWidth: 1, // 中等粗细(3/3=1,逻辑像素)
|
lineWidth: 0.6, // Fill + Stroke 模式,线条宽度调小一点(逻辑像素)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawDivider 已在基类中实现,此方法保留以保持兼容
|
// drawDivider 已在基类中实现,此方法保留以保持兼容
|
||||||
drawDivider(linY?: number) {
|
drawDivider() {
|
||||||
super.drawDivider();
|
super.drawDivider();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+256
-249
@@ -1,253 +1,260 @@
|
|||||||
{
|
{
|
||||||
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
|
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
|
||||||
"projectname": "doodle-mini",
|
"projectname": "doodle-mini",
|
||||||
"setting": {
|
"setting": {
|
||||||
"compileHotReLoad": false,
|
"compileHotReLoad": false,
|
||||||
"urlCheck": true,
|
"urlCheck": true,
|
||||||
"coverView": false,
|
"coverView": false,
|
||||||
"lazyloadPlaceholderEnable": false,
|
"lazyloadPlaceholderEnable": false,
|
||||||
"skylineRenderEnable": true,
|
"skylineRenderEnable": true,
|
||||||
"preloadBackgroundData": false,
|
"preloadBackgroundData": false,
|
||||||
"autoAudits": false,
|
"autoAudits": false,
|
||||||
"useApiHook": true,
|
"useApiHook": true,
|
||||||
"useApiHostProcess": true,
|
"useApiHostProcess": true,
|
||||||
"showShadowRootInWxmlPanel": false,
|
"showShadowRootInWxmlPanel": false,
|
||||||
"useStaticServer": false,
|
"useStaticServer": false,
|
||||||
"useLanDebug": false,
|
"useLanDebug": false,
|
||||||
"showES6CompileOption": false,
|
"showES6CompileOption": false,
|
||||||
"checkInvalidKey": true,
|
"checkInvalidKey": true,
|
||||||
"ignoreDevUnusedFiles": true,
|
"ignoreDevUnusedFiles": true,
|
||||||
"bigPackageSizeSupport": false
|
"bigPackageSizeSupport": false
|
||||||
},
|
},
|
||||||
"libVersion": "3.7.12",
|
"libVersion": "3.7.12",
|
||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"name": "mathPages/multiplicationTable/multiplicationTable",
|
"name": "pages/copyBook/copyBook",
|
||||||
"pathName": "mathPages/multiplicationTable/multiplicationTable",
|
"pathName": "pages/copyBook/copyBook",
|
||||||
"query": "id=multiplication-table",
|
"query": "",
|
||||||
"scene": null,
|
"scene": null,
|
||||||
"launchMode": "default"
|
"launchMode": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/calculationPractice/calculationPractice",
|
"name": "mathPages/multiplicationTable/multiplicationTable",
|
||||||
"pathName": "mathPages/calculationPractice/calculationPractice",
|
"pathName": "mathPages/multiplicationTable/multiplicationTable",
|
||||||
"query": "id=practice-addition",
|
"query": "id=multiplication-table",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/oneDigitAddition/oneDigitAddition",
|
"name": "mathPages/calculationPractice/calculationPractice",
|
||||||
"pathName": "mathPages/oneDigitAddition/oneDigitAddition",
|
"pathName": "mathPages/calculationPractice/calculationPractice",
|
||||||
"query": "id=one-digit-addition",
|
"query": "id=practice-addition",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/borrowTen/borrowTen",
|
"name": "mathPages/oneDigitAddition/oneDigitAddition",
|
||||||
"pathName": "mathPages/borrowTen/borrowTen",
|
"pathName": "mathPages/oneDigitAddition/oneDigitAddition",
|
||||||
"query": "id=borrow-ten",
|
"query": "id=one-digit-addition",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/flatTen/flatTen",
|
"name": "mathPages/borrowTen/borrowTen",
|
||||||
"pathName": "mathPages/flatTen/flatTen",
|
"pathName": "mathPages/borrowTen/borrowTen",
|
||||||
"query": "id=flat-ten",
|
"query": "id=borrow-ten",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/breakTen/breakTen",
|
"name": "mathPages/flatTen/flatTen",
|
||||||
"pathName": "mathPages/breakTen/breakTen",
|
"pathName": "mathPages/flatTen/flatTen",
|
||||||
"query": "id=break-ten",
|
"query": "id=flat-ten",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/makeTen/makeTen",
|
"name": "mathPages/breakTen/breakTen",
|
||||||
"pathName": "mathPages/makeTen/makeTen",
|
"pathName": "mathPages/breakTen/breakTen",
|
||||||
"query": "id=make-ten",
|
"query": "id=break-ten",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/dotConnect/dotConnect",
|
"name": "mathPages/makeTen/makeTen",
|
||||||
"pathName": "focusPages/dotConnect/dotConnect",
|
"pathName": "mathPages/makeTen/makeTen",
|
||||||
"query": "id=dot-connect",
|
"query": "id=make-ten",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/numberObjectMatch/numberObjectMatch",
|
"name": "focusPages/dotConnect/dotConnect",
|
||||||
"pathName": "mathPages/numberObjectMatch/numberObjectMatch",
|
"pathName": "focusPages/dotConnect/dotConnect",
|
||||||
"query": "id=number-object-match",
|
"query": "id=dot-connect",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/colorShapeMatch/colorShapeMatch",
|
"name": "mathPages/numberObjectMatch/numberObjectMatch",
|
||||||
"pathName": "focusPages/colorShapeMatch/colorShapeMatch",
|
"pathName": "mathPages/numberObjectMatch/numberObjectMatch",
|
||||||
"query": "id=color-shape-match",
|
"query": "id=number-object-match",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/numberSort/numberSort",
|
"name": "focusPages/colorShapeMatch/colorShapeMatch",
|
||||||
"pathName": "mathPages/numberSort/numberSort",
|
"pathName": "focusPages/colorShapeMatch/colorShapeMatch",
|
||||||
"query": "id=number-sort",
|
"query": "id=color-shape-match",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/codeConnect/codeConnect",
|
"name": "mathPages/numberSort/numberSort",
|
||||||
"pathName": "focusPages/codeConnect/codeConnect",
|
"pathName": "mathPages/numberSort/numberSort",
|
||||||
"query": "id=code-connect",
|
"query": "id=number-sort",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/gridReasoning/gridReasoning",
|
"name": "focusPages/codeConnect/codeConnect",
|
||||||
"pathName": "focusPages/gridReasoning/gridReasoning",
|
"pathName": "focusPages/codeConnect/codeConnect",
|
||||||
"query": "id=grid-reasoning",
|
"query": "id=code-connect",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/lineRecognition/lineRecognition",
|
"name": "focusPages/gridReasoning/gridReasoning",
|
||||||
"pathName": "focusPages/lineRecognition/lineRecognition",
|
"pathName": "focusPages/gridReasoning/gridReasoning",
|
||||||
"query": "id=line-recognition",
|
"query": "id=grid-reasoning",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/matchConnect/matchConnect",
|
"name": "focusPages/lineRecognition/lineRecognition",
|
||||||
"pathName": "focusPages/matchConnect/matchConnect",
|
"pathName": "focusPages/lineRecognition/lineRecognition",
|
||||||
"query": "id=match-connect",
|
"query": "id=line-recognition",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/colorPattern/colorPattern",
|
"name": "focusPages/matchConnect/matchConnect",
|
||||||
"pathName": "focusPages/colorPattern/colorPattern",
|
"pathName": "focusPages/matchConnect/matchConnect",
|
||||||
"query": "id=color-pattern",
|
"query": "id=match-connect",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/shapeSymbol/shapeSymbol",
|
"name": "focusPages/colorPattern/colorPattern",
|
||||||
"pathName": "focusPages/shapeSymbol/shapeSymbol",
|
"pathName": "focusPages/colorPattern/colorPattern",
|
||||||
"query": "id=shape-symbol",
|
"query": "id=color-pattern",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/positionColoring/positionColoring",
|
"name": "focusPages/shapeSymbol/shapeSymbol",
|
||||||
"pathName": "focusPages/positionColoring/positionColoring",
|
"pathName": "focusPages/shapeSymbol/shapeSymbol",
|
||||||
"query": "id=position-coloring",
|
"query": "id=shape-symbol",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/focusIndex/focusIndex",
|
"name": "focusPages/positionColoring/positionColoring",
|
||||||
"pathName": "pages/focusIndex/focusIndex",
|
"pathName": "focusPages/positionColoring/positionColoring",
|
||||||
"query": "",
|
"query": "id=position-coloring",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/gridDrawing/gridDrawing",
|
"name": "pages/focusIndex/focusIndex",
|
||||||
"pathName": "focusPages/gridDrawing/gridDrawing",
|
"pathName": "pages/focusIndex/focusIndex",
|
||||||
"query": "id=grid-drawing",
|
"query": "",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/shape/shape",
|
"name": "focusPages/gridDrawing/gridDrawing",
|
||||||
"pathName": "focusPages/shape/shape",
|
"pathName": "focusPages/gridDrawing/gridDrawing",
|
||||||
"query": "",
|
"query": "id=grid-drawing",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/index/index",
|
"name": "focusPages/shape/shape",
|
||||||
"pathName": "pages/index/index",
|
"pathName": "focusPages/shape/shape",
|
||||||
"query": "",
|
"query": "",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/numberDecompose/numberDecompose",
|
"name": "pages/index/index",
|
||||||
"pathName": "mathPages/numberDecompose/numberDecompose",
|
"pathName": "pages/index/index",
|
||||||
"query": "id=number-decompose&mode=with-image",
|
"query": "",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/countingSelect/countingSelect",
|
"name": "mathPages/numberDecompose/numberDecompose",
|
||||||
"pathName": "mathPages/countingSelect/countingSelect",
|
"pathName": "mathPages/numberDecompose/numberDecompose",
|
||||||
"query": "id=counting-fill",
|
"query": "id=number-decompose&mode=with-image",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/compare/compare",
|
"name": "mathPages/countingSelect/countingSelect",
|
||||||
"pathName": "mathPages/compare/compare",
|
"pathName": "mathPages/countingSelect/countingSelect",
|
||||||
"query": "id=compare",
|
"query": "id=counting-fill",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/missingNumber/missingNumber",
|
"name": "mathPages/compare/compare",
|
||||||
"pathName": "mathPages/missingNumber/missingNumber",
|
"pathName": "mathPages/compare/compare",
|
||||||
"query": "id=missing-number",
|
"query": "id=compare",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/addition/addition",
|
"name": "mathPages/missingNumber/missingNumber",
|
||||||
"pathName": "mathPages/addition/addition",
|
"pathName": "mathPages/missingNumber/missingNumber",
|
||||||
"query": "id=addition-5",
|
"query": "id=missing-number",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/countMatch/countMatch",
|
"name": "mathPages/addition/addition",
|
||||||
"pathName": "mathPages/countMatch/countMatch",
|
"pathName": "mathPages/addition/addition",
|
||||||
"query": "id=number-coloring",
|
"query": "id=addition-5",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/mathIndex/mathIndex",
|
"name": "mathPages/countMatch/countMatch",
|
||||||
"pathName": "pages/mathIndex/mathIndex",
|
"pathName": "mathPages/countMatch/countMatch",
|
||||||
"query": "",
|
"query": "id=number-coloring",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/countMatch/countMatch",
|
"name": "pages/mathIndex/mathIndex",
|
||||||
"pathName": "mathPages/countMatch/countMatch",
|
"pathName": "pages/mathIndex/mathIndex",
|
||||||
"query": "id=counting-matching",
|
"query": "",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "mathPages/numberFind/numberFind",
|
"name": "mathPages/countMatch/countMatch",
|
||||||
"pathName": "mathPages/numberFind/numberFind",
|
"pathName": "mathPages/countMatch/countMatch",
|
||||||
"query": "id=number-write",
|
"query": "id=counting-matching",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pages/mathIndex/mathIndex",
|
"name": "mathPages/numberFind/numberFind",
|
||||||
"pathName": "pages/mathIndex/mathIndex",
|
"pathName": "mathPages/numberFind/numberFind",
|
||||||
"query": "",
|
"query": "id=number-write",
|
||||||
"launchMode": "default",
|
"launchMode": "default",
|
||||||
"scene": null
|
"scene": null
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
"name": "pages/mathIndex/mathIndex",
|
||||||
|
"pathName": "pages/mathIndex/mathIndex",
|
||||||
|
"query": "",
|
||||||
|
"launchMode": "default",
|
||||||
|
"scene": null
|
||||||
}
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user