feat:2.6.1增加 数物对应 和 根据颜色画图形
This commit is contained in:
@@ -1,32 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: true, currentModeName: currentModeName, typeActions: typeActions, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -1,38 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 随机生成按钮 -->
|
||||
<view class="random-button-area">
|
||||
<toy-button
|
||||
class="random-button"
|
||||
type="primary"
|
||||
bind:click="onRandom"
|
||||
width="100%"
|
||||
height="80rpx"
|
||||
icon="refresh"
|
||||
icon-class-prefix="toy-icon">
|
||||
随机生成
|
||||
</toy-button>
|
||||
</view>
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: false, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
wx:if="{{showTypeSelector}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: true, currentModeName: currentModeName, typeActions: typeActions, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -1,38 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 随机生成按钮 -->
|
||||
<view class="random-button-area">
|
||||
<toy-button
|
||||
class="random-button"
|
||||
type="primary"
|
||||
bind:click="onRandom"
|
||||
width="100%"
|
||||
height="80rpx"
|
||||
icon="refresh"
|
||||
icon-class-prefix="toy-icon">
|
||||
随机生成
|
||||
</toy-button>
|
||||
</view>
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: false, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
wx:if="{{showTypeSelector}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: true, currentModeName: currentModeName, typeActions: typeActions, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<math-type-selector
|
||||
wx:if="{{showTypeSelector}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: true, currentModeName: currentModeName, typeActions: typeActions, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -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,145 @@
|
||||
import NumberObjectMatchDraw from '../shared/service/numberObjectMatchDraw';
|
||||
import {
|
||||
createMathPage,
|
||||
CanvasDataState,
|
||||
} from '../shared/common/mathPageMixin';
|
||||
import { NumberObjectMatchData } from '../shared/service/numberObjectMatchDraw';
|
||||
|
||||
createMathPage({
|
||||
canvas: null as Canvas | null,
|
||||
ctx: null as RenderingContext | null,
|
||||
boxHeight: 0,
|
||||
boxWidth: 0,
|
||||
drawService: null as NumberObjectMatchDraw | null,
|
||||
matchData: null as NumberObjectMatchData | null,
|
||||
|
||||
data: {
|
||||
pageTitle: '数物对应',
|
||||
functionId: '',
|
||||
hasContent: false,
|
||||
showShareDialog: false,
|
||||
} as CanvasDataState,
|
||||
|
||||
onLoad(options: { id?: string }) {
|
||||
const functionId = options.id || 'number-object-match';
|
||||
this.initPageInfo(functionId, '数物对应');
|
||||
},
|
||||
|
||||
onReady() {
|
||||
this.initCanvas({
|
||||
createDrawService: (
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) => {
|
||||
return new NumberObjectMatchDraw(canvas, ctx, options);
|
||||
},
|
||||
drawServiceOptions: {},
|
||||
onCanvasReady: () => {
|
||||
// 初始随机生成
|
||||
this.onRandom();
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 绘制Canvas内容
|
||||
*/
|
||||
async drawCanvas() {
|
||||
if (!this.ctx || !this.drawService || !this.matchData) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.drawService.draw(this.matchData);
|
||||
this.setData({ hasContent: true });
|
||||
} catch (error) {
|
||||
console.error('绘制失败:', error);
|
||||
this.setData({ hasContent: false });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 随机生成
|
||||
*/
|
||||
onRandom() {
|
||||
// 从两个目录中随机选择4张不重复的图片
|
||||
// fruits: 1-22, twelve-animals: 1-12
|
||||
const fruitsIndices = Array.from({ length: 22 }, (_, i) => i + 1);
|
||||
const animalsIndices = Array.from({ length: 12 }, (_, i) => i + 1);
|
||||
|
||||
// 合并所有可用的图片索引
|
||||
const allImages: Array<{
|
||||
imageIndex: number;
|
||||
folder: 'fruits' | 'twelve-animals';
|
||||
}> = [
|
||||
...fruitsIndices.map((idx) => ({
|
||||
imageIndex: idx,
|
||||
folder: 'fruits' as const,
|
||||
})),
|
||||
...animalsIndices.map((idx) => ({
|
||||
imageIndex: idx,
|
||||
folder: 'twelve-animals' as const,
|
||||
})),
|
||||
];
|
||||
|
||||
// 随机打乱并选择4张不重复的图片
|
||||
const shuffled = [...allImages].sort(() => Math.random() - 0.5);
|
||||
const selectedImages = shuffled.slice(0, 4);
|
||||
|
||||
// 生成上面区域的16张图片(4行4列)
|
||||
// 每张图片随机出现,并添加随机偏移
|
||||
const gridImages: Array<{
|
||||
imageIndex: number;
|
||||
folder: 'fruits' | 'twelve-animals';
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
}> = [];
|
||||
|
||||
const maxOffset = 15; // 最大偏移量
|
||||
for (let i = 0; i < 20; i++) {
|
||||
// 从选中的4张图片中随机选择一张
|
||||
const randomImage =
|
||||
selectedImages[
|
||||
Math.floor(Math.random() * selectedImages.length)
|
||||
];
|
||||
|
||||
// 生成随机偏移(-maxOffset 到 maxOffset)
|
||||
const offsetX = (Math.random() - 0.5) * 2 * maxOffset;
|
||||
const offsetY = (Math.random() - 0.5) * 2 * maxOffset;
|
||||
|
||||
gridImages.push({
|
||||
...randomImage,
|
||||
offsetX,
|
||||
offsetY,
|
||||
});
|
||||
}
|
||||
|
||||
// 下面区域第一行的4张图片(使用选中的4张图片)
|
||||
const bottomImages = [...selectedImages];
|
||||
|
||||
// 计算每个图片在上面区域出现的次数
|
||||
const imageCountMap = new Map<string, number>();
|
||||
gridImages.forEach((item) => {
|
||||
const key = `${item.folder}-${item.imageIndex}`;
|
||||
imageCountMap.set(key, (imageCountMap.get(key) || 0) + 1);
|
||||
});
|
||||
|
||||
// 获取每个图片的数量(按 selectedImages 的顺序)
|
||||
const imageCounts = selectedImages.map((img) => {
|
||||
const key = `${img.folder}-${img.imageIndex}`;
|
||||
return imageCountMap.get(key) || 0;
|
||||
});
|
||||
|
||||
// 将数量乱序排列
|
||||
const bottomNumbers = [...imageCounts].sort(() => Math.random() - 0.5);
|
||||
|
||||
this.matchData = {
|
||||
gridImages,
|
||||
bottomImages,
|
||||
bottomNumbers,
|
||||
};
|
||||
|
||||
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: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
@@ -0,0 +1 @@
|
||||
@import '../../base/baseDrawPage.wxss';
|
||||
@@ -1,38 +1,6 @@
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
<!-- 模版不能跨包引用 -->
|
||||
<import src="../shared/templates/canvas-page-template.wxml" />
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 随机生成按钮 -->
|
||||
<view class="random-button-area">
|
||||
<toy-button
|
||||
class="random-button"
|
||||
type="primary"
|
||||
bind:click="onRandom"
|
||||
width="100%"
|
||||
height="80rpx"
|
||||
icon="refresh"
|
||||
icon-class-prefix="toy-icon">
|
||||
随机生成
|
||||
</toy-button>
|
||||
</view>
|
||||
<draw-ad type="mathDraw"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
<math-bottom-buttons
|
||||
disabled="{{!hasContent}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: false, adType: 'mathDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
/**
|
||||
* 数物对应绘制服务
|
||||
*/
|
||||
|
||||
import { BaseDrawService } from '../../../service/baseDraw';
|
||||
import { getImage } from '../../../utils/index';
|
||||
|
||||
/**
|
||||
* 数物对应数据
|
||||
*/
|
||||
export interface NumberObjectMatchData {
|
||||
/** 上面区域的图片配置(4行4列,共16张) */
|
||||
gridImages: Array<{
|
||||
imageIndex: number;
|
||||
folder: 'fruits' | 'twelve-animals';
|
||||
offsetX: number; // 随机左右偏移
|
||||
offsetY: number; // 随机上下偏移
|
||||
}>;
|
||||
/** 下面区域第一行的4张图片配置 */
|
||||
bottomImages: Array<{
|
||||
imageIndex: number;
|
||||
folder: 'fruits' | 'twelve-animals';
|
||||
}>;
|
||||
/** 第二行要显示的数量(乱序排列) */
|
||||
bottomNumbers: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 数物对应绘制服务
|
||||
*/
|
||||
class NumberObjectMatchDraw extends BaseDrawService {
|
||||
matchData: NumberObjectMatchData | null = null;
|
||||
|
||||
constructor(
|
||||
canvas: Canvas,
|
||||
ctx: RenderingContext,
|
||||
options?: Record<string, any>,
|
||||
) {
|
||||
super(canvas, ctx, options);
|
||||
this.matchData = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制数物对应内容
|
||||
*/
|
||||
async draw(matchData: NumberObjectMatchData) {
|
||||
if (
|
||||
!matchData ||
|
||||
!matchData.gridImages ||
|
||||
!matchData.bottomImages ||
|
||||
!matchData.bottomNumbers
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.matchData = matchData;
|
||||
this.prepareDraw();
|
||||
await this.drawHeaderAndDivider();
|
||||
await this.drawContent({
|
||||
canvas: this.canvas,
|
||||
ctx: this.ctx,
|
||||
matchData: this.matchData,
|
||||
canvasWidth: this.canvasWidth,
|
||||
startY: this.currentY,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制内容区域
|
||||
*/
|
||||
private async drawContent(params: {
|
||||
canvas: WechatMiniprogram.Canvas;
|
||||
ctx: RenderingContext;
|
||||
matchData: NumberObjectMatchData;
|
||||
canvasWidth: number;
|
||||
startY: number;
|
||||
}): Promise<void> {
|
||||
const { canvas, ctx, matchData, canvasWidth } = params;
|
||||
let { startY } = params;
|
||||
|
||||
startY = startY + 25;
|
||||
|
||||
const margin = 0; // 左右边距
|
||||
const imagePadding = 10; // 图片内边距
|
||||
const imageSize = 75; // 图片大小
|
||||
const cellSize = imageSize + imagePadding * 2; // 每个格子的大小
|
||||
|
||||
// ========== 绘制上面区域(4行4列) ==========
|
||||
const gridCols = 5;
|
||||
const gridRows = 4;
|
||||
const gridWidth = cellSize * gridCols;
|
||||
const gridStartX = margin + (canvasWidth - margin * 2 - gridWidth) / 2; // 居中
|
||||
const gridStartY = startY;
|
||||
|
||||
// 加载并绘制所有图片
|
||||
const imagePromises = matchData.gridImages.map(async (item, index) => {
|
||||
const row = Math.floor(index / gridCols);
|
||||
const col = index % gridCols;
|
||||
|
||||
const baseX = gridStartX + col * cellSize + cellSize / 2;
|
||||
const baseY = gridStartY + row * cellSize + cellSize / 2;
|
||||
|
||||
// 加上随机偏移
|
||||
const imageX = baseX + item.offsetX;
|
||||
const imageY = baseY + item.offsetY;
|
||||
|
||||
// 加载图片
|
||||
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,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (image) {
|
||||
// 计算图片高度(等比例缩放)
|
||||
// @ts-ignore - 微信小程序图片对象有 width 和 height 属性
|
||||
const scaledHeight = (image.height / image.width) * imageSize;
|
||||
|
||||
// 绘制图片(居中)
|
||||
ctx.drawImage(
|
||||
image,
|
||||
imageX - imageSize / 2,
|
||||
imageY - scaledHeight / 2,
|
||||
imageSize,
|
||||
scaledHeight,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(imagePromises);
|
||||
|
||||
// ========== 绘制虚线分割线 ==========
|
||||
const dividerY = gridStartY + gridRows * cellSize + 30;
|
||||
this.drawDashedDivider(dividerY, margin);
|
||||
|
||||
// ========== 绘制下面区域 ==========
|
||||
const bottomStartY = dividerY + 30;
|
||||
const bottomImageSize = 50; // 下面图片大小
|
||||
const bottomBoxSize = bottomImageSize + imagePadding * 2; // 图片框大小
|
||||
const bottomSpacing = 40; // 图片间距
|
||||
const bottomRowSpacing = 80; // 两行间距
|
||||
|
||||
// 计算下面区域的总宽度(4个图片框 + 3个间距)
|
||||
const bottomTotalWidth = bottomBoxSize * 4 + bottomSpacing * 3;
|
||||
const bottomStartX =
|
||||
margin + (canvasWidth - margin * 2 - bottomTotalWidth) / 2; // 居中
|
||||
|
||||
// 第一行:绘制图片框和图片
|
||||
for (let i = 0; i < matchData.bottomImages.length; i++) {
|
||||
const item = matchData.bottomImages[i];
|
||||
const boxX = bottomStartX + i * (bottomBoxSize + bottomSpacing);
|
||||
const boxY = bottomStartY;
|
||||
const imageX = boxX + imagePadding;
|
||||
const imageY = boxY + imagePadding;
|
||||
|
||||
// 绘制方框(边线宽度2)
|
||||
ctx.strokeStyle = '#999';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(boxX, boxY, bottomBoxSize, bottomBoxSize);
|
||||
|
||||
// 加载并绘制图片
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制第一行方框下方的圆点(间隔5)
|
||||
const dotRadius = 4; // 圆点半径
|
||||
const dotSpacing = 10; // 圆点与方框的间距
|
||||
const topDotY = bottomStartY + bottomBoxSize + dotSpacing;
|
||||
|
||||
for (let i = 0; i < matchData.bottomImages.length; i++) {
|
||||
const boxX = bottomStartX + i * (bottomBoxSize + bottomSpacing);
|
||||
const dotX = boxX + bottomBoxSize / 2; // 方框正中间
|
||||
|
||||
// 绘制圆点
|
||||
ctx.fillStyle = '#93D333';
|
||||
ctx.beginPath();
|
||||
ctx.arc(dotX, topDotY, dotRadius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// 第二行:绘制数字输入框(4个方框)
|
||||
const numberBoxY = bottomStartY + bottomBoxSize + bottomRowSpacing;
|
||||
const numberBoxSize = bottomBoxSize; // 数字框大小和图片框一样
|
||||
|
||||
// 绘制第二行方框上方的圆点(间隔5)
|
||||
const bottomDotY = numberBoxY - dotSpacing;
|
||||
|
||||
// 先绘制所有圆点
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const boxX = bottomStartX + i * (numberBoxSize + bottomSpacing);
|
||||
const dotX = boxX + numberBoxSize / 2; // 方框正中间
|
||||
|
||||
// 绘制圆点
|
||||
ctx.fillStyle = '#93D333';
|
||||
ctx.beginPath();
|
||||
ctx.arc(dotX, bottomDotY, dotRadius, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
// 再绘制方框和数字
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const boxX = bottomStartX + i * (numberBoxSize + bottomSpacing);
|
||||
|
||||
// 绘制方框(边线宽度2)
|
||||
ctx.strokeStyle = '#999';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeRect(boxX, numberBoxY, numberBoxSize, numberBoxSize);
|
||||
|
||||
// 绘制数字(如果存在)
|
||||
if (
|
||||
matchData.bottomNumbers &&
|
||||
matchData.bottomNumbers[i] !== undefined
|
||||
) {
|
||||
const number = matchData.bottomNumbers[i];
|
||||
ctx.fillStyle = '#141414';
|
||||
ctx.font = `bold ${48}px "Microsoft Yahei"`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(
|
||||
String(number),
|
||||
boxX + numberBoxSize / 2,
|
||||
numberBoxY + numberBoxSize / 2,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NumberObjectMatchDraw;
|
||||
@@ -0,0 +1,78 @@
|
||||
<!--
|
||||
Canvas 页面模板
|
||||
用于统一管理 Canvas 绘制页面的通用结构
|
||||
|
||||
使用方式:
|
||||
<import src="../../templates/canvas-page-template.wxml" />
|
||||
<template
|
||||
is="canvasPageTemplate"
|
||||
data="{{boxWidth: boxWidth, boxHeight: boxHeight, hasTypeSelector: false, adType: 'focusDraw', disabled: !hasContent, showShareDialog: showShareDialog}}" />
|
||||
|
||||
参数说明:
|
||||
- boxWidth: Canvas 宽度
|
||||
- boxHeight: Canvas 高度
|
||||
- hasTypeSelector: 是否显示类型选择器(布尔值)
|
||||
- showTypeSelector: 条件显示类型选择器(可选,用于 countMatch、missingNumber 等页面)
|
||||
- currentModeName: 当前模式名称(类型选择器使用)
|
||||
- typeActions: 类型选项数组(类型选择器使用)
|
||||
- adType: 广告位类型('focusDraw' 或 'mathDraw')
|
||||
- disabled: 底部按钮是否禁用
|
||||
- showShareDialog: 是否显示分享弹窗
|
||||
-->
|
||||
<template name="canvasPageTemplate">
|
||||
<view class="page-container">
|
||||
<view class="wrapper">
|
||||
<text class="wrapper-title">预览打印效果</text>
|
||||
|
||||
<!-- 预览打印效果 -->
|
||||
<view id="canvasWrapper" class="canvas-wrapper">
|
||||
<canvas
|
||||
type="2d"
|
||||
id="canvasContent"
|
||||
class="canvas-content"
|
||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||
</view>
|
||||
|
||||
<!-- 类型选择器和随机生成按钮 -->
|
||||
<!-- 支持两种方式:hasTypeSelector(布尔值)或 showTypeSelector(条件显示) -->
|
||||
<math-type-selector
|
||||
wx:if="{{hasTypeSelector || showTypeSelector}}"
|
||||
current-type-name="{{currentModeName}}"
|
||||
type-actions="{{typeActions}}"
|
||||
bind:select="onSelectType"
|
||||
bind:random="onRandom" />
|
||||
|
||||
<!-- 随机生成按钮(当没有类型选择器时显示) -->
|
||||
<view
|
||||
class="random-button-area"
|
||||
wx:if="{{!hasTypeSelector && !showTypeSelector}}">
|
||||
<toy-button
|
||||
class="random-button"
|
||||
type="primary"
|
||||
bind:click="onRandom"
|
||||
width="100%"
|
||||
height="80rpx"
|
||||
icon="refresh"
|
||||
icon-class-prefix="toy-icon">
|
||||
随机生成
|
||||
</toy-button>
|
||||
</view>
|
||||
|
||||
<!-- 广告位 -->
|
||||
<draw-ad type="{{adType}}"></draw-ad>
|
||||
</view>
|
||||
<view class="empty"></view>
|
||||
</view>
|
||||
|
||||
<!-- 底部按钮 -->
|
||||
<math-bottom-buttons
|
||||
disabled="{{disabled}}"
|
||||
bind:share="onShareAppMessage"
|
||||
bind:export="exportToPrint" />
|
||||
|
||||
<!-- 分享引导弹窗 -->
|
||||
<share-guide-popup
|
||||
show="{{showShareDialog}}"
|
||||
bind:onClose="onCloseShareDialog"
|
||||
bind:onShareSuccess="onShareSuccess" />
|
||||
</template>
|
||||
Reference in New Issue
Block a user