赞
踩
图片需要通过 canvas进行处理 预先在canvas上绘制然后读取图片
打印途中报 10007 特性不支持 解决方案 可以看文章最后的描述
chooseImage() { this.clearCanvas(); const ctx = uni.createCanvasContext("secondCanvas", this); uni.downloadFile({ // uni.chooseImage({ url: "http://xxx.Jpeg", //仅为示例,并非真实的资源 // url: pointDataUrl, success: (res) => { console.log("res", res); if (res.statusCode === 200) { // const tempFilePath = res.tempFilePaths[0]; const tempFilePath = res.tempFilePath; uni.getImageInfo({ src: tempFilePath, success: (res) => { // 打印宽度须是8的整数倍,这里处理掉多余的,使得宽度合适,不然有可能乱码 // paperWidth 用于设置打印机的宽度 最大打印宽度 产品说明书上有写 我的是576点 const mw = this.paperWidth % 8; // 计算出宽度以及高度 const w = mw === 0 ? this.paperWidth : this.paperWidth - mw; // 等比算出图片的高度 const h = Math.floor((res.height * w) / res.width); // 设置canvas宽高 this.img = tempFilePath; // 这边是进行设置画布高度 this.canvasHeight = h; this.canvasWidth = w; console.log("this.canvasHeight", this.canvasHeight); console.log("this.canvasWidth", this.canvasWidth); // 使用nextTick 让canvas宽度生效 this.$nextTick(() => { // 在canvas 画一张图片 ctx.fillStyle = "rgba(255,255,255,1)"; ctx.clearRect(0, 0, w, h); ctx.fillRect(0, 0, w, h); // 进行绘画图片 ctx.drawImage(tempFilePath, 0, 0, w, h); ctx.draw(false, () => {}); }); }, fail: (res) => {}, }); } }, }); },
设置完图片后 我们就可以开始连接蓝牙打印机了
function uniAsyncPromise(name, options) { return new Promise((resolve, reject) => { uni[name]({ ...(options || {}), // ...options, success: (res) => { resolve(res); }, fail: (err) => { reject(err); }, }); }); } function openBlue() { return uniAsyncPromise("openBluetoothAdapter"); } function startBluetoothDevicesDiscovery(option) { console.log("开始蓝牙扫描"); uniAsyncPromise("startBluetoothDevicesDiscovery", option).then((res) => { // console.log("正在搜寻蓝牙设备", res); }); } function getConnectedBluetoothDevices(option) { console.log("开始获取已连接设备"); return uniAsyncPromise("getConnectedBluetoothDevices", option); } function stopBlueDevicesDiscovery() { //监听寻找到新设备的事件 console.log("停止蓝牙扫描"); return uniAsyncPromise("stopBluetoothDevicesDiscovery").then((res) => { // console.log("停止搜寻蓝牙设备", res); }); } function createBLEConnection(deviceId, sucess, fail) { //连接蓝牙设备 console.log("连接蓝牙设备", deviceId); uniAsyncPromise("createBLEConnection", { deviceId, }) .then((res) => { //连接成功可选择停止搜索蓝牙 //stopBlueDevicesDiscovery(); console.log("连接成功"); sucess && sucess({ res: res, }); }) .catch((res) => { console.log("连接设备异常" + res); fail && fail({ res: res, }); }); } function getBLEDeviceServices(deviceId, success, fail) { console.log("获取ServiceId", deviceId); //加延迟避免取不到service setTimeout(() => { uniAsyncPromise("getBLEDeviceServices", { deviceId: deviceId, }) .then((res) => { console.log("服务", res); success && success({ serviceId: res.services, }); }) .catch((res) => { //getBLEDeviceServices(deviceId, success, fail); console.log("获取ServiceId异常" + res); fail && fail({ res: res, }); }); }, 1000); } function getDeviceCharacteristics(deviceId, services, success, fail) { //services = services.slice(0); console.log("获取Characteristics", deviceId, services); if (services.length) { const serviceId = services.shift().uuid; console.log("ServceID ", serviceId); uniAsyncPromise("getBLEDeviceCharacteristics", { deviceId, serviceId, }) .then((res) => { let finished = false; let write = false; let notify = false; let indicate = false; var readId; var writeId; for (var i = 0; i < res.characteristics.length; i++) { if (!notify) { notify = res.characteristics[i].properties.notify; if (notify) readId = res.characteristics[i].uuid; } if (!indicate) { indicate = res.characteristics[i].properties.indicate; if (indicate) readId = res.characteristics[i].uuid; } if (!write) { write = res.characteristics[i].properties.write; writeId = res.characteristics[i].uuid; } if ((notify || indicate) && write) { /* 获取蓝牙特征值uuid */ success && success({ serviceId, writeId: writeId, readId: readId, }); finished = true; break; } } if (!finished) { getDeviceCharacteristics(deviceId, services, success, fail); } }) .catch((res) => { getDeviceCharacteristics(deviceId, services, success, fail); }); } else { fail && fail(); } }
思路就是
1.先扫描蓝牙 选中具体的 deviceId后 进行连接蓝牙
2.连接蓝牙后 进行连接服务
3.服务连接后 进行获取特性
分为三步 三步完事后 基本上就连上蓝牙了
连完蓝牙 画完图片之后呢 我们就可以获取到具体的点位信息了
//获取画布里的图片数据 uni.canvasGetImageData({ canvasId: "secondCanvas", // canvas 唯一id x: 0, y: 0, width: canvasWidth, // 之前保存的画布宽高 height: canvasHeight, success: (res) => { const pix = res.data; const opt = { // 获取蓝牙配置文件 deviceId: this.deviceId, // 设备id serviceId: this.serviceId, // 设备服务 characteristicId: this.characteristicId, // 设备特性 printAlign: "left", lineByLine: true, onProgress: (percentage) => { self.percentage = percentage; // 回调的进度 }, lasterSuccess: () => { console.log("laster success"); self.printing = false; self.percentage = 0; uni.showModal({ title: "提示", content: "数据已发送完,请检查打印的内容是否正常", showCancel: false, confirmText: "好的", }); }, }; this.printing = true; //打印图片 printImage( opt, // 将图片数据转成位图数据 overwriteImageData({ threshold: threshold[0], imageData: pix, width: canvasWidth, height: canvasHeight, }) ); }, complete: () => {}, });
获取完图片后 调用打印 打印代码如下:
// 将打印内容进行转换 区分逐行打印和整体打印 function getImageCommandArray(opt = {}, imageInfo = {}) { const lineByLine = typeof opt.lineByLine !== "boolean" ? true : opt.lineByLine; const width = imageInfo.width; const h = imageInfo.height; const xl = width % 256; const xh = (width - xl) / 256; const yl = h % 256; const yh = (h - yl) / 256; //打印图片的十进制指令数组 let command = []; if (lineByLine) { // 分段逐行的指令 command = command.concat([29, 118, 48, 0, xl, xh, 1, 0]); } else { // 非分段逐行的指令 command = command.concat([29, 118, 48, 0, xl, xh, yl, yh]); } return command; } // 获取图片信息 function overwriteImageData(data) { let sendWidth = data.width, sendHeight = data.height; const threshold = data.threshold || 180; let sendImageData = new ArrayBuffer((sendWidth * sendHeight) / 8); sendImageData = new Uint8Array(sendImageData); let pix = data.imageData; const part = []; let index = 0; for (let i = 0; i < pix.length; i += 32) { //横向每8个像素点组成一个字节(8位二进制数)。 for (let k = 0; k < 8; k++) { const grayPixle1 = grayPixle(pix.slice(i + k * 4, i + k * 4 + (4 - 1))); //阈值调整 if (grayPixle1 > threshold) { //灰度值大于128位 白色 为第k位0不打印 part[k] = 0; } else { part[k] = 1; } } let temp = 0; for (let a = 0; a < part.length; a++) { temp += part[a] * Math.pow(2, part.length - 1 - a); } //开始不明白以下算法什么意思,了解了字节才知道,一个字节是8位的二进制数,part这个数组存的0和1就是二进制的0和1,传输到打印的位图数据的一个字节是0-255之间的十进制数,以下是用相权相加法转十进制数,理解了这个就用上面的for循环替代了 // const temp = // part[0] * 128 + // part[1] * 64 + // part[2] * 32 + // part[3] * 16 + // part[4] * 8 + // part[5] * 4 + // part[6] * 2 + // part[7] * 1; sendImageData[index++] = temp; } return { array: Array.from(sendImageData), width: sendWidth / 8, height: sendHeight, }; } function getPrintImageWriteArray(opt = {}, imageInfo = {}) { const lineByLine = typeof opt.lineByLine !== "boolean" ? true : opt.lineByLine; const width = imageInfo.width; let arr = imageInfo.array; let writeArray = []; const iniTcommand = [] .concat(printCommand.clear) .concat(printCommand[opt.printAlign || "left"]); const command = getImageCommandArray(opt, imageInfo); writeArray.push(new Uint8Array(iniTcommand)); // 分段逐行打印的数据 if (lineByLine) { for (let i = 0; i < arr.length / width; i++) { const subArr = arr.slice(i * width, i * width + width); const tempArr = command.concat(subArr); writeArray.push(new Uint8Array(tempArr)); } } else { // 非逐行打印 writeArray.push(new Uint8Array(command.concat(arr))); } // writeArray.push(new Uint8Array([27, 74, 3])); return writeArray; } function sendDataToDevice(options) { let byteLength = options.value.byteLength; // 这里默认一次20个字发送 const speed = options.onceByleLength || 20; options.reTry = options.reTry || 20; if (byteLength > 0) { uni.writeBLECharacteristicValue({ ...options, // writeType:"writeNoResponse", value: options.value.slice(0, byteLength > speed ? speed : byteLength), success: function (res) { if (byteLength > speed) { sendDataToDevice({ ...options, value: options.value.slice(speed, byteLength), }); } else { options.lasterSuccess && options.lasterSuccess(); } }, fail: function (res) { // console.log('options.nowTry', options.nowTry); options.nowTry = options.nowTry ? (options.nowTry += 1) : 1; if (options.nowTry < options.reTry && res.errCode === 10007) { if (byteLength > speed) { sendDataToDevice({ ...options, value: options.value.slice(speed, byteLength), }); } else { options.lasterSuccess && options.lasterSuccess(); } } options.onError && options.onError(res); }, }); } } export function sendDataToPrint(options, allUint8Array) { const allLenth = allUint8Array.length; const writeArrayCopyer = allUint8Array.slice(0); const print = (options, writeArray) => { if (writeArray.length) { sendDataToDevice({ ...options, value: writeArray.shift().buffer, lasterSuccess: () => { if (writeArray.length) { print(options, writeArray); } else { options.lasterSuccess && options.lasterSuccess(); } // console.log("writeArray.length", writeArray.length); options.onProgress && options.onProgress(Math.floor(((allLenth - writeArray.length) / allLenth) * 100)); }, }); } }; print(options, writeArrayCopyer); } function printImage(opt, imageInfo) { sendDataToPrint( opt, getPrintImageWriteArray( { printAlign: opt.printAlign, lineByLine: opt.lineByLine, }, imageInfo ) ); }
案例参照:https://juejin.cn/post/7002878986472652830
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。