feat: 文字涂色
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"pages": [
|
"pages": [
|
||||||
"pages/index/index",
|
"pages/index/index",
|
||||||
"pages/logs/logs",
|
|
||||||
"demoPages/textPaint/textPaint",
|
"demoPages/textPaint/textPaint",
|
||||||
"demoPages/textPaint2/textPaint"
|
"demoPages/textPaint2/textPaint"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
const PAPER_SIZE = {
|
const PAPER_SIZE = {
|
||||||
A4: {
|
A4: {
|
||||||
width: 2480,
|
// width: 2480,
|
||||||
height: 3508,
|
// height: 3508,
|
||||||
|
width: 595,
|
||||||
|
height: 842,
|
||||||
},
|
},
|
||||||
A3: {
|
A3: {
|
||||||
width: 3508,
|
width: 3508,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class TextDrawService {
|
|||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
this.setPaper();
|
this.setPaper();
|
||||||
this.setPaper3();
|
// this.setPaper3();
|
||||||
this.drawHeader();
|
this.drawHeader();
|
||||||
// this.drawLegend();
|
// this.drawLegend();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,220 @@
|
|||||||
|
// import { PAPER_SIZE } from './constant';
|
||||||
|
import { PAPER_SIZE } from './constant';
|
||||||
|
import { getMiniCodeImage } from './utils';
|
||||||
|
|
||||||
|
class TextDrawService {
|
||||||
|
canvas: WechatMiniprogram.Canvas;
|
||||||
|
ctx: RenderingContext;
|
||||||
|
options: Record<string, any>;
|
||||||
|
paperSize: PaperSize;
|
||||||
|
currentX: number;
|
||||||
|
currentY: number;
|
||||||
|
|
||||||
|
constructor(canvas: Canvas, ctx: RenderingContext, options?: Record<string, any>) {
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.ctx = ctx;
|
||||||
|
this.paperSize = 'A4';
|
||||||
|
this.options = {
|
||||||
|
appName: '涂鸦呀',
|
||||||
|
appHint: '涂色|识字|画画|认知',
|
||||||
|
title: '找一找 涂 色',
|
||||||
|
subTitle: '把相同的文字涂上相同的颜色',
|
||||||
|
colors: ['#FF0000', '#FDAF1A', '#0057FC', '#09FF00', '#8D00FF'],
|
||||||
|
characters: ['文', '升', '国', '舟', '丹'],
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
this.currentX = 0;
|
||||||
|
this.currentY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw() {
|
||||||
|
this.setPaper();
|
||||||
|
this.drawHeader();
|
||||||
|
this.drawLegend();
|
||||||
|
this.drawContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
async drawHeader() {
|
||||||
|
const { canvas, ctx } = this;
|
||||||
|
const { appName, appHint, title, subTitle } = this.options;
|
||||||
|
|
||||||
|
this.currentX = 80;
|
||||||
|
this.currentY = 80;
|
||||||
|
const titleX = this.currentX + 200 + 48;
|
||||||
|
const titleY = 80;
|
||||||
|
const image = await getMiniCodeImage(canvas);
|
||||||
|
ctx.drawImage(image, 80, 80, 200, 200);
|
||||||
|
|
||||||
|
ctx.font = 'bold 64px "Microsoft Yahei"';
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.textBaseline = 'top';
|
||||||
|
|
||||||
|
ctx.fillText(appName, titleX, titleY);
|
||||||
|
|
||||||
|
ctx.font = '48px "Microsoft Yahei"';
|
||||||
|
ctx.fillStyle = '#666';
|
||||||
|
ctx.fillText(appHint, titleX, 186);
|
||||||
|
|
||||||
|
// 绘制分割线
|
||||||
|
// ctx.strokeStyle = '#888';
|
||||||
|
// ctx.lineWidth = 2;
|
||||||
|
// ctx.beginPath();
|
||||||
|
// ctx.moveTo(483, 70);
|
||||||
|
// ctx.lineTo(483, 70 + 140);
|
||||||
|
// ctx.stroke();
|
||||||
|
|
||||||
|
ctx.font = 'bold 64px "Microsoft Yahei"';
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.fillText(title, 1015, titleY);
|
||||||
|
|
||||||
|
ctx.font = '48px "Microsoft Yahei"';
|
||||||
|
ctx.fillStyle = '#666';
|
||||||
|
ctx.fillText(subTitle, 1015, 186);
|
||||||
|
|
||||||
|
this.currentY = 304;
|
||||||
|
this.drawLine(this.currentY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绘制示例
|
||||||
|
* 矩形: x、y为左上角
|
||||||
|
* 圆形:x、y为圆心
|
||||||
|
* 文字:x textAlign 为 start,文本左边缘对齐
|
||||||
|
* y textBaseline 为 alphabetic,y 对应字母基线(类似左下角)
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
drawLegend() {
|
||||||
|
const { canvas, ctx, options } = this;
|
||||||
|
const { colors, characters } = options;
|
||||||
|
const radius = 65;
|
||||||
|
const rectWidth = 180;
|
||||||
|
const rectHeight = 80;
|
||||||
|
const startX = 260 + radius; // 圆形的X坐标是圆心
|
||||||
|
const startY = 304 + 26 + radius; // 圆形的Y坐标是圆心
|
||||||
|
const len = characters.length || 4;
|
||||||
|
const canvasWidth = canvas.width;
|
||||||
|
/** 计算每个圆之间的间距 俩个60,一个是页面右侧边距,另一个是圆离右边距地边距*/
|
||||||
|
const spaceWidth = (canvasWidth - startX * 2) / (len - 1);
|
||||||
|
|
||||||
|
ctx.moveTo(startX, startY);
|
||||||
|
colors.forEach((color: string, index: number) => {
|
||||||
|
const x = startX + index * spaceWidth;
|
||||||
|
const y = startY; // 计算圆的Y坐标
|
||||||
|
|
||||||
|
// 绘制圆
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.fillStyle = color;
|
||||||
|
ctx.lineWidth = 4;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
// 绘制长方形
|
||||||
|
ctx.fillStyle = '#fff'; // 长方形背景色
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 4;
|
||||||
|
const rectangleX = x - rectWidth / 2; // 从圆形移动一半的长方形的长
|
||||||
|
const rectangleY = y + radius + 43; // 从圆形下移一个半径,再加上43的间距
|
||||||
|
ctx.strokeRect(rectangleX, rectangleY, rectWidth, rectHeight); // 绘制长方形
|
||||||
|
|
||||||
|
// 绘制字符
|
||||||
|
ctx.fillStyle = '#333';
|
||||||
|
ctx.font = 'bold 48px "Microsoft Yahei"';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(
|
||||||
|
characters[index],
|
||||||
|
rectangleX + rectWidth / 2,
|
||||||
|
rectangleY + rectHeight / 2,
|
||||||
|
rectWidth,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.currentY = 612; // 计算分割线的Y坐标,距离示例图20px
|
||||||
|
this.drawLine(this.currentY);
|
||||||
|
}
|
||||||
|
|
||||||
|
drawContent() {
|
||||||
|
const { canvas, ctx, options } = this;
|
||||||
|
const { characters } = options;
|
||||||
|
|
||||||
|
const len = characters.length;
|
||||||
|
const rows = 8;
|
||||||
|
const radius = 80;
|
||||||
|
const fontSize = 72;
|
||||||
|
|
||||||
|
const startY = this.currentY + 62 + radius;
|
||||||
|
const startX1 = 310 + radius;
|
||||||
|
const startX2 = 200 + radius;
|
||||||
|
const canvasWidth = canvas.width;
|
||||||
|
const spaceWidthFrist = (canvasWidth - startX1 * 2) / (5 - 1);
|
||||||
|
const spaceWidthSecond = (canvasWidth - startX2 * 2) / (6 - 1);
|
||||||
|
const spaceHeight = 54;
|
||||||
|
|
||||||
|
ctx.moveTo(startX1, startY);
|
||||||
|
for (let i = 0; i < rows; i++) {
|
||||||
|
const cols = i % 2 === 0 ? 5 : 6;
|
||||||
|
|
||||||
|
for (let j = 0; j < cols; j++) {
|
||||||
|
const y = startY + i * (spaceHeight + radius * 2);
|
||||||
|
const x =
|
||||||
|
i % 2 === 0 ? startX1 + j * spaceWidthFrist : startX2 + j * spaceWidthSecond;
|
||||||
|
|
||||||
|
const character = characters[Math.floor(Math.random() * len)];
|
||||||
|
|
||||||
|
ctx.fillStyle = '#fff';
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 4;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, radius, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.closePath();
|
||||||
|
|
||||||
|
ctx.fillStyle = '#333';
|
||||||
|
ctx.font = `${fontSize}px "Microsoft Yahei"`;
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(character, x, y, radius * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawLine(linY: number) {
|
||||||
|
const { canvas, ctx } = this;
|
||||||
|
|
||||||
|
ctx.strokeStyle = '#000';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(80, linY);
|
||||||
|
ctx.lineTo(canvas.width - 80, linY);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
setPaper() {
|
||||||
|
const { ctx, canvas } = this;
|
||||||
|
const { pixelRatio: dpr } = wx.getWindowInfo();
|
||||||
|
// const { width, height } = { width: 595 * dpr, height: 842 * dpr };
|
||||||
|
let { width, height } = PAPER_SIZE[this.paperSize];
|
||||||
|
width = width * dpr;
|
||||||
|
height = height * dpr;
|
||||||
|
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
console.log('width---:', width);
|
||||||
|
console.log('height---:', height);
|
||||||
|
this.clear();
|
||||||
|
ctx.fillStyle = '#fff'; // 设置背景色为白色
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
const canvas = this.canvas;
|
||||||
|
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TextDrawService;
|
||||||
@@ -22,16 +22,20 @@
|
|||||||
.container {
|
.container {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
// overflow-y: hidden;
|
background-color: transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.canvas-container {
|
.canvas-container {
|
||||||
margin: 20rpx;
|
margin-top: 20rpx;
|
||||||
width: calc(100% - 40rpx);
|
// margin: 20rpx;
|
||||||
// width: 100%;
|
// width: calc(100% - 40rpx);
|
||||||
height: 86%;
|
// // width: 100%;
|
||||||
|
// height: 580px;
|
||||||
// height: calc(100% - 80rpx);
|
// height: calc(100% - 80rpx);
|
||||||
border: 1rpx solid #333;
|
border: 2rpx solid #333;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// import TextDrawService from '../../canvasService/textDrawService';
|
// import TextDrawService from '../../canvasService/textDrawService';
|
||||||
import TextDrawServiceV2 from '../../canvasService/textDrawServiceV2';
|
// import TextDrawServiceV2 from '../../canvasService/textDrawServiceV2';
|
||||||
|
import { PAPER_SIZE } from '../../canvasService/constant';
|
||||||
|
import TextDrawServiceV3 from '../../canvasService/textDrawServiceV3';
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
canvas: null as Canvas | null,
|
canvas: null as Canvas | null,
|
||||||
@@ -19,10 +21,49 @@ Page({
|
|||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
console.log('textPaint2 onLoad');
|
console.log('textPaint2 onLoad');
|
||||||
this.generateMatrix();
|
// this.setCanvasBoxSize();
|
||||||
this.initCanvas();
|
// this.generateMatrix();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onReady() {
|
||||||
|
wx.nextTick(() => {
|
||||||
|
this.setCanvasBoxSize();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// setCanvasBoxSize() {
|
||||||
|
// const { width: paperWidth, height: paperHeight } = PAPER_SIZE['A4'];
|
||||||
|
|
||||||
|
// // 逻辑尺寸(容器宽高)
|
||||||
|
// const boxWidth = windowWidth - 40;
|
||||||
|
// const boxHeight = boxWidth / (paperWidth / paperHeight);
|
||||||
|
// console.log('boxWidth---:', boxWidth);
|
||||||
|
// console.log('boxHeight---:', boxHeight);
|
||||||
|
// this.setData({ boxWidth, boxHeight });
|
||||||
|
// // // 实际像素尺寸(需乘以设备像素比)
|
||||||
|
// // const canvasWidth = boxWidth * pixelRatio;
|
||||||
|
// // const canvasHeight = boxHeight * pixelRatio;
|
||||||
|
|
||||||
|
// // // 更新 Canvas 节点属性
|
||||||
|
// // const query = wx.createSelectorQuery().select('#textCanvas');
|
||||||
|
// // query.fields({ node: true, size: true }).exec((res) => {
|
||||||
|
// // const canvas = res[0].node;
|
||||||
|
// // canvas.width = canvasWidth;
|
||||||
|
// // canvas.height = canvasHeight;
|
||||||
|
// // // 缩放上下文以适配逻辑尺寸
|
||||||
|
// // const ctx = canvas.getContext('2d');
|
||||||
|
// // ctx.scale(pixelRatio, pixelRatio);
|
||||||
|
|
||||||
|
// // this.canvas = canvas;
|
||||||
|
// // this.ctx = ctx;
|
||||||
|
|
||||||
|
// // const textDrawService = new TextDrawServiceV3(canvas, ctx);
|
||||||
|
// // textDrawService.draw();
|
||||||
|
// // ctx.fillStyle = '#fff'; // 设置背景色为白色
|
||||||
|
// // ctx.fillRect(0, 0, canvasWidth, canvasHeight);
|
||||||
|
// // });
|
||||||
|
// },
|
||||||
|
|
||||||
// 生成6列随机矩阵
|
// 生成6列随机矩阵
|
||||||
generateMatrix() {
|
generateMatrix() {
|
||||||
const shuffled = [...this.data.characters]
|
const shuffled = [...this.data.characters]
|
||||||
@@ -35,6 +76,18 @@ Page({
|
|||||||
this.setData({ matrix: shuffled.slice(0, 42) }); // 6列x7行
|
this.setData({ matrix: shuffled.slice(0, 42) }); // 6列x7行
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setCanvasBoxSize() {
|
||||||
|
const { width, height } = PAPER_SIZE['A4'];
|
||||||
|
const { windowWidth } = wx.getWindowInfo();
|
||||||
|
const boxWidth = windowWidth - 20;
|
||||||
|
const boxHeight = boxWidth / (width / height);
|
||||||
|
console.log('boxWidth----:', boxWidth);
|
||||||
|
console.log('boxHeight----:', boxHeight);
|
||||||
|
this.setData({ boxHeight, boxWidth }, () => {
|
||||||
|
this.initCanvas();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// 初始化A4画布(300DPI)
|
// 初始化A4画布(300DPI)
|
||||||
initCanvas() {
|
initCanvas() {
|
||||||
wx.createSelectorQuery()
|
wx.createSelectorQuery()
|
||||||
@@ -44,13 +97,35 @@ Page({
|
|||||||
size: true,
|
size: true,
|
||||||
})
|
})
|
||||||
.exec((res) => {
|
.exec((res) => {
|
||||||
const canvas = res[0].node;
|
if (res[0] && res[0].node) {
|
||||||
const ctx = canvas.getContext('2d');
|
const canvas = res[0].node;
|
||||||
this.canvas = canvas;
|
const ctx = canvas.getContext('2d');
|
||||||
this.ctx = ctx;
|
if (ctx) {
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.ctx = ctx;
|
||||||
|
const rect = res[0];
|
||||||
|
console.log('rect-----:', rect);
|
||||||
|
// const width = rect.width * dpr;
|
||||||
|
// const height = rect.height * dpr;
|
||||||
|
|
||||||
const textDrawService = new TextDrawServiceV2(canvas, ctx);
|
// 设置canvas的实际尺寸
|
||||||
textDrawService.draw();
|
// canvas.width = width;
|
||||||
|
// canvas.height = height;
|
||||||
|
// ctx.scale(dpr, dpr);
|
||||||
|
|
||||||
|
// 使用TextDrawServiceV3绘制
|
||||||
|
const textDrawService = new TextDrawServiceV3(canvas, ctx);
|
||||||
|
textDrawService.draw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const canvas = res[0].node;
|
||||||
|
// const ctx = canvas.getContext('2d');
|
||||||
|
// this.canvas = canvas;
|
||||||
|
// this.ctx = ctx;
|
||||||
|
|
||||||
|
// const textDrawService = new TextDrawServiceV3(canvas, ctx);
|
||||||
|
// textDrawService.draw();
|
||||||
|
|
||||||
// this.setCanvasSize(canvas, ctx);
|
// this.setCanvasSize(canvas, ctx);
|
||||||
// ctx.fillRect(0, 0, 2480, 3508);
|
// ctx.fillRect(0, 0, 2480, 3508);
|
||||||
|
|||||||
@@ -9,10 +9,9 @@
|
|||||||
data-index="{{index}}"></view>
|
data-index="{{index}}"></view>
|
||||||
</block>
|
</block>
|
||||||
</view> -->
|
</view> -->
|
||||||
|
|
||||||
<!-- 画布容器 -->
|
<!-- 画布容器 -->
|
||||||
<canvas type="2d" id="textCanvas" class="canvas-container" />
|
<canvas type="2d" id="textCanvas" class="canvas-container" style="width:{{boxWidth}}px;height:{{boxHeight}}px" />
|
||||||
|
<view>当前宽度:{{boxWidth}}px,高度:{{boxHeight}}px</view>
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<button class="action-btn" bindtap="exportToPrint">导出A4图片</button>
|
<button class="action-btn" bindtap="exportToPrint">导出A4图片</button>
|
||||||
</view>
|
</view>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"setting": {
|
"setting": {
|
||||||
"compileHotReLoad": true
|
"compileHotReLoad": true
|
||||||
},
|
},
|
||||||
"libVersion": "3.7.10",
|
"libVersion": "3.7.12",
|
||||||
"condition": {
|
"condition": {
|
||||||
"miniprogram": {
|
"miniprogram": {
|
||||||
"list": [
|
"list": [
|
||||||
|
|||||||
Reference in New Issue
Block a user