1
0
forked from dyf/APP
Files
APP/components/TextToHex/textToDotMatrixFor7305.vue
2025-12-15 17:31:43 +08:00

262 lines
6.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<canvas type="2d" canvas-id="reusableCanvas" :width="currentCanvasWidth" :height="currentCanvasHeight"
class="offscreen-canvas"></canvas>
</view>
</template>
<script>
export default {
name: "textToDotMatrixFor7305",
props: {
txts: {
type: Array,
default: () => [],
validator: (value) => value.every(item => typeof item === 'string')
},
fontSize: {
type: Number,
default: 16,
validator: (value) => value > 0 && value <= 100
},
bgColor: {
type: String,
default: "#ffffff"
},
color: {
type: String,
default: "#000000"
}
},
data() {
return {
// 当前Canvas的宽高动态调整
currentCanvasWidth: 0,
currentCanvasHeight: 0,
// Canvas上下文复用
ctx: null,
// 标记画布是否已预热(解决首次发送像素不完整问题)
canvasWarmed: false
};
},
computed: {
validTxts() {
return this.txts.filter(line => line.trim() !== '');
}
},
mounted() {
// 初始化Canvas上下文只创建一次
this.ctx = uni.createCanvasContext('reusableCanvas', this);
},
methods: {
/**
* 估算单行文本所需的Canvas宽度
*/
calcLineWidth(textLine) {
return textLine.length * this.fontSize;
},
/**
* 清除Canvas内容
*/
clearCanvas() {
this.ctx.setFillStyle(this.bgColor);
this.ctx.fillRect(0, 0, this.currentCanvasWidth, this.currentCanvasHeight);
},
/**
* 预热画布,防止 APP 首次调用时获取到的像素数据不完整
*/
async warmupCanvas() {
if (this.canvasWarmed) {
return;
}
try {
// 先用一个最小画布绘制测试字形,触发字体和 canvas 初始化
this.currentCanvasWidth = 16;
this.currentCanvasHeight = 16;
this.clearCanvas();
this.ctx.setFillStyle(this.color);
this.ctx.setFontSize(this.fontSize);
this.ctx.font = `${this.fontSize}px "PingFangBold", "PingFang SC", Arial, sans-serif`;
this.ctx.setTextBaseline('middle');
this.ctx.fillText('测', 0, 8);
await new Promise((resolve) => {
this.ctx.draw(false, () => {
// 读取一次数据,确保 canvasGetImageData 就绪
setTimeout(() => {
uni.canvasGetImageData({
canvasId: 'reusableCanvas',
x: 0,
y: 0,
width: 16,
height: 16,
success: () => {
this.canvasWarmed = true;
resolve();
},
fail: () => {
// 即便失败也认为已尝试过,避免反复预热阻塞流程
this.canvasWarmed = true;
resolve();
}
});
}, 100);
});
});
// 额外等待,确保字体完全加载
await new Promise(resolve => setTimeout(resolve, 200));
} catch (ex) {
console.log("画布预热异常:", ex);
this.canvasWarmed = true;
}
},
/**
* 复用单个Canvas处理所有文本行
*/
async drawAndGetPixels() {
// 首次调用先做预热,避免第一次上报缺字
await this.warmupCanvas();
let binaryToHex = (binaryArray) => {
if (!Array.isArray(binaryArray) || binaryArray.length !== 8) {
throw new Error("输入必须是包含8个元素的二进制数组");
}
// 检查每个元素是否为0或1
if (!binaryArray.every(bit => bit === 0 || bit === 1)) {
throw new Error("数组元素必须只能是0或1");
}
// 将二进制数组转换为十进制数
let decimalValue = 0;
for (let i = 0; i < 8; i++) {
// 计算每个位的权重并累加
decimalValue += binaryArray[i] * Math.pow(2, 7 - i);
}
// 将十进制转换为十六进制字符串并添加0x前缀
const hexString = "0x" + decimalValue.toString(16).padStart(2, '0').toUpperCase();
return hexString;
}
let convertCharToMatrix = (imageData, item) => {
const charWidth = 13;
const charHeight = 13;
const pixels = [];
for (let i = 0; i < imageData.length; i += 4) {
const R = imageData[i];
pixels.push(R < 128 ? 1 : 0);
}
const lowBytes = new Array(charWidth).fill(0);
const highBytes = new Array(charWidth).fill(0);
for (let col = 0; col < charWidth; col++) {
for (let row = 0; row < charHeight; row++) {
const pixel = pixels[row * charWidth + col];
if (pixel === 1) {
if (row < 8) {
lowBytes[col] |= (1 << row);
} else {
highBytes[col] |= (1 << (row - 8));
}
}
}
}
return [...lowBytes, ...highBytes];
}
let drawTxt = async (textLine) => {
let result = {};
let ctx = this.ctx;
// 1. 动态调整Canvas尺寸
this.currentCanvasWidth = 13;
this.currentCanvasHeight = 13;
// 2. 清空Canvas绘制背景
this.clearCanvas();
// 3. 设置文字样式
ctx.setFillStyle(this.color);
ctx.setTextBaseline('middle');
// ctx.setTextAlign('center')
ctx.setFontSize(this.fontSize);
ctx.font = `${this.fontSize}px "PingFangBold", "PingFang SC", Arial, sans-serif`;
// 4. 绘制当前行文本
let currentX = 0;
let currentY = this.fontSize / 2 + 1;
for (let j = 0; j < textLine.length; j++) {
let char = textLine[j];
ctx.fillText(char, currentX, currentY);
// 按实际字符宽度计算间距
let charWidth = ctx.measureText(char).width;
currentX += charWidth;
}
// 5. 异步绘制并获取像素数据(串行处理避免冲突)
await new Promise((resolve, reject) => {
ctx.draw(false, () => {
setTimeout(() => {
uni.canvasGetImageData({
canvasId: 'reusableCanvas',
x: 0,
y: 0,
width: this.currentCanvasWidth,
height: this.currentCanvasHeight,
success: res => {
result = {
line: textLine,
pixelData: res.data,
width: this.currentCanvasWidth,
height: this.currentCanvasHeight
};
resolve();
},
fail: err => {
// console.error(`处理第${i+1}行失败:`, err);
reject(err)
}
});
}, 100);
});
});
return result;
}
let arr = [];
// 循环处理每行文本
for (let i = 0; i < this.validTxts.length; i++) {
let linePixls = [];
let item = this.validTxts[i];
// console.log("item=", item);
for (var j = 0; j < item.length; j++) {
let result = await drawTxt(item[j]);
linePixls.push(convertCharToMatrix(result.pixelData, item));
}
// console.log("hexs=", linePixls.join(","));
arr.push(linePixls);
}
return arr;
}
}
};
</script>
<style>
.offscreen-canvas {
position: fixed;
left: -9999px;
top: -9999px;
visibility: hidden;
}
</style>