feat: 绘制基础类重构
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user