feat:V2.5.3 修改下载分享弹窗问题、新增连连看
This commit is contained in:
@@ -29,7 +29,8 @@
|
|||||||
"shape/shape",
|
"shape/shape",
|
||||||
"positionColoring/positionColoring",
|
"positionColoring/positionColoring",
|
||||||
"shapeSymbol/shapeSymbol",
|
"shapeSymbol/shapeSymbol",
|
||||||
"colorPattern/colorPattern"
|
"colorPattern/colorPattern",
|
||||||
|
"matchConnect/matchConnect"
|
||||||
],
|
],
|
||||||
"independent": false
|
"independent": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,12 @@ App<IAppOption>({
|
|||||||
onLaunch() {
|
onLaunch() {
|
||||||
const accountInfo = wx.getAccountInfoSync();
|
const accountInfo = wx.getAccountInfoSync();
|
||||||
const env = accountInfo.miniProgram.envVersion || 'release';
|
const env = accountInfo.miniProgram.envVersion || 'release';
|
||||||
|
/**
|
||||||
|
* 设置环境变量
|
||||||
|
* 开发环境:develop
|
||||||
|
* 体验环境:trial
|
||||||
|
* 正式环境:release
|
||||||
|
*/
|
||||||
this.globalData.env = env;
|
this.globalData.env = env;
|
||||||
|
|
||||||
// 从 localStorage 读取 UUID,如果没有则生成并存储
|
// 从 localStorage 读取 UUID,如果没有则生成并存储
|
||||||
|
|||||||
@@ -40,6 +40,14 @@ export const FOCUS_FUNCTION_TYPES: FocusFunctionType[] = [
|
|||||||
desc: '观察颜色规律,在空白图形中涂上颜色',
|
desc: '观察颜色规律,在空白图形中涂上颜色',
|
||||||
icon: '🎨',
|
icon: '🎨',
|
||||||
},
|
},
|
||||||
|
// 连连看
|
||||||
|
{
|
||||||
|
id: 'match-connect',
|
||||||
|
page: 'matchConnect',
|
||||||
|
title: '连连看',
|
||||||
|
desc: '快来根据物品连一连吧!',
|
||||||
|
icon: '🔗',
|
||||||
|
},
|
||||||
// 格子仿画
|
// 格子仿画
|
||||||
{
|
{
|
||||||
id: 'grid-drawing-3x3',
|
id: 'grid-drawing-3x3',
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import MatchConnectDraw from '../shared/service/matchConnectDraw';
|
||||||
|
import { createFocusPage } from '../shared/common/focusPageMixin';
|
||||||
|
import { MatchConnectData } from '../shared/service/matchConnectDraw';
|
||||||
|
|
||||||
|
createFocusPage({
|
||||||
|
canvas: null as Canvas | null,
|
||||||
|
ctx: null as RenderingContext | null,
|
||||||
|
boxHeight: 0,
|
||||||
|
boxWidth: 0,
|
||||||
|
drawService: null as MatchConnectDraw | null,
|
||||||
|
matchData: null as MatchConnectData | null,
|
||||||
|
|
||||||
|
data: {
|
||||||
|
pageTitle: '连连看',
|
||||||
|
functionId: '',
|
||||||
|
hasContent: false,
|
||||||
|
showShareDialog: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad(options: { id?: string }) {
|
||||||
|
const functionId = options.id || 'match-connect';
|
||||||
|
this.setData({
|
||||||
|
functionId,
|
||||||
|
});
|
||||||
|
this.initPageInfo(functionId, '连连看');
|
||||||
|
},
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
this.initCanvas({
|
||||||
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
return new MatchConnectDraw(canvas, ctx, options);
|
||||||
|
},
|
||||||
|
drawServiceOptions: {
|
||||||
|
title: this.data.pageTitle,
|
||||||
|
subTitle: '快来根据物品连一连吧!',
|
||||||
|
functionId: this.data.functionId,
|
||||||
|
},
|
||||||
|
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() {
|
||||||
|
// 从33张图片中随机选择5张不重复的图片
|
||||||
|
const allImageIndices = Array.from({ length: 33 }, (_, i) => i + 1);
|
||||||
|
const shuffled = [...allImageIndices].sort(() => Math.random() - 0.5);
|
||||||
|
const selectedImages = shuffled.slice(0, 5);
|
||||||
|
|
||||||
|
// 创建参考序列(顺序)
|
||||||
|
const referenceSequence = [...selectedImages];
|
||||||
|
|
||||||
|
// 创建6个框,每个框包含5张图片的索引(位置由绘制服务随机生成)
|
||||||
|
const boxes: Array<{
|
||||||
|
imageIndices: number[];
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
for (let boxIndex = 0; boxIndex < 6; boxIndex++) {
|
||||||
|
// 每个框使用相同的5张图片
|
||||||
|
boxes.push({
|
||||||
|
imageIndices: [...selectedImages],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.matchData = {
|
||||||
|
referenceSequence,
|
||||||
|
boxes,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.drawCanvas();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- 随机生成按钮 -->
|
||||||
|
<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>
|
||||||
|
</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" />
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@import '../../base/baseDrawPage.wxss';
|
||||||
@@ -0,0 +1,461 @@
|
|||||||
|
/**
|
||||||
|
* 连连看绘制服务
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDrawService } from '../../../service/baseDraw';
|
||||||
|
import { getImage } from '../../../utils/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连连看数据
|
||||||
|
*/
|
||||||
|
export interface MatchConnectData {
|
||||||
|
/** 参考序列:5张图片的索引数组 */
|
||||||
|
referenceSequence: number[];
|
||||||
|
/** 6个框,每个框包含5张图片的索引(位置由绘制服务随机生成) */
|
||||||
|
boxes: Array<{
|
||||||
|
imageIndices: number[];
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连连看绘制服务
|
||||||
|
*/
|
||||||
|
class MatchConnectDraw extends BaseDrawService {
|
||||||
|
matchData: MatchConnectData | null = null;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) {
|
||||||
|
super(canvas, ctx, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制连连看内容
|
||||||
|
*/
|
||||||
|
async draw(matchData: MatchConnectData) {
|
||||||
|
if (!matchData || !matchData.referenceSequence || !matchData.boxes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPrintConfig();
|
||||||
|
this.matchData = matchData;
|
||||||
|
this.clear();
|
||||||
|
this.setPaper();
|
||||||
|
|
||||||
|
// 绘制Header
|
||||||
|
if (this.headerType !== 'minimal') {
|
||||||
|
await this.drawHeader();
|
||||||
|
} else {
|
||||||
|
this.drawMiniHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制内容区域
|
||||||
|
this.drawDivider();
|
||||||
|
await this.drawContent({
|
||||||
|
canvas: this.canvas,
|
||||||
|
ctx: this.ctx,
|
||||||
|
matchData: this.matchData,
|
||||||
|
canvasWidth: this.canvasWidth,
|
||||||
|
startY: this.currentY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成不重叠的图片位置
|
||||||
|
* @param count 图片数量
|
||||||
|
* @param contentWidth 内容区域宽度
|
||||||
|
* @param contentHeight 内容区域高度
|
||||||
|
* @param imageSize 图片大小
|
||||||
|
* @param imagePadding 图片padding
|
||||||
|
* @returns 图片位置数组,每个位置包含x和y(相对于内容区域的坐标)
|
||||||
|
*/
|
||||||
|
private generateNonOverlappingPositions(
|
||||||
|
count: number,
|
||||||
|
contentWidth: number,
|
||||||
|
contentHeight: number,
|
||||||
|
imageSize: number,
|
||||||
|
imagePadding: number,
|
||||||
|
): Array<{ x: number; y: number }> {
|
||||||
|
// 计算最大可放置位置(图片左上角的位置)
|
||||||
|
const maxX = contentWidth - imageSize;
|
||||||
|
const maxY = contentHeight - imageSize;
|
||||||
|
const positions: Array<{ x: number; y: number }> = [];
|
||||||
|
const placedRects: Array<{
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
right: number;
|
||||||
|
bottom: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查两个矩形是否重叠
|
||||||
|
*/
|
||||||
|
const isRectOverlap = (
|
||||||
|
rect1: {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
right: number;
|
||||||
|
bottom: number;
|
||||||
|
},
|
||||||
|
rect2: {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
right: number;
|
||||||
|
bottom: number;
|
||||||
|
},
|
||||||
|
): boolean => {
|
||||||
|
return !(
|
||||||
|
rect1.right < rect2.left ||
|
||||||
|
rect1.left > rect2.right ||
|
||||||
|
rect1.bottom < rect2.top ||
|
||||||
|
rect1.top > rect2.bottom
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算图片的实际占用大小(包含padding)
|
||||||
|
const occupiedSize = imageSize + imagePadding * 2;
|
||||||
|
|
||||||
|
// 生成候选位置网格,然后随机打乱
|
||||||
|
// 这样可以确保位置分布更均匀,同时保持随机性
|
||||||
|
const gridCols = Math.max(3, Math.floor(contentWidth / occupiedSize));
|
||||||
|
const gridRows = Math.max(2, Math.floor(contentHeight / occupiedSize));
|
||||||
|
const cellWidth = maxX / Math.max(1, gridCols - 1);
|
||||||
|
const cellHeight = maxY / Math.max(1, gridRows - 1);
|
||||||
|
|
||||||
|
// 生成所有可能的网格位置
|
||||||
|
const candidatePositions: Array<{ x: number; y: number }> = [];
|
||||||
|
for (let row = 0; row < gridRows; row++) {
|
||||||
|
for (let col = 0; col < gridCols; col++) {
|
||||||
|
const x = col * cellWidth;
|
||||||
|
const y = row * cellHeight;
|
||||||
|
candidatePositions.push({ x, y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 随机打乱候选位置
|
||||||
|
const shuffledCandidates = [...candidatePositions].sort(
|
||||||
|
() => Math.random() - 0.5,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 从打乱的候选位置中选择不重叠的位置
|
||||||
|
for (let i = 0; i < count; i++) {
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
// 先尝试从网格候选位置中选择
|
||||||
|
for (const candidate of shuffledCandidates) {
|
||||||
|
const x = candidate.x;
|
||||||
|
const y = candidate.y;
|
||||||
|
|
||||||
|
// 计算当前图片的实际占用区域(包含padding)
|
||||||
|
const currentRect = {
|
||||||
|
left: x - imagePadding,
|
||||||
|
top: y - imagePadding,
|
||||||
|
right: x + imageSize + imagePadding,
|
||||||
|
bottom: y + imageSize + imagePadding,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确保不超出内容区域的范围
|
||||||
|
if (
|
||||||
|
currentRect.left < 0 ||
|
||||||
|
currentRect.top < 0 ||
|
||||||
|
currentRect.right > contentWidth ||
|
||||||
|
currentRect.bottom > contentHeight
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否与已放置的图片重叠
|
||||||
|
let overlaps = false;
|
||||||
|
for (const placedRect of placedRects) {
|
||||||
|
if (isRectOverlap(currentRect, placedRect)) {
|
||||||
|
overlaps = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overlaps) {
|
||||||
|
// 存储实际占用区域
|
||||||
|
placedRects.push({
|
||||||
|
left: x - imagePadding,
|
||||||
|
top: y - imagePadding,
|
||||||
|
right: x + imageSize + imagePadding,
|
||||||
|
bottom: y + imageSize + imagePadding,
|
||||||
|
});
|
||||||
|
|
||||||
|
positions.push({ x, y });
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果网格位置都不行,尝试完全随机的位置
|
||||||
|
if (!found) {
|
||||||
|
let attempts = 0;
|
||||||
|
const maxAttempts = 1000;
|
||||||
|
|
||||||
|
while (attempts < maxAttempts) {
|
||||||
|
// 生成随机位置,添加一些随机偏移
|
||||||
|
const randomOffsetX =
|
||||||
|
(Math.random() - 0.5) * cellWidth * 0.5;
|
||||||
|
const randomOffsetY =
|
||||||
|
(Math.random() - 0.5) * cellHeight * 0.5;
|
||||||
|
const baseX = Math.random() * maxX;
|
||||||
|
const baseY = Math.random() * maxY;
|
||||||
|
const x = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(maxX, baseX + randomOffsetX),
|
||||||
|
);
|
||||||
|
const y = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(maxY, baseY + randomOffsetY),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 计算当前图片的实际占用区域(包含padding)
|
||||||
|
const currentRect = {
|
||||||
|
left: x - imagePadding,
|
||||||
|
top: y - imagePadding,
|
||||||
|
right: x + imageSize + imagePadding,
|
||||||
|
bottom: y + imageSize + imagePadding,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 确保不超出内容区域的范围
|
||||||
|
if (
|
||||||
|
currentRect.left < 0 ||
|
||||||
|
currentRect.top < 0 ||
|
||||||
|
currentRect.right > contentWidth ||
|
||||||
|
currentRect.bottom > contentHeight
|
||||||
|
) {
|
||||||
|
attempts++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否与已放置的图片重叠
|
||||||
|
let overlaps = false;
|
||||||
|
for (const placedRect of placedRects) {
|
||||||
|
if (isRectOverlap(currentRect, placedRect)) {
|
||||||
|
overlaps = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!overlaps) {
|
||||||
|
// 存储实际占用区域
|
||||||
|
placedRects.push({
|
||||||
|
left: x - imagePadding,
|
||||||
|
top: y - imagePadding,
|
||||||
|
right: x + imageSize + imagePadding,
|
||||||
|
bottom: y + imageSize + imagePadding,
|
||||||
|
});
|
||||||
|
|
||||||
|
positions.push({ x, y });
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后的后备方案:使用网格布局,但添加随机偏移
|
||||||
|
if (!found) {
|
||||||
|
const gridCols = 3;
|
||||||
|
const gridRows = 2;
|
||||||
|
const gridCellWidth = maxX / Math.max(1, gridCols - 1);
|
||||||
|
const gridCellHeight = maxY / Math.max(1, gridRows - 1);
|
||||||
|
const baseX = (i % gridCols) * gridCellWidth;
|
||||||
|
const baseY = Math.floor(i / gridCols) * gridCellHeight;
|
||||||
|
// 添加随机偏移,但确保不重叠
|
||||||
|
const offsetX = (Math.random() - 0.5) * gridCellWidth * 0.3;
|
||||||
|
const offsetY = (Math.random() - 0.5) * gridCellHeight * 0.3;
|
||||||
|
const x = Math.max(0, Math.min(maxX, baseX + offsetX));
|
||||||
|
const y = Math.max(0, Math.min(maxY, baseY + offsetY));
|
||||||
|
positions.push({ x, y });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最后再次随机打乱位置顺序,增加随机性
|
||||||
|
return positions.sort(() => Math.random() - 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制箭头
|
||||||
|
*/
|
||||||
|
private drawArrow(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
x1: number,
|
||||||
|
y1: number,
|
||||||
|
x2: number,
|
||||||
|
y2: number,
|
||||||
|
) {
|
||||||
|
ctx.strokeStyle = '#FF69B4'; // 粉色
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.setLineDash([]); // 实线
|
||||||
|
|
||||||
|
// 绘制箭头线
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x1, y1);
|
||||||
|
ctx.lineTo(x2, y2);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 绘制箭头头部
|
||||||
|
const angle = Math.atan2(y2 - y1, x2 - x1);
|
||||||
|
const arrowLength = 8;
|
||||||
|
const arrowAngle = Math.PI / 6; // 30度
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x2, y2);
|
||||||
|
ctx.lineTo(
|
||||||
|
x2 - arrowLength * Math.cos(angle - arrowAngle),
|
||||||
|
y2 - arrowLength * Math.sin(angle - arrowAngle),
|
||||||
|
);
|
||||||
|
ctx.moveTo(x2, y2);
|
||||||
|
ctx.lineTo(
|
||||||
|
x2 - arrowLength * Math.cos(angle + arrowAngle),
|
||||||
|
y2 - arrowLength * Math.sin(angle + arrowAngle),
|
||||||
|
);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制内容区域
|
||||||
|
*/
|
||||||
|
private async drawContent(params: {
|
||||||
|
canvas: WechatMiniprogram.Canvas;
|
||||||
|
ctx: RenderingContext;
|
||||||
|
matchData: MatchConnectData;
|
||||||
|
canvasWidth: number;
|
||||||
|
startY: number;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { ctx, matchData, canvasWidth } = params;
|
||||||
|
let { startY } = params;
|
||||||
|
|
||||||
|
startY = startY + 25;
|
||||||
|
|
||||||
|
const margin = 40; // 左右边距
|
||||||
|
const imageSize = 50; // 图片大小
|
||||||
|
const arrowLength = 30; // 箭头长度
|
||||||
|
const arrowSpacing = 8; // 箭头左右间距
|
||||||
|
|
||||||
|
// 计算参考序列的总宽度
|
||||||
|
// 总宽度 = 所有图片宽度 + 所有箭头长度 + 所有箭头间距(左右各8px)
|
||||||
|
const arrowCount = matchData.referenceSequence.length - 1;
|
||||||
|
const referenceWidth =
|
||||||
|
matchData.referenceSequence.length * imageSize +
|
||||||
|
arrowCount * arrowLength +
|
||||||
|
arrowCount * arrowSpacing * 2; // 每个箭头左右各8px
|
||||||
|
|
||||||
|
// 绘制参考序列(上面部分)- 居中显示
|
||||||
|
const referenceStartX =
|
||||||
|
margin + (canvasWidth - margin * 2 - referenceWidth) / 2;
|
||||||
|
const referenceY = startY;
|
||||||
|
|
||||||
|
// 绘制参考序列的图片和箭头
|
||||||
|
let currentX = referenceStartX;
|
||||||
|
for (let i = 0; i < matchData.referenceSequence.length; i++) {
|
||||||
|
const imageIndex = matchData.referenceSequence[i];
|
||||||
|
|
||||||
|
// 绘制图片
|
||||||
|
try {
|
||||||
|
const imagePath = `/focusPages/shared/aeests/material/${imageIndex}.png`;
|
||||||
|
const image = await getImage(this.canvas, imagePath);
|
||||||
|
ctx.drawImage(
|
||||||
|
image,
|
||||||
|
currentX,
|
||||||
|
referenceY,
|
||||||
|
imageSize,
|
||||||
|
imageSize,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`加载参考图片失败: ${imageIndex}`, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果不是最后一张,绘制箭头
|
||||||
|
if (i < matchData.referenceSequence.length - 1) {
|
||||||
|
// 箭头左边间距8px
|
||||||
|
const arrowStartX = currentX + imageSize + arrowSpacing;
|
||||||
|
const arrowEndX = arrowStartX + arrowLength;
|
||||||
|
const arrowY = referenceY + imageSize / 2;
|
||||||
|
this.drawArrow(ctx, arrowStartX, arrowY, arrowEndX, arrowY);
|
||||||
|
// 箭头右边间距8px,然后开始下一张图片
|
||||||
|
currentX = arrowEndX + arrowSpacing;
|
||||||
|
} else {
|
||||||
|
currentX += imageSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制虚线分割线
|
||||||
|
const dividerY = referenceY + imageSize + 30;
|
||||||
|
this.drawDashedDivider(dividerY, margin);
|
||||||
|
|
||||||
|
// 绘制下面的6个框(3行2列)
|
||||||
|
const boxStartY = dividerY + 30;
|
||||||
|
const boxWidth = (canvasWidth - margin * 2 - 20) / 2; // 减去列间距
|
||||||
|
const boxHeight = 165; // 框的高度
|
||||||
|
const boxSpacingX = 20; // 列间距
|
||||||
|
const boxSpacingY = 30; // 行间距
|
||||||
|
const boxPadding = 10; // 框内边距
|
||||||
|
const boxImageSize = 35; // 框内图片大小
|
||||||
|
const imagePadding = 20; // 每张图片的内边距(padding)
|
||||||
|
|
||||||
|
for (let row = 0; row < 3; row++) {
|
||||||
|
for (let col = 0; col < 2; col++) {
|
||||||
|
const boxIndex = row * 2 + col;
|
||||||
|
const box = matchData.boxes[boxIndex];
|
||||||
|
if (!box || !box.imageIndices) continue;
|
||||||
|
|
||||||
|
const boxX = margin + col * (boxWidth + boxSpacingX);
|
||||||
|
const boxY = boxStartY + row * (boxHeight + boxSpacingY);
|
||||||
|
|
||||||
|
// 绘制框(圆角实线)
|
||||||
|
this.drawRoundedRect(boxX, boxY, boxWidth, boxHeight, {
|
||||||
|
isDashed: false,
|
||||||
|
radius: 16,
|
||||||
|
color: '#000',
|
||||||
|
lineWidth: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算框内可用的内容区域
|
||||||
|
const contentWidth = boxWidth - boxPadding * 2;
|
||||||
|
const contentHeight = boxHeight - boxPadding * 2;
|
||||||
|
|
||||||
|
// 为这个框的图片随机分配位置,确保图片+padding不重叠
|
||||||
|
const imagePositions = this.generateNonOverlappingPositions(
|
||||||
|
box.imageIndices.length,
|
||||||
|
contentWidth,
|
||||||
|
contentHeight,
|
||||||
|
boxImageSize,
|
||||||
|
imagePadding,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 绘制框内的图片
|
||||||
|
for (let i = 0; i < box.imageIndices.length; i++) {
|
||||||
|
const imageIndex = box.imageIndices[i];
|
||||||
|
const position = imagePositions[i];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const imagePath = `/focusPages/shared/aeests/material/${imageIndex}.png`;
|
||||||
|
const image = await getImage(this.canvas, imagePath);
|
||||||
|
|
||||||
|
// 计算图片位置
|
||||||
|
const imageX = boxX + boxPadding + position.x;
|
||||||
|
const imageY = boxY + boxPadding + position.y;
|
||||||
|
|
||||||
|
ctx.drawImage(
|
||||||
|
image,
|
||||||
|
imageX,
|
||||||
|
imageY,
|
||||||
|
boxImageSize,
|
||||||
|
boxImageSize,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`加载框内图片失败: ${imageIndex}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MatchConnectDraw;
|
||||||
@@ -217,4 +217,69 @@ export class BaseDrawService {
|
|||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制圆角矩形框(逻辑像素,尺寸除以3)
|
||||||
|
* @param x 框的X坐标
|
||||||
|
* @param y 框的Y坐标
|
||||||
|
* @param width 框的宽度
|
||||||
|
* @param height 框的高度
|
||||||
|
* @param options 可选参数
|
||||||
|
* @param options.isDashed 是否为虚线,默认为false(实线)
|
||||||
|
* @param options.radius 圆角半径,默认为10
|
||||||
|
* @param options.color 线条颜色,默认为'#000'
|
||||||
|
* @param options.lineWidth 线条宽度,默认为1
|
||||||
|
*/
|
||||||
|
drawRoundedRect(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
options?: {
|
||||||
|
isDashed?: boolean;
|
||||||
|
radius?: number;
|
||||||
|
color?: string;
|
||||||
|
lineWidth?: number;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
|
const { ctx } = this;
|
||||||
|
const {
|
||||||
|
isDashed = false,
|
||||||
|
radius = 10,
|
||||||
|
color = '#000',
|
||||||
|
lineWidth = 1,
|
||||||
|
} = options || {};
|
||||||
|
|
||||||
|
ctx.strokeStyle = color;
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
|
||||||
|
// 设置虚线或实线
|
||||||
|
if (isDashed) {
|
||||||
|
ctx.setLineDash([4, 4]); // 虚线
|
||||||
|
} else {
|
||||||
|
ctx.setLineDash([]); // 实线
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制圆角矩形
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + radius, y);
|
||||||
|
ctx.lineTo(x + width - radius, y);
|
||||||
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
||||||
|
ctx.lineTo(x + width, y + height - radius);
|
||||||
|
ctx.quadraticCurveTo(
|
||||||
|
x + width,
|
||||||
|
y + height,
|
||||||
|
x + width - radius,
|
||||||
|
y + height,
|
||||||
|
);
|
||||||
|
ctx.lineTo(x + radius, y + height);
|
||||||
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
||||||
|
ctx.lineTo(x, y + radius);
|
||||||
|
ctx.quadraticCurveTo(x, y, x + radius, y);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 重置为实线(避免影响后续绘制)
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,9 @@ export function recordShareSuccess(): void {
|
|||||||
* @returns true表示需要显示分享引导,false表示不需要
|
* @returns true表示需要显示分享引导,false表示不需要
|
||||||
*/
|
*/
|
||||||
export function shouldShowShareGuide(): boolean {
|
export function shouldShowShareGuide(): boolean {
|
||||||
// TODO: 开启分享引导
|
|
||||||
return false;
|
|
||||||
// 如果今天已经分享过,不需要显示引导
|
// 如果今天已经分享过,不需要显示引导
|
||||||
if (hasSharedToday()) {
|
const env = getApp().globalData.env;
|
||||||
|
if (env === 'develop' || hasSharedToday()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// 如果今天已经下载过,需要显示引导
|
// 如果今天已经下载过,需要显示引导
|
||||||
|
|||||||
@@ -23,12 +23,19 @@
|
|||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"name": "focusPages/matchConnect/matchConnect",
|
||||||
|
"pathName": "focusPages/matchConnect/matchConnect",
|
||||||
|
"query": "id=match-connect",
|
||||||
|
"scene": null,
|
||||||
|
"launchMode": "default"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/colorPattern/colorPattern",
|
"name": "focusPages/colorPattern/colorPattern",
|
||||||
"pathName": "focusPages/colorPattern/colorPattern",
|
"pathName": "focusPages/colorPattern/colorPattern",
|
||||||
"query": "id=color-pattern",
|
"query": "id=color-pattern",
|
||||||
"scene": null,
|
"launchMode": "default",
|
||||||
"launchMode": "default"
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/shapeSymbol/shapeSymbol",
|
"name": "focusPages/shapeSymbol/shapeSymbol",
|
||||||
|
|||||||
Reference in New Issue
Block a user