From 27d212e7dc9dcf61f361bd633482d5f26cde4886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=AE=E5=BE=AE=E4=B8=80=E7=AC=91?= <709648985@qq.com> Date: Wed, 5 Nov 2025 19:18:05 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=BC=98=E5=8C=967305?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TextToHex/textToDotMatrixFor7305.vue | 124 +- pages/7305/BJQ7305.vue | 3816 +++++++++-------- 2 files changed, 2042 insertions(+), 1898 deletions(-) diff --git a/components/TextToHex/textToDotMatrixFor7305.vue b/components/TextToHex/textToDotMatrixFor7305.vue index 37f98a3..307b03b 100644 --- a/components/TextToHex/textToDotMatrixFor7305.vue +++ b/components/TextToHex/textToDotMatrixFor7305.vue @@ -27,6 +27,16 @@ color: { type: String, default: "#000000" + }, + // 二值化阈值(0~1),越大越细;默认偏清晰 + threshold: { + type: Number, + default: 0.45 + }, + // 是否启用轻度笔画修复(3x3 膨胀);默认关闭以避免变粗 + sharpen: { + type: Boolean, + default: false } }, data() { @@ -48,6 +58,16 @@ this.ctx = uni.createCanvasContext('reusableCanvas', this); }, methods: { + /** + * 外部可调用:复位画布为纯背景并立即提交 + */ + async resetCanvas() { + if (!this.ctx) return; + this.clearCanvas(); + await new Promise((resolve) => { + this.ctx.draw(true, () => setTimeout(resolve, 30)); + }); + }, /** * 估算单行文本所需的Canvas宽度 */ @@ -69,6 +89,8 @@ * 复用单个Canvas处理所有文本行 */ async drawAndGetPixels() { + // 发送前:确保画布处于干净背景态 + await this.resetCanvas(); // 超采样比例(提高分辨率再降采样,减少模糊) const SCALE = 3; const PADDING_X = 1 * SCALE; // 左侧预留像素,避免首字裁剪 @@ -96,21 +118,28 @@ } let convertCharToMatrix = (drawResult, item) => { - // 设备端使用13x13点阵渲染 + // 设备端使用13x13点阵渲染(保持输出13列×13行),但只在内部采样12×12,保留右侧与底部1像素缓冲 const charWidth = 13; const charHeight = 13; + const effectiveWidth = 12; // 仅采样前12列 + const effectiveHeight = 12; // 仅采样前12行 const { pixelData, width, height } = drawResult; // 将高分辨率像素降采样为13x13的布尔矩阵 const target = new Array(charWidth * charHeight).fill(0); - const threshold = 0.5; // 每个小块中超过50%亮色(文字)判为1 + const threshold = Math.max(0.2, Math.min(0.8, this.threshold || 0.45)); - // 确保采样区域严格对齐,从PADDING_X开始,只采样13列 - // 字符实际绘制区域:从PADDING_X开始,宽度为13*SCALE + // 确保采样区域严格对齐,从PADDING_X开始,只采样12列(右侧预留1px),12行(底部预留1px) + // 字符实际绘制区域:从PADDING_X开始,宽度为12*SCALE const charStartX = PADDING_X; - const charEndX = PADDING_X + charWidth * SCALE; + const charEndX = PADDING_X + effectiveWidth * SCALE; // 仅采样到第12列 for (let y = 0; y < charHeight; y++) { for (let x = 0; x < charWidth; x++) { + // 超出有效采样区域(第13列或第13行)直接置0,作为缓冲 + if (x >= effectiveWidth || y >= effectiveHeight) { + target[y * charWidth + x] = 0; + continue; + } let onCount = 0; let total = 0; // 采样区域:字符从PADDING_X开始绘制,每列宽度为SCALE @@ -151,6 +180,32 @@ } } + // 轻度笔画修复:可选 3x3 膨胀(启用时阈值更严格,避免变粗) + if (this.sharpen) { + const dilated = target.slice(); + for (let y = 0; y < effectiveHeight; y++) { + for (let x = 0; x < effectiveWidth; x++) { + const idx = y * charWidth + x; + if (target[idx] === 1) continue; + let neighbors = 0; + for (let dy = -1; dy <= 1; dy++) { + for (let dx = -1; dx <= 1; dx++) { + if (dx === 0 && dy === 0) continue; + const nx = x + dx; + const ny = y + dy; + if (nx < 0 || ny < 0 || nx >= effectiveWidth || ny >= effectiveHeight) continue; + if (target[ny * charWidth + nx] === 1) neighbors++; + } + } + // 使用更严格的邻居门限,避免整体变粗 + if (neighbors >= 5) { + dilated[idx] = 1; + } + } + } + for (let i = 0; i < target.length; i++) target[i] = dilated[i]; + } + const lowBytes = new Array(charWidth).fill(0); const highBytes = new Array(charWidth).fill(0); // 按列打包,每列13行分成上下两字节:低字节0-7行(8行),高字节8-12行(5行) @@ -198,30 +253,41 @@ ctx.fillText(textLine, charX, charY); // 5. 异步绘制并获取像素数据(串行处理避免冲突) - await new Promise((resolve, reject) => { - ctx.draw(false, () => { - uni.canvasGetImageData({ - canvasId: 'reusableCanvas', - x: 0, - y: 0, - width: this.currentCanvasWidth, - height: this.currentCanvasHeight, - success: res => { - result = { - line: textLine, - pixelData: res.data, + const grabPixels = () => new Promise((resolve, reject) => { + // 立即绘制并给一点缓冲时间,避免取像素过早 + ctx.draw(true, () => { + setTimeout(() => { + uni.canvasGetImageData({ + canvasId: 'reusableCanvas', + x: 0, + y: 0, width: this.currentCanvasWidth, - height: this.currentCanvasHeight - }; - resolve(); - }, - fail: err => { - // console.error(`处理第${i+1}行失败:`, err); - reject(err) - } - }); + height: this.currentCanvasHeight, + success: res => { + result = { + line: textLine, + pixelData: res.data, + width: this.currentCanvasWidth, + height: this.currentCanvasHeight + }; + resolve(); + }, + fail: err => reject(err) + }); + }, 70); }); }); + + await grabPixels(); + // 一次性校验:若像素全黑或明显异常,重绘重取一次 + let nonZero = false; + for (let i = 0; i < result.pixelData.length; i += 4) { + if (result.pixelData[i] || result.pixelData[i+1] || result.pixelData[i+2]) { nonZero = true; break; } + } + if (!nonZero) { + await new Promise(r => setTimeout(r, 50)); + await grabPixels(); + } return result; } @@ -239,11 +305,17 @@ // 调试:打印每个字符的点阵数据 console.log(`[点阵生成] 字符"${char}" 点阵数据:`, matrix.map(b => '0x' + b.toString(16).padStart(2, '0')).join(' ')); linePixls.push(matrix); + // 在字符间增加轻微延时,避免相邻提取竞争(尤其是末字符) + await new Promise(r => setTimeout(r, 20)); } // console.log("hexs=", linePixls.join(",")); arr.push(linePixls); + // 每行结束再等一会,提高末字符稳定性 + await new Promise(r => setTimeout(r, 40)); } + // 发送后:再次清空画布,避免残留影响下一次 + await this.resetCanvas(); return arr; } } diff --git a/pages/7305/BJQ7305.vue b/pages/7305/BJQ7305.vue index d8a3883..47c2e16 100644 --- a/pages/7305/BJQ7305.vue +++ b/pages/7305/BJQ7305.vue @@ -1,1873 +1,1945 @@ - - - - - \ No newline at end of file