feat: 绘制基础类重构

This commit is contained in:
R524809
2026-04-10 15:06:11 +08:00
parent a911da6181
commit e74fd3709c
17 changed files with 1091 additions and 278 deletions
+79 -40
View File
@@ -1,5 +1,6 @@
import { PAPER_SIZE } from '../../constants/colors';
import { drawBaseHeader, drawBaseMiniHeader } from './baseHeaderDraw';
import { getImage } from '../../utils/index';
import drawHeader from './drawHeader';
/**
* 基础绘制服务
@@ -8,12 +9,14 @@ import { drawBaseHeader, drawBaseMiniHeader } from './baseHeaderDraw';
* 提供功能:
* - Canvas 初始化和配置
* - Paper 尺寸设置(支持 A4 等标准尺寸)
* - Header 绘制(支持完整 Header 和迷你 Header
* - Header 绘制(统一打印纸页眉
* - 分割线绘制
* - 打印配置管理
*/
const LINE_COLOR = 'rgba(50, 46, 37, 0.4)';
const LINE_WIDTH = 3;
export class BaseDrawService {
headerType: PrintHeader = 'wechat';
canvas: WechatMiniprogram.Canvas;
ctx: RenderingContext;
options: Record<string, any>;
@@ -22,6 +25,8 @@ export class BaseDrawService {
currentY: number;
canvasWidth: number; // 逻辑像素宽度
canvasHeight: number; // 逻辑像素高度
/** 紧跟在 drawHeader 之后的第一次 drawDivider 不画线(页眉已含底部分割线) */
private _suppressNextDividerLine = false;
constructor(
canvas: Canvas,
@@ -37,20 +42,18 @@ export class BaseDrawService {
appName,
appHint,
title: '看数字,涂一涂',
subTitle: '找一找下面相同的数字,涂上颜色',
...options,
};
this.currentX = 0;
this.currentY = 0;
this.canvasWidth = 0;
this.canvasHeight = 0;
this._suppressNextDividerLine = false;
this.setPrintConfig();
}
setPrintConfig() {
const printConfig = getApp().getPrintConfig();
this.headerType = printConfig.header;
this.options.appName = printConfig.appName;
}
@@ -93,51 +96,95 @@ export class BaseDrawService {
* 绘制Header(逻辑像素,尺寸除以3)
*/
async drawHeader() {
this.currentX = 25;
this.currentY = 25;
await drawBaseHeader({
this.currentX = 24;
this.currentY = 14;
await drawHeader({
canvas: this.canvas,
ctx: this.ctx,
headerType: this.headerType,
canvasWidth: this.canvasWidth,
options: {
appName: this.options.appName || '涂鸦丫小程序',
appHint: this.options.appHint || '识字|识图|练字|打印',
title: this.options.title || '看数字,涂一涂',
subTitle:
this.options.subTitle || '找一找下面相同的数字,涂上颜色',
},
onHeaderDrawn: (currentY) => {
onHeaderDrawn: (currentY: number) => {
this.currentY = currentY;
this._suppressNextDividerLine = true;
},
});
}
/**
* 绘制迷你Header(逻辑像素,尺寸除以3)
* 页脚:顶部分割线 + 居中 `doodle-footer.png`(小鸭与「涂鸦丫」品牌图)。
* 图片底边与纸张底边留白对齐,便于「探头」视觉。请在整页正文绘制完成后调用,避免被内容覆盖。
*/
drawMiniHeader() {
drawBaseMiniHeader({
ctx: this.ctx,
canvasWidth: this.canvasWidth,
options: {
appName: this.options.appName || '涂鸦丫小程序',
title: this.options.title || '看数字,涂一涂',
},
onHeaderDrawn: (currentY) => {
console.log('drawMiniHeader currentY', currentY);
this.currentY = currentY;
},
});
async drawPrintFooter(): Promise<void> {
const { ctx, canvasWidth, canvasHeight } = this;
const margin = 24;
const bottomMargin = 2;
const lineGapAboveImg = 10;
const maxImgW = canvasWidth - margin * 2;
const maxImgH = 40;
let dw = maxImgW;
let dh = maxImgH;
try {
const image = (await getImage(
this.canvas,
'/assets/imgs/doodle-footer.png',
)) as WechatMiniprogram.Image;
const nw = image.width || 2;
const nh = image.height || 1;
const aspect = nw / nh;
dh = Math.min(maxImgH, maxImgW / aspect);
dw = dh * aspect;
if (dw > maxImgW) {
dw = maxImgW;
dh = dw / aspect;
}
const imgX = (canvasWidth - dw) / 2;
const imgY = canvasHeight - bottomMargin - dh;
const lineY = imgY - lineGapAboveImg;
ctx.save();
ctx.strokeStyle = LINE_COLOR;
ctx.lineWidth = LINE_WIDTH;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(margin, lineY);
ctx.lineTo(canvasWidth - margin, lineY);
ctx.stroke();
ctx.restore();
ctx.drawImage(image, imgX, imgY, dw, dh);
} catch {
const lineY =
canvasHeight - bottomMargin - maxImgH - lineGapAboveImg;
ctx.save();
ctx.strokeStyle = LINE_COLOR;
ctx.lineWidth = LINE_WIDTH;
ctx.lineCap = 'round';
ctx.beginPath();
ctx.moveTo(margin, lineY);
ctx.lineTo(canvasWidth - margin, lineY);
ctx.stroke();
ctx.restore();
}
}
/**
* 绘制分割线(逻辑像素,尺寸除以3)
*/
drawDivider() {
if (this._suppressNextDividerLine) {
this._suppressNextDividerLine = false;
return;
}
const { ctx, canvasWidth } = this;
const dividerY = this.currentY;
ctx.strokeStyle = '#000';
ctx.strokeStyle = LINE_COLOR;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(24, dividerY);
@@ -475,18 +522,10 @@ export class BaseDrawService {
}
/**
* 绘制 Header 和 Divider(公共绘制逻辑)
* 根据 headerType 自动选择绘制完整 Header 或迷你 Header,然后绘制分割线
* 绘制统一页眉;紧随其后的 drawDivider 会跳过首条分割线(页眉已含底部灰线)。
*/
async drawHeaderAndDivider() {
// 绘制Header
if (this.headerType !== 'minimal') {
await this.drawHeader();
} else {
this.drawMiniHeader();
}
// 绘制内容区域分割线
await this.drawHeader();
this.drawDivider();
}