feat:V2.5.4增加线条识别
This commit is contained in:
+12
-3
@@ -30,7 +30,8 @@
|
|||||||
"positionColoring/positionColoring",
|
"positionColoring/positionColoring",
|
||||||
"shapeSymbol/shapeSymbol",
|
"shapeSymbol/shapeSymbol",
|
||||||
"colorPattern/colorPattern",
|
"colorPattern/colorPattern",
|
||||||
"matchConnect/matchConnect"
|
"matchConnect/matchConnect",
|
||||||
|
"lineRecognition/lineRecognition"
|
||||||
],
|
],
|
||||||
"independent": false
|
"independent": false
|
||||||
}
|
}
|
||||||
@@ -38,11 +39,19 @@
|
|||||||
"preloadRule": {
|
"preloadRule": {
|
||||||
"pages/index/index": {
|
"pages/index/index": {
|
||||||
"network": "all",
|
"network": "all",
|
||||||
"packages": ["mathPages"]
|
"packages": ["mathPages", "focusPages"]
|
||||||
},
|
},
|
||||||
"pages/mathIndex/mathIndex": {
|
"pages/mathIndex/mathIndex": {
|
||||||
"network": "all",
|
"network": "all",
|
||||||
"packages": ["mathPages"]
|
"packages": ["mathPages", "focusPages"]
|
||||||
|
},
|
||||||
|
"pages/focusIndex/focusIndex": {
|
||||||
|
"network": "all",
|
||||||
|
"packages": ["mathPages", "focusPages"]
|
||||||
|
},
|
||||||
|
"pages/copyBook/copyBook": {
|
||||||
|
"network": "all",
|
||||||
|
"packages": ["mathPages", "focusPages"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"window": {},
|
"window": {},
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
.tip-content {
|
.tip-content {
|
||||||
background: rgba(0, 0, 0, 0.8);
|
background: rgba(0, 0, 0, 0.8);
|
||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
padding: 24rpx 32rpx;
|
padding: 22rpx 30rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 24rpx;
|
gap: 24rpx;
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
.tip-arrow {
|
.tip-arrow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -15rpx;
|
top: -16rpx;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
border-left: 12rpx solid transparent;
|
border-left: 12rpx solid transparent;
|
||||||
|
|||||||
@@ -102,14 +102,14 @@ export const NUMBER_COLORS: Record<number, string> = {
|
|||||||
10: '#FC9B6C', // 柔和橙红色
|
10: '#FC9B6C', // 柔和橙红色
|
||||||
11: '#BC8F71', // 柔和棕色
|
11: '#BC8F71', // 柔和棕色
|
||||||
12: '#666666', // 柔和黑灰
|
12: '#666666', // 柔和黑灰
|
||||||
13: '#FF2222', // 鲜艳大红
|
13: '#FF6F61', // 童趣珊瑚橘
|
||||||
14: '#FF9900', // 鲜艳橙色
|
14: '#FFCB05', // 童趣明亮黄
|
||||||
15: '#FFD700', // 鲜艳金黄
|
15: '#5AE2B1', // 童趣薄荷绿
|
||||||
16: '#32CD32', // 鲜艳亮绿
|
16: '#FF85A1', // 童趣樱花粉
|
||||||
17: '#21C5E4', // 鲜艳青色
|
17: '#8CBAFF', // 童趣天真蓝
|
||||||
18: '#3777FF', // 鲜艳蓝色
|
18: '#FFD6E0', // 童趣泡泡粉
|
||||||
19: '#9D27E3', // 鲜艳紫色
|
19: '#57DAFF', // 童趣冰雪蓝
|
||||||
20: '#FF40A0', // 鲜艳玫粉
|
20: '#FFE066', // 童趣香蕉黄
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -48,6 +48,14 @@ export const FOCUS_FUNCTION_TYPES: FocusFunctionType[] = [
|
|||||||
desc: '快来根据物品连一连吧!',
|
desc: '快来根据物品连一连吧!',
|
||||||
icon: '🔗',
|
icon: '🔗',
|
||||||
},
|
},
|
||||||
|
// 线条识别
|
||||||
|
{
|
||||||
|
id: 'line-recognition',
|
||||||
|
page: 'lineRecognition',
|
||||||
|
title: '线条识别',
|
||||||
|
desc: '认识不同线条,画出颜色对应的线条',
|
||||||
|
icon: '📏',
|
||||||
|
},
|
||||||
// 格子仿画
|
// 格子仿画
|
||||||
{
|
{
|
||||||
id: 'grid-drawing-3x3',
|
id: 'grid-drawing-3x3',
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
随机生成
|
随机生成
|
||||||
</toy-button>
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
bind:select="onSelectType"
|
bind:select="onSelectType"
|
||||||
bind:random="onRandom" />
|
bind:random="onRandom" />
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
<math-bottom-buttons
|
<math-bottom-buttons
|
||||||
|
|||||||
@@ -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,146 @@
|
|||||||
|
import LineRecognitionDraw from '../shared/service/lineRecognitionDraw';
|
||||||
|
import { createFocusPage } from '../shared/common/focusPageMixin';
|
||||||
|
import {
|
||||||
|
LineRecognitionData,
|
||||||
|
LineType,
|
||||||
|
LineTypeConfig,
|
||||||
|
} from '../shared/service/lineRecognitionDraw';
|
||||||
|
import { NUMBER_COLORS } from '../../constants/colors';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广告位ID:adunit-89cff2de998f1146
|
||||||
|
请返回广告位列表,获取广告位代码,将代码插入小程序页面中适合位置,发布后即展现对应广告位。
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 线条类型配置
|
||||||
|
const LINE_TYPES: Array<{ type: LineType; name: string }> = [
|
||||||
|
{ type: 'straight', name: '直线' },
|
||||||
|
{ type: 'dashed', name: '虚线' },
|
||||||
|
{ type: 'wavy', name: '波浪线' },
|
||||||
|
{ type: 'zigzag', name: '锯齿线' },
|
||||||
|
{ type: 'spiral', name: '电话线' },
|
||||||
|
];
|
||||||
|
|
||||||
|
createFocusPage({
|
||||||
|
canvas: null as Canvas | null,
|
||||||
|
ctx: null as RenderingContext | null,
|
||||||
|
boxHeight: 0,
|
||||||
|
boxWidth: 0,
|
||||||
|
drawService: null as LineRecognitionDraw | null,
|
||||||
|
lineData: null as LineRecognitionData | null,
|
||||||
|
|
||||||
|
data: {
|
||||||
|
pageTitle: '线条识别',
|
||||||
|
functionId: '',
|
||||||
|
hasContent: false,
|
||||||
|
showShareDialog: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
onLoad(options: { id?: string }) {
|
||||||
|
const functionId = options.id || 'line-recognition';
|
||||||
|
this.setData({
|
||||||
|
functionId,
|
||||||
|
});
|
||||||
|
this.initPageInfo(functionId, '线条识别');
|
||||||
|
},
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
this.initCanvas({
|
||||||
|
createDrawService: (
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) => {
|
||||||
|
return new LineRecognitionDraw(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.lineData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.drawService.draw(this.lineData);
|
||||||
|
this.setData({ hasContent: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('绘制失败:', error);
|
||||||
|
this.setData({ hasContent: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 随机生成
|
||||||
|
*/
|
||||||
|
onRandom() {
|
||||||
|
// 从NUMBER_COLORS中随机选择5种颜色(排除11和12:柔和棕色和柔和黑灰)
|
||||||
|
const colorKeys = Object.keys(NUMBER_COLORS)
|
||||||
|
.map(Number)
|
||||||
|
.filter((key) => key !== 11 && key !== 12); // 排除柔和棕色和柔和黑灰
|
||||||
|
const shuffledColors = [...colorKeys].sort(() => Math.random() - 0.5);
|
||||||
|
const selectedColorKeys = shuffledColors.slice(0, 5);
|
||||||
|
|
||||||
|
// 创建5个线条配置(随机排列)
|
||||||
|
const shuffledLineTypes = [...LINE_TYPES].sort(
|
||||||
|
() => Math.random() - 0.5,
|
||||||
|
);
|
||||||
|
const referenceLines: LineTypeConfig[] = shuffledLineTypes.map(
|
||||||
|
(lineType, index) => ({
|
||||||
|
type: lineType.type,
|
||||||
|
name: lineType.name,
|
||||||
|
color: NUMBER_COLORS[selectedColorKeys[index]],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 创建练习区域的16个色块(2列×8行)
|
||||||
|
const practiceBlocks: Array<{
|
||||||
|
color: string;
|
||||||
|
lineType: LineType;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
// 为每行生成色块
|
||||||
|
for (let row = 0; row < 8; row++) {
|
||||||
|
for (let col = 0; col < 2; col++) {
|
||||||
|
// 随机选择一个线条类型和颜色
|
||||||
|
const randomLineIndex = Math.floor(
|
||||||
|
Math.random() * referenceLines.length,
|
||||||
|
);
|
||||||
|
const selectedLine = referenceLines[randomLineIndex];
|
||||||
|
|
||||||
|
// 生成随机错落感(X轴偏移:-20到20,Y轴偏移:-8到8,确保不重叠)
|
||||||
|
// 行高55,色块35,所以Y轴偏移最大8,确保不重叠
|
||||||
|
const xOffset = (Math.random() - 0.5) * 40;
|
||||||
|
const yOffset = (Math.random() - 0.5) * 16;
|
||||||
|
|
||||||
|
practiceBlocks.push({
|
||||||
|
color: selectedLine.color,
|
||||||
|
lineType: selectedLine.type,
|
||||||
|
x: xOffset,
|
||||||
|
y: yOffset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lineData = {
|
||||||
|
referenceLines,
|
||||||
|
practiceBlocks,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.drawCanvas();
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<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>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
|
</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';
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
随机生成
|
随机生成
|
||||||
</toy-button>
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
随机生成
|
随机生成
|
||||||
</toy-button>
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
class="canvas-content"
|
class="canvas-content"
|
||||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
随机生成
|
随机生成
|
||||||
</toy-button>
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-89cff2de998f1146"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,430 @@
|
|||||||
|
/**
|
||||||
|
* 线条识别绘制服务
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BaseDrawService } from '../../../service/baseDraw';
|
||||||
|
import { getImage } from '../../../utils/index';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线条类型
|
||||||
|
*/
|
||||||
|
export type LineType = 'straight' | 'dashed' | 'wavy' | 'zigzag' | 'spiral';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线条类型配置
|
||||||
|
*/
|
||||||
|
export interface LineTypeConfig {
|
||||||
|
type: LineType;
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线条识别数据
|
||||||
|
*/
|
||||||
|
export interface LineRecognitionData {
|
||||||
|
/** 预览区域的5个线条配置(随机排列) */
|
||||||
|
referenceLines: LineTypeConfig[];
|
||||||
|
/** 练习区域的16个色块(2列×8行) */
|
||||||
|
practiceBlocks: Array<{
|
||||||
|
color: string;
|
||||||
|
lineType: LineType;
|
||||||
|
x: number; // 相对于列的X偏移(错落感)
|
||||||
|
y: number; // 相对于行的Y偏移(错落感)
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 线条识别绘制服务
|
||||||
|
*/
|
||||||
|
class LineRecognitionDraw extends BaseDrawService {
|
||||||
|
lineData: LineRecognitionData | null = null;
|
||||||
|
lineStrokeStyle: string = '#222'; // 线条颜色
|
||||||
|
lineWidth: number = 2; // 线条宽度
|
||||||
|
spiralLineImage: any = null; // 电话线图片对象
|
||||||
|
private spiralLineImagePromise: Promise<any> | null = null; // 图片加载Promise
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
canvas: Canvas,
|
||||||
|
ctx: RenderingContext,
|
||||||
|
options?: Record<string, any>,
|
||||||
|
) {
|
||||||
|
super(canvas, ctx, options);
|
||||||
|
// 在构造函数中启动图片加载
|
||||||
|
this.loadSpiralLineImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加载电话线图片
|
||||||
|
*/
|
||||||
|
private loadSpiralLineImage() {
|
||||||
|
if (!this.spiralLineImagePromise) {
|
||||||
|
const imagePath = `/focusPages/shared/aeests/imgs/spiral-line.png`;
|
||||||
|
this.spiralLineImagePromise = getImage(this.canvas, imagePath).then(
|
||||||
|
(image) => {
|
||||||
|
this.spiralLineImage = image;
|
||||||
|
return image;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.spiralLineImagePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制线条识别内容
|
||||||
|
*/
|
||||||
|
async draw(lineData: LineRecognitionData) {
|
||||||
|
if (!lineData || !lineData.referenceLines || !lineData.practiceBlocks) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保电话线图片已加载
|
||||||
|
if (!this.spiralLineImage) {
|
||||||
|
await this.loadSpiralLineImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPrintConfig();
|
||||||
|
this.lineData = lineData;
|
||||||
|
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,
|
||||||
|
lineData: this.lineData,
|
||||||
|
canvasWidth: this.canvasWidth,
|
||||||
|
startY: this.currentY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制直线
|
||||||
|
*/
|
||||||
|
private drawStraightLine(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
) {
|
||||||
|
ctx.strokeStyle = this.lineStrokeStyle;
|
||||||
|
ctx.lineWidth = this.lineWidth;
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
ctx.lineTo(x + width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制虚线
|
||||||
|
*/
|
||||||
|
private drawDashedLine(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
) {
|
||||||
|
ctx.strokeStyle = this.lineStrokeStyle;
|
||||||
|
ctx.lineWidth = this.lineWidth;
|
||||||
|
ctx.setLineDash([6, 4]);
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x, y);
|
||||||
|
ctx.lineTo(x + width, y);
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制波浪线
|
||||||
|
*/
|
||||||
|
private drawWavyLine(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
) {
|
||||||
|
ctx.strokeStyle = this.lineStrokeStyle;
|
||||||
|
ctx.lineWidth = this.lineWidth;
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
ctx.beginPath();
|
||||||
|
const radius = 8; // 波浪半径
|
||||||
|
const turns = 5; // 波浪圈数
|
||||||
|
const points = 120; // 点数
|
||||||
|
const startX = x;
|
||||||
|
const endX = x + width;
|
||||||
|
|
||||||
|
ctx.moveTo(startX, y);
|
||||||
|
for (let i = 0; i <= points; i++) {
|
||||||
|
const progress = i / points;
|
||||||
|
const currentX = startX + (endX - startX) * progress;
|
||||||
|
const angle = progress * turns * Math.PI * 2;
|
||||||
|
const currentY = y + Math.sin(angle) * radius;
|
||||||
|
ctx.lineTo(currentX, currentY);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制锯齿线
|
||||||
|
*/
|
||||||
|
private drawZigzagLine(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
) {
|
||||||
|
ctx.strokeStyle = this.lineStrokeStyle;
|
||||||
|
ctx.lineWidth = this.lineWidth;
|
||||||
|
ctx.setLineDash([]); // 实线
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
const segmentLength = 12; // 每段长度
|
||||||
|
const amplitude = 7; // 波峰/波谷高度
|
||||||
|
|
||||||
|
// 第一个点为波峰顶点
|
||||||
|
let currentX = x;
|
||||||
|
let currentY = y - amplitude;
|
||||||
|
ctx.moveTo(currentX, currentY);
|
||||||
|
|
||||||
|
// 计算一共能画多少完整段,两点之间斜线斜率为±1(45度)
|
||||||
|
const nSegments = Math.ceil(width / segmentLength);
|
||||||
|
|
||||||
|
for (let i = 0; i < nSegments; i++) {
|
||||||
|
// 下一点横坐标
|
||||||
|
let nextX = currentX + segmentLength;
|
||||||
|
// 最后一个点不能超过终点,且如正好到终点,高度需计算
|
||||||
|
if (nextX > x + width) {
|
||||||
|
nextX = x + width;
|
||||||
|
}
|
||||||
|
// 交替:偶数索引连向波谷,奇数索引连向波峰
|
||||||
|
let nextY;
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
nextY = y + amplitude; // 波谷
|
||||||
|
} else {
|
||||||
|
nextY = y - amplitude; // 波峰
|
||||||
|
}
|
||||||
|
// 如果已经到最后一段,最后一个点应当总是在波谷
|
||||||
|
if (i === nSegments - 1) {
|
||||||
|
nextY = y + amplitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.lineTo(nextX, nextY);
|
||||||
|
|
||||||
|
currentX = nextX;
|
||||||
|
currentY = nextY;
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制电话线(使用图片)
|
||||||
|
*/
|
||||||
|
private drawSpiralLine(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
) {
|
||||||
|
// 使用预加载的图片对象
|
||||||
|
if (!this.spiralLineImage) {
|
||||||
|
console.warn('电话线图片未加载完成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算图片高度,保持原始宽高比,或者使用固定高度
|
||||||
|
const imageHeight = 22; // 固定高度,与线条宽度匹配
|
||||||
|
const drawY = y - imageHeight / 2; // 垂直居中
|
||||||
|
|
||||||
|
// 绘制图片
|
||||||
|
ctx.drawImage(this.spiralLineImage, x, drawY, width, imageHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制线条
|
||||||
|
*/
|
||||||
|
private drawLine(
|
||||||
|
ctx: RenderingContext,
|
||||||
|
lineType: LineType,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
width: number,
|
||||||
|
) {
|
||||||
|
switch (lineType) {
|
||||||
|
case 'straight':
|
||||||
|
this.drawStraightLine(ctx, x, y, width);
|
||||||
|
break;
|
||||||
|
case 'dashed':
|
||||||
|
this.drawDashedLine(ctx, x, y, width);
|
||||||
|
break;
|
||||||
|
case 'wavy':
|
||||||
|
this.drawWavyLine(ctx, x, y, width);
|
||||||
|
break;
|
||||||
|
case 'zigzag':
|
||||||
|
this.drawZigzagLine(ctx, x, y, width);
|
||||||
|
break;
|
||||||
|
case 'spiral':
|
||||||
|
this.drawSpiralLine(ctx, x, y, width);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绘制内容区域
|
||||||
|
*/
|
||||||
|
private async drawContent(params: {
|
||||||
|
canvas: WechatMiniprogram.Canvas;
|
||||||
|
ctx: RenderingContext;
|
||||||
|
lineData: LineRecognitionData;
|
||||||
|
canvasWidth: number;
|
||||||
|
startY: number;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { ctx, lineData, canvasWidth } = params;
|
||||||
|
let { startY } = params;
|
||||||
|
|
||||||
|
startY = startY + 25;
|
||||||
|
|
||||||
|
const margin = 40; // 左右边距
|
||||||
|
const referenceBlockWidth = 140; // 预览区域色块宽度(加长)
|
||||||
|
const referenceBlockHeight = 35; // 预览区域色块高度(缩减)
|
||||||
|
const referenceBlockSpacing = 30; // 色块间距
|
||||||
|
const textHeight = 30; // 文字高度
|
||||||
|
const lineMargin = 15; // 线条距离色块边缘的距离
|
||||||
|
const rowSpacing = 15; // 行间距
|
||||||
|
|
||||||
|
// 绘制预览区域(两行:第一行2个,第二行3个)
|
||||||
|
const referenceStartY = startY;
|
||||||
|
|
||||||
|
// 第一行:2个色块
|
||||||
|
const firstRowWidth = 2 * referenceBlockWidth + referenceBlockSpacing;
|
||||||
|
const firstRowStartX =
|
||||||
|
margin + (canvasWidth - margin * 2 - firstRowWidth) / 2;
|
||||||
|
|
||||||
|
for (let i = 0; i < 2; i++) {
|
||||||
|
const config = lineData.referenceLines[i];
|
||||||
|
const blockX =
|
||||||
|
firstRowStartX +
|
||||||
|
i * (referenceBlockWidth + referenceBlockSpacing);
|
||||||
|
const blockY = referenceStartY;
|
||||||
|
|
||||||
|
// 绘制色块(无边框)
|
||||||
|
ctx.fillStyle = config.color;
|
||||||
|
ctx.fillRect(
|
||||||
|
blockX,
|
||||||
|
blockY,
|
||||||
|
referenceBlockWidth,
|
||||||
|
referenceBlockHeight,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 绘制线条
|
||||||
|
const lineX = blockX + lineMargin;
|
||||||
|
const lineY = blockY + referenceBlockHeight / 2;
|
||||||
|
const lineWidth = referenceBlockWidth - lineMargin * 2;
|
||||||
|
this.drawLine(ctx, config.type, lineX, lineY, lineWidth);
|
||||||
|
|
||||||
|
// 绘制文字标签
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.font = 'bold 16px "Microsoft Yahei"';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.fillText(
|
||||||
|
config.name,
|
||||||
|
blockX + referenceBlockWidth / 2,
|
||||||
|
blockY + referenceBlockHeight + 5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二行:3个色块
|
||||||
|
const secondRowWidth =
|
||||||
|
3 * referenceBlockWidth + 2 * referenceBlockSpacing;
|
||||||
|
const secondRowStartX =
|
||||||
|
margin + (canvasWidth - margin * 2 - secondRowWidth) / 2;
|
||||||
|
const secondRowY =
|
||||||
|
referenceStartY + referenceBlockHeight + textHeight + rowSpacing;
|
||||||
|
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const config = lineData.referenceLines[i + 2];
|
||||||
|
const blockX =
|
||||||
|
secondRowStartX +
|
||||||
|
i * (referenceBlockWidth + referenceBlockSpacing);
|
||||||
|
const blockY = secondRowY;
|
||||||
|
|
||||||
|
// 绘制色块(无边框)
|
||||||
|
ctx.fillStyle = config.color;
|
||||||
|
ctx.fillRect(
|
||||||
|
blockX,
|
||||||
|
blockY,
|
||||||
|
referenceBlockWidth,
|
||||||
|
referenceBlockHeight,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 绘制线条
|
||||||
|
const lineX = blockX + lineMargin;
|
||||||
|
const lineY = blockY + referenceBlockHeight / 2;
|
||||||
|
const lineWidth = referenceBlockWidth - lineMargin * 2;
|
||||||
|
this.drawLine(ctx, config.type, lineX, lineY, lineWidth);
|
||||||
|
|
||||||
|
// 绘制文字标签
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.font = 'bold 16px "Microsoft Yahei"';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
ctx.fillText(
|
||||||
|
config.name,
|
||||||
|
blockX + referenceBlockWidth / 2,
|
||||||
|
blockY + referenceBlockHeight + 5,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制虚线分割线
|
||||||
|
const dividerY = secondRowY + referenceBlockHeight + textHeight + 30;
|
||||||
|
this.drawDashedDivider(dividerY, margin);
|
||||||
|
|
||||||
|
const practiceMargin = 60;
|
||||||
|
// 绘制练习区域(2列×8行)
|
||||||
|
const practiceStartY = dividerY + 30;
|
||||||
|
// 练习区域色块尺寸与预览区域一致
|
||||||
|
const practiceBlockWidth = referenceBlockWidth + 50; // 140
|
||||||
|
const practiceBlockHeight = referenceBlockHeight + 5; // 35
|
||||||
|
const practiceRowHeight = 60; // 每行高度(增加间距,确保不重叠)
|
||||||
|
const practiceColWidth = (canvasWidth - practiceMargin * 2 - 30) / 2; // 列宽度(减去列间距)
|
||||||
|
const practiceColSpacing = 30; // 列间距
|
||||||
|
|
||||||
|
for (let row = 0; row < 8; row++) {
|
||||||
|
for (let col = 0; col < 2; col++) {
|
||||||
|
const blockIndex = row * 2 + col;
|
||||||
|
const block = lineData.practiceBlocks[blockIndex];
|
||||||
|
if (!block) continue;
|
||||||
|
|
||||||
|
// 计算基础位置(考虑错落感)
|
||||||
|
const baseX =
|
||||||
|
practiceMargin +
|
||||||
|
col * (practiceColWidth + practiceColSpacing);
|
||||||
|
const baseY = practiceStartY + row * practiceRowHeight;
|
||||||
|
|
||||||
|
// 添加随机错落感(限制范围,确保不重叠)
|
||||||
|
const blockX = baseX + block.x;
|
||||||
|
const blockY = baseY + block.y;
|
||||||
|
|
||||||
|
// 绘制色块(无边框)
|
||||||
|
ctx.fillStyle = block.color;
|
||||||
|
ctx.fillRect(
|
||||||
|
blockX,
|
||||||
|
blockY,
|
||||||
|
practiceBlockWidth,
|
||||||
|
practiceBlockHeight,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LineRecognitionDraw;
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
type-actions="{{typeActions}}"
|
type-actions="{{typeActions}}"
|
||||||
bind:select="onSelectType"
|
bind:select="onSelectType"
|
||||||
bind:random="onRandom" />
|
bind:random="onRandom" />
|
||||||
|
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
随机生成
|
随机生成
|
||||||
</toy-button>
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
type-actions="{{typeActions}}"
|
type-actions="{{typeActions}}"
|
||||||
bind:select="onSelectType"
|
bind:select="onSelectType"
|
||||||
bind:random="onRandom" />
|
bind:random="onRandom" />
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
随机生成
|
随机生成
|
||||||
</toy-button>
|
</toy-button>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
type-actions="{{typeActions}}"
|
type-actions="{{typeActions}}"
|
||||||
bind:select="onSelectType"
|
bind:select="onSelectType"
|
||||||
bind:random="onRandom" />
|
bind:random="onRandom" />
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
type-actions="{{typeActions}}"
|
type-actions="{{typeActions}}"
|
||||||
bind:select="onSelectType"
|
bind:select="onSelectType"
|
||||||
bind:random="onRandom" />
|
bind:random="onRandom" />
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -68,6 +68,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-c9055fd69f1b8d13"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
<view class="empty"></view>
|
<view class="empty"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
class="canvas-content"
|
class="canvas-content"
|
||||||
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-6f972b9b9bca0b9f"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="bottom-btn-box">
|
<view class="bottom-btn-box">
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
<view class="template-name">找字模板</view>
|
<view class="template-name">找字模板</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<ad-custom unit-id="adunit-4efb537f971b3f7d"></ad-custom>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view id="previewWrapper" class="wrapper" wx:if="{{cardList.length > 0}}">
|
<view id="previewWrapper" class="wrapper" wx:if="{{cardList.length > 0}}">
|
||||||
|
|||||||
@@ -23,12 +23,19 @@
|
|||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
|
{
|
||||||
|
"name": "focusPages/lineRecognition/lineRecognition",
|
||||||
|
"pathName": "focusPages/lineRecognition/lineRecognition",
|
||||||
|
"query": "id=line-recognition",
|
||||||
|
"scene": null,
|
||||||
|
"launchMode": "default"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/matchConnect/matchConnect",
|
"name": "focusPages/matchConnect/matchConnect",
|
||||||
"pathName": "focusPages/matchConnect/matchConnect",
|
"pathName": "focusPages/matchConnect/matchConnect",
|
||||||
"query": "id=match-connect",
|
"query": "id=match-connect",
|
||||||
"scene": null,
|
"launchMode": "default",
|
||||||
"launchMode": "default"
|
"scene": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "focusPages/colorPattern/colorPattern",
|
"name": "focusPages/colorPattern/colorPattern",
|
||||||
|
|||||||
Reference in New Issue
Block a user