首页
学习
关于
友链
Search
1
小程序 蓝牙连接(出现的问题和一些解决方法)
327 阅读
2
颜色空间:RGB、HSV和HSL
160 阅读
3
小程序_连接蓝牙设备根据平台去打开定位权限
143 阅读
4
小程序 加快安卓手机向蓝牙设备发送大数据
96 阅读
5
侧边菜单切换的显示与隐藏,图标的 + 与 -
93 阅读
全部
感想
旅行
生活
学习
登录
Search
标签搜索
css
javascript
jquery
html
小程序
github
图床
假期
发布订阅
typecho
第一次
未来
快乐与忧伤
努力
奋斗
PicGo
倒计时
元旦
svg
vue
逸曦穆泽
累计撰写
35
篇文章
累计收到
40
条评论
首页
栏目
全部
感想
旅行
生活
学习
页面
学习
关于
友链
搜索到
1
篇与
图片
的结果
2022-12-17
小程序_图片转canvas,上传和rgb888转rgb565发送到设备
序: 小程序需要将图片上传到蓝牙设备,那么图片要转为二进制数据,转为二进制的图片数据不能过大,就要裁剪图片,因为要尽量减少图片的二进制数据,要将图片的rgb888转为rgb565,也就是24位缩小为16位数据;例如:100像素*100像素的图片,就有1万个点,一个点包含rgba四个数,只需要rgb这三个数,rgb三个数各包含8位(也就是二进制,为rgb888),rgb888为三个字节,rgb565为两个字节,数据量传输就少了三分一,但也有几十K的数据了,分240个字节每包发送完也要十秒左右;如果要上传,上传到后端的路径要是临时路径才可以,不能是本地路径,所以要使用 canvas 转一下得到 tempFilePath 的临时路径,一、图片渲染为 canvas 得到临时路径,并上传// 选择本地图片渲染成 canvas locationImg(e) { let imgUrl = e.currentTarget.dataset.img; let that = this; gbImgComStr = ""; wx.createSelectorQuery().select('#canvasImg').fields({ node: true, size: true }).exec((res) => { const canvas = res[0].node; const ctx = canvas.getContext('2d'); var dpr = wx.getSystemInfoSync().pixelRatio // 设备像素比 canvas.width = res[0].width * dpr canvas.height = res[0].height * dpr ctx.scale(dpr, dpr) const img = canvas.createImage(); img.onload = () => { ctx.drawImage(img, 0, 0, 200, 200); wx.canvasToTempFilePath({ x: 0, y: 0, width: 200, height: 200, destWidth: 200, destHeight: 200, canvas: canvas, fileType: "jpg", quality: .8, success(res) { that.initImgData(res.tempFilePath); // 使用服务器 } }) } img.src = imgUrl; }) // console.log(imgUrl) }, // 图片上传到服务器 initImgData(imgUrl) { let that = this; let gbImgPath = ""; wx.uploadFile({ url: apiUrl+'/imgUpload', //你的后端上传路径 filePath: imgUrl, name: "imgPath", // 键值key header: { "content-type": "multipart/form-data"}, success: function (res) { if (res.statusCode == 200) { gbImgPath = JSON.parse(res.data).imgPath; // 存放到服务器上的图片路径 } }, fail: function (err) { wx.showToast({ title: "上传失败", icon: "none", duration: 2000 }) }, }) // 图片回显 或 gbImgPath that.setData({ imgPath: imgUrl }) },后端接口(PHP)://对应的文件夹 public function dirFile($name){ $dirName = "store/".$name."/".date("Ym"); if(!file_exists($dirName)){ mkdir(app()->getRootPath().'public/'.$dirName,0777,true); } return $dirName; } // 图片处理 function imgUpload(){ if(request()->isPost()){ $dirName = $this -> dirFile('image'); $file = $_FILES['imgPath']; // 获取小程序传来的图片 if(is_uploaded_file($file['tmp_name'])) { $type = $file["type"];//文件类型 如:image/png $ext = strrchr($type, '/');//切割字符串 //类型判断 if (in_array($ext, ['/jpeg', '/jpg', '/png', '/svg', '/gif','ico'])) { $filePrefix1 = strstr($file["name"], '.', true);// 文件名前缀 无.png $filestr = date("Ymdhis").$filePrefix1; // 避免同一时间调用该方法时产生同一个文件名 $fileExt1 = strstr($file["name"], '.'); // 获取 .png $fileName = $dirName . "/" . md5($filestr) . $fileExt1; //拼接文件路径 //判断文件是否存在 // if (file_exists($fileName)) { // return json(["statusCode" => 200,"msg"=>"exist","imgPath" => $fileName]); // } else { //保存文件 ($_FILES["文件名"]["tmp_name"],newPath),(旧路径,新路径) move_uploaded_file($file["tmp_name"], $fileName); return json(["statusCode" => 200,"msg"=>"success","imgPath" => $fileName]); // } } return json(["statusCode" => 402,"msg"=>"format error"]); } return json(["statusCode"=>401,"msg"=>"file error"]); } return json(["statusCode"=>400,"msg"=>"request error"]); }二、在小程序上将图片数据rgb888转为rgb565var gbImgComStr = ""; // 图片:16进制补零 function imgHexFill(hexStr) { let len = hexStr.length; var map = { 1: "000", 2: "00", 3: "0" } if (map[len]) { hexStr = map[len] + hexStr; } return hexStr; } // 上为 Page 外,下为 Page 内 // 图片画成 canvas 获取数据 initImgData565: async function(imgUrl) { return new Promise((resolve, reject) => { let that = this; imgLimitNum = 0; wx.createSelectorQuery().select('#canvasImg').fields({ node: true, size: true }).exec((res) => { const canvas = res[0].node; const ctx = canvas.getContext('2d'); // var dpr = wx.getSystemInfoSync().pixelRatio // 设备像素比 var dpr = 1; canvas.width = res[0].width * dpr canvas.height = res[0].height * dpr // ctx.scale(dpr, dpr) let sideLen = 100; ctx.fillRect(0, 0, sideLen, sideLen); const img = canvas.createImage(); img.onload = () => { ctx.drawImage(img, 0, 0, sideLen, sideLen); var imageData = ctx.getImageData(0, 0, sideLen, sideLen).data; // console.log(imageData.length) var rgbStr = ""; var rgb = 0; var rgb565 = 0; var w = 0; var h = 0; // 逐列式 (逐行式则是Z扫描) for (var w = 0; w < sideLen; w++) { for (var h = 0; h < sideLen; h++) { var offset = (h*sideLen + w)*4; var red = imageData[offset]; var green = imageData[offset + 1]; var blue = imageData[offset + 2]; rgb = 0xff000000 | (red << 16) | (green << 8) | blue; rgb565 = ((rgb & 0xf80000) >> 8) | ((rgb & 0xfc00) >> 5) | ((rgb & 0xf8) >> 3) rgbStr += imgHexFill(rgb565.toString(16)); } } gbImgComStr = rgbStr.toUpperCase(); imgUrl != "" ? resolve() : reject(); } img.src = imgUrl; }) }) },由于数据过大,这些数据还要分包发送到蓝牙设备,首先,要发第一包数据告诉设备要发图片的指令了,可以加入长度,这个要和设备那边沟通,分包发送还不能全部直接发,不然蓝牙设备很容易造成丢包的,小程序发完了,设备那边可能才获取十分之一呢,要分20、30包发完后再发一个确定指令,如果返回正确的指令则继续分20、30包发下去再发一个确定指令,然后重复发完为止;部分代码:// 图片_下个数据包指令 imgNextCmd: async function(){ let that = this; let sData = that.data; let comStr = "", roleData = "", command = ""; let rlt = ""; let packageNum = 30; // 连续发送包的个数 var rowSum = ((imgPackageSum - imgIndex) >= packageNum) ? (imgIndex + packageNum) : imgPackageSum; for (let row = imgIndex; row < rowSum; row++) { comStr = gbImgComStr.substr((row)*(imgCharLen-10),(imgCharLen-10)); roleData = "AC" + decToHexLenFill(row+1) + decToHex(comStr.length/2) + comStr; let res = new Uint8Array(roleData.match(/[\da-f]{2}/gi).map(function (h) { return parseInt(h, 16) })); command = roleData + arrSumCheck(res); imgIndex += 1; // await sleep(50); rlt = await that.sendCom(command, sData.deviceId, sData.serviceId, sData.characteristicId); if (rlt == "success") { // 包确认 if(row == (rowSum-1)){ // console.log(">>> sure",rowSum)// 成功 let sureCom = "BCxxxx"+decToHexLenFill(row+1); let cs = new Uint8Array(sureCom.match(/[\da-f]{2}/gi).map(function (h) { return parseInt(h, 16) })); let sureCommand = sureCom + arrSumCheck(cs); that.writeBLECharacteristicValue(sureCommand); } } } if(rowSum == imgPackageSum && imgLimitNum == 0){ imgLimitNum = 1; let endCom = "xxx"; // 传输完毕 that.writeBLECharacteristicValue(endCom); } },彩蛋: 一般一个包只能发20个字节,你觉得一张150*150的图片数据发完需要多久?要两分钟左右吧,这个还没考虑中途断连等情况呢!你觉得你是用户,你会接受吗?所以,可以先获取一下mtu是很有必要的,一般Android的是23(包含头部3个字节信息),iOS的是512,有部分Android手机是没有mtu值的,只能落空默认值了,Android可以协商mtu值 wx.setBLEMTU ,你可以一个包发256、300个字节,看你需求,过大会产生一些问题,并且每包发送也可以设置一下延迟多少毫秒,也可以避免传输过程中断连问题。
2022年12月17日
55 阅读
0 评论
0 点赞