cropper.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. // pages/piano/cropper/cropper.js
  2. const { BtCmd } = require('../../../devices/bluetooth/bt_cmd');
  3. import EventManager from '../../../utils/event_bus'
  4. import { EnumCmdEvent, CmdEvent } from '../../../devices/cmd_key_event';
  5. import js_md5 from '../../../utils/js_md5';
  6. import store from '../../../utils/store';
  7. const { BtHelper } = require('../../../devices/bt_helper');
  8. Page({
  9. /**
  10. * 页面的初始数据
  11. */
  12. data: {
  13. navbarData: {
  14. showCapsule: 1, //是否显示左上角图标 1表示显示 0表示不显示
  15. title: '壁纸设置', //导航栏 中间的标题
  16. callback: true,
  17. },
  18. src: "",
  19. width: 320,//宽度
  20. height: 320,//高度
  21. showProgress: false,
  22. _chunks: [],
  23. progress: 0,
  24. _timer: null,
  25. _imgUrl: null,
  26. _imgMD5: null,
  27. _imgIndex: null,
  28. _imgTopPic: null,
  29. _imgNext: 0,
  30. max_scale: 2,
  31. _hasPermission: false,
  32. wallpaper: {},
  33. },
  34. cropper: null,
  35. callback() {
  36. let that = this
  37. if (this.data.progress > 0) {
  38. wx.showModal({
  39. title: '保存壁纸中,确定要中断退出吗?',
  40. content: '',
  41. complete: (res) => {
  42. if (res.confirm) {
  43. that.endImage(2)
  44. wx.navigateBack({
  45. delta: 1,
  46. })
  47. }
  48. }
  49. })
  50. } else {
  51. wx.navigateBack({
  52. delta: 1,
  53. })
  54. }
  55. },
  56. cropperload(e) {
  57. console.log('cropper加载完成');
  58. },
  59. upload() {
  60. let that = this;
  61. wx.chooseMedia({
  62. count: 1,
  63. mediaType: ['image'],
  64. sourceType: ['album'],
  65. success(res) {
  66. wx.showLoading({
  67. title: '加载中',
  68. })
  69. const tempFilePaths = res.tempFiles[0].tempFilePath;
  70. // //重置壁纸角度、缩放、位置
  71. that.cropper.imgReset();
  72. that.setData({
  73. src: tempFilePaths
  74. });
  75. // todo 测试用
  76. // that.testUpload(tempFilePaths)
  77. }, fail(res) {
  78. console.log(res)
  79. }
  80. })
  81. },
  82. testUpload(url) {
  83. let _this = this
  84. // 读取裁剪的jpg图片
  85. const fs = wx.getFileSystemManager();
  86. fs.readFile({
  87. filePath: url,
  88. encoding: '', // 不指定编码以获取原始二进制数据
  89. success: (obj) => {
  90. console.log("加载文件成功:", obj.data.byteLength, obj.data.length)
  91. fs.getFileInfo({
  92. "filePath": url, "digestAlgorithm": "md5", success: (res) => {
  93. console.log("md5:", res)
  94. _this.data._imgMD5 = res.digest
  95. _this.data._imgIndex = 0
  96. console.log("加载文件成功:", obj.data.byteLength, obj.data.length, obj.getFileInfo)
  97. let binData = obj.data;
  98. // let myMd5 = _this.getImgMd5(binData)
  99. // let my2Md5 = _this.getImg2Md5(binData)
  100. // let my3Md5 = _this.getImg3Md5(binData)
  101. wx.setClipboardData({
  102. data: res.digest,
  103. fail: (err) => {
  104. console.error('setClipboardData失败:', err);
  105. },
  106. success: (res) => {
  107. console.log('setClipboardData成功:', res);
  108. },
  109. })
  110. _this.sliceDataIntoChunks(binData, 64);
  111. console.log("加载文件成功2:", binData.byteLength, binData.length)
  112. _this.data._imageBufferLength = binData.byteLength ?? binData.length
  113. wx.hideLoading();
  114. wx.showLoading({
  115. title: '开始传输壁纸',
  116. })
  117. console.log("md5 2:", _this.data._imgMD5)
  118. _this.startImage()
  119. }, fail: (err) => {
  120. console.error('getFileInfo失败:', err);
  121. },
  122. })
  123. },
  124. fail: (err) => {
  125. console.error('读取 .bin 文件失败2:', err);
  126. },
  127. });
  128. // 651kb的
  129. // _this.downloadAndSaveFile("https://music-play.oss-cn-shenzhen.aliyuncs.com/backOss/file/8770a6097d9940b59032d099b2fdde3b.bin");
  130. },
  131. loadimage(e) {
  132. wx.hideLoading();
  133. console.log('壁纸', e);
  134. this.cropper.imgReset();
  135. },
  136. clickcut(e) {
  137. console.log("clickcut:", e.detail);
  138. //壁纸预览
  139. wx.previewImage({
  140. current: e.detail.url,
  141. urls: [e.detail.url]
  142. })
  143. },
  144. cancel() {
  145. wx.navigateBack({
  146. delta: -1
  147. })
  148. },
  149. submit() {
  150. let _this = this
  151. // if (_this.data._chunks.length > 0) {
  152. // return;
  153. // }
  154. // 读取裁剪的jpg图片
  155. const fs = wx.getFileSystemManager();
  156. this.cropper.getImg((res) => {
  157. wx.showLoading({
  158. title: '壁纸裁剪中',
  159. })
  160. // app.globalData.imgSrc = obj.url;
  161. console.log("裁剪壁纸:", res);
  162. _this.data._imgUrl = res.url
  163. _this.data._imgTopPic = res.url
  164. fs.readFile({
  165. filePath: _this.data._imgUrl,
  166. encoding: '', // 不指定编码以获取原始二进制数据
  167. success: (obj) => {
  168. console.log("加载文件成功:", obj.data.byteLength, obj.data.length)
  169. fs.getFileInfo({
  170. "filePath": _this.data._imgUrl, "digestAlgorithm": "md5", success: (res2) => {
  171. console.log("md5:", res2)
  172. _this.data._imgMD5 = res2.digest
  173. _this.data._imgIndex = 0
  174. console.log("加载文件成功:", obj.data.byteLength, obj.data.length, obj.getFileInfo)
  175. let binData = obj.data;
  176. // let myMd5 = _this.getImgMd5(binData)
  177. // let my2Md5 = _this.getImg2Md5(binData)
  178. // let my3Md5 = _this.getImg3Md5(binData)
  179. // todo 测试用
  180. // wx.setClipboardData({
  181. // data: res2.digest,
  182. // })
  183. _this.sliceDataIntoChunks(binData, 64);
  184. console.log("加载文件成功2:", binData.byteLength, binData.length)
  185. _this.data._imageBufferLength = binData.byteLength ?? binData.length
  186. wx.hideLoading();
  187. wx.showLoading({
  188. title: '开始传输壁纸',
  189. })
  190. console.log("md5 2:", _this.data._imgMD5)
  191. _this.startImage()
  192. }, fail: (err) => {
  193. console.error('getFileInfo失败:', err);
  194. },
  195. })
  196. },
  197. fail: (err) => {
  198. s
  199. wx.hideLoading()
  200. wx.showToast({
  201. title: '读取该图片失败',
  202. })
  203. console.error('读取 .bin 文件失败:', err);
  204. },
  205. });
  206. });
  207. // 651kb的
  208. // _this.downloadAndSaveFile("https://music-play.oss-cn-shenzhen.aliyuncs.com/backOss/file/8770a6097d9940b59032d099b2fdde3b.bin");
  209. },
  210. checkAndRequestImagePermission: function () {
  211. const _this = this;
  212. // 检查用户是否已经授权访问相册
  213. wx.getSetting({
  214. success(res) {
  215. if (!res.authSetting['scope.writePhotosAlbum']) {
  216. // 用户未授权访问相册,请求用户授权
  217. wx.authorize({
  218. scope: 'scope.writePhotosAlbum',
  219. success() {
  220. // 用户同意授权
  221. console.log('用户已授权访问相册1');
  222. _this.upload()
  223. // 可以在这里执行访问相册的操作
  224. },
  225. fail() {
  226. // 用户拒绝授权
  227. console.log('用户拒绝授权访问相册');
  228. wx.showModal({
  229. title: '提示',
  230. content: '您拒绝了访问相册的权限,无法上传壁纸',
  231. showCancel: false,
  232. success(res) {
  233. if (res.confirm) {
  234. // 跳转到设置页面
  235. wx.openSetting({
  236. success(settingRes) {
  237. if (settingRes.authSetting['scope.writePhotosAlbum']) {
  238. console.log('用户已在设置中开启访问相册的权限');
  239. // 可以在这里执行访问相册的操作
  240. _this.upload(); //上传壁纸
  241. } else {
  242. console.log('用户仍未授权访问相册');
  243. }
  244. }
  245. });
  246. }
  247. }
  248. });
  249. }
  250. });
  251. } else {
  252. // 用户已授权访问相册
  253. console.log('用户已授权访问相册2');
  254. // 可以在这里执行访问相册的操作
  255. _this.upload()
  256. }
  257. }
  258. });
  259. },
  260. async startImage() {
  261. console.log("开始发送壁纸数据: 0x78",)
  262. BtHelper.sendCallBack(BtCmd.wallPaper(1), function (res) {
  263. if (!res) {
  264. wx.hideLoading()
  265. wx.showToast({
  266. title: '发送图片失败',
  267. icon: 'error'
  268. })
  269. }
  270. });
  271. },
  272. sliceDataIntoChunks(data, chunkSize) {
  273. let buffer = data;
  274. console.log("发送壁纸数据:", buffer.byteLength, buffer.length)
  275. let total = buffer.byteLength || buffer.length
  276. const chunks = [];
  277. // for (let i = 0; i < data.length; i += chunkSize) {
  278. for (let i = 0; i < total; i += chunkSize) {
  279. const chunk = buffer.slice(i, i + chunkSize);
  280. chunks.push(chunk);
  281. }
  282. // const sliceSize = chunkSize / 2; // 每个 RGB565 占 2 字节
  283. // const chunks = [];
  284. // for (let i = 0; i < total; i += sliceSize) {
  285. // chunks.push(buffer.slice(i, i + sliceSize));
  286. // }
  287. this.data._chunks = chunks;
  288. },
  289. getImgMd5(buffer) {
  290. let md5ctx = js_md5.md5_init();
  291. js_md5.md5_update(md5ctx, buffer, buffer.byteLength ?? buffer.length);
  292. let result = js_md5.md5_final(md5ctx);
  293. let md5 = js_md5.md5_encode_hex(result, 16);
  294. console.log("result:", md5, result);
  295. return md5
  296. },
  297. sendImageMD5() {
  298. let lengthCode = this.data._chunks.length
  299. console.log("发送壁纸MD5 1:", lengthCode, this.data._imgMD5)
  300. let value = BtCmd.sendWiFiInfo("99", this.data._imgMD5)
  301. console.log("发送壁纸MD5 2:", value)
  302. BtHelper.sendCallBack(BtCmd.wallPaperMD5(value), function (res) {
  303. if (!res) {
  304. wx.hideLoading()
  305. wx.showToast({
  306. title: '发送图片失败',
  307. icon: 'error'
  308. })
  309. }
  310. });
  311. },
  312. // 切分高位和低位
  313. splitHighLowBytes(value) {
  314. let highByte = (value >> 8) & 0xFF;
  315. let lowByte = value & 0xFF;
  316. return { highByte, lowByte };
  317. },
  318. async sendImage(index) {
  319. let _this = this
  320. wx.hideLoading()
  321. let chunks = _this.data._chunks
  322. let total = _this.data._imageBufferLength;
  323. let btHelper = BtHelper.getInstance();
  324. // let i = 0;
  325. // _this.data._timer = setInterval(async () => {
  326. if (index > chunks.length - 1) {
  327. // wx.showModal({
  328. // title: '壁纸上传完成md5:' + _this.data._imgMD5,
  329. // content: '最终发送数据大小:' + _this.data._imgNext + ',总大小:' + total + '',
  330. // showCancel: false,
  331. // success(res) {
  332. // }
  333. // })
  334. _this.endImage(0)
  335. return;
  336. }
  337. const chunk = chunks[index];
  338. _this.data._imgNext += (chunk.byteLength ?? chunk.length);
  339. console.log("发送壁纸数据1:", index, ":", _this.data._imgNext, ":", chunk.length, ":", chunk.byteLength, ":", total, chunks.length)
  340. let res = await btHelper.wallPaperSyncData(chunk);
  341. // let res = true;
  342. // btHelper.wallPaperData(chunk);
  343. if (!res) {
  344. wx.showModal({
  345. title: '壁纸上传失败',
  346. showCancel: false
  347. })
  348. _this.endImage(2)
  349. }
  350. _this.data._imgIndex += 1;
  351. _this.updateProgress(_this.data._imgNext, total);
  352. // }, 30);
  353. },
  354. async delay(ms) {
  355. return new Promise(resolve => setTimeout(resolve, ms));
  356. },
  357. endImage(value) {
  358. let _this = this
  359. console.log("结束壁纸上传:", value)
  360. if (_this.data._timer) {
  361. clearInterval(_this.data._timer)
  362. _this.data._timer = null
  363. }
  364. _this.setData({
  365. progress: 0,
  366. showProgress: false
  367. })
  368. _this.data._chunks = []
  369. _this.data._imgNext = 0
  370. _this.data._imgMD5 = null
  371. _this.data._imgIndex = 0
  372. BtHelper.getInstance().wallPaper(value);
  373. },
  374. startProgress() {
  375. this.setData({
  376. progress: 0,
  377. showProgress: true
  378. })
  379. },
  380. updateProgress(chunk, total) {
  381. let progress = parseFloat((chunk / total * 100).toFixed(2));
  382. let _this = this
  383. if (chunk >= total) {
  384. _this.setData({
  385. progress: 100,
  386. showCropImg: false
  387. });
  388. } else {
  389. _this.setData({
  390. progress: progress,
  391. });
  392. }
  393. },
  394. successSend() {
  395. let _this = this
  396. wx.showModal({
  397. title: '保存壁纸成功',
  398. showCancel: false,
  399. success(res) {
  400. if (res.confirm) {
  401. const pages = getCurrentPages();
  402. // 获取上一级页面实例
  403. const prevPage = pages[pages.length - 2];
  404. // 传递参数
  405. prevPage.updateTopImg(_this.data._imgTopPic)
  406. wx.navigateBack({
  407. delta: 1,
  408. })
  409. }
  410. }
  411. })
  412. },
  413. failSend(showToast) {
  414. let _this = this
  415. _this.data._chunks = null
  416. _this.data._imgMD5 = null
  417. _this.data._imgIndex = 0
  418. _this.data._imgNext = 0
  419. wx.hideLoading()
  420. if (showToast) {
  421. // todo 测试注释的
  422. // wx.showModal({
  423. // title: '壁纸上传失败了',
  424. // showCancel: false
  425. // })
  426. wx.showToast({
  427. title: '发送图片败',
  428. icon: 'failure'
  429. })
  430. }
  431. _this.setData({
  432. progress: 0,
  433. showCropImg: false
  434. });
  435. },
  436. hasImage(image) {
  437. let that = this;
  438. that.cropper.imgReset();
  439. // 有图片的进制缩放
  440. that.setData({
  441. src: image.pic,
  442. max_scale: 1,
  443. });
  444. that.data._imgTopPic = image.pic
  445. },
  446. disconnect(event) {
  447. let _this = this;
  448. console.log("断开连接", event.commonValue, event.deviceId);
  449. if (event.commonValue === "offline") {
  450. wx.showModal({
  451. title: '提示',
  452. content: '设备断开连接,请重新连接设备',
  453. showCancel: false,
  454. success: function (res) {
  455. if (res.confirm) {
  456. wx.navigateBack({
  457. delta: 1
  458. })
  459. }
  460. }
  461. })
  462. }
  463. // 设备断开连接,请重新连接设备
  464. },
  465. /**
  466. * 生命周期函数--监听页面加载
  467. */
  468. onLoad(options) {
  469. this.cropper = this.selectComponent("#image-cropper");
  470. this.cropper.imgReset();
  471. let image = options.param
  472. let json = JSON.parse(image)
  473. console.log("裁剪页:", image)
  474. if (json.pic) {
  475. this.hasImage(json)
  476. } else {
  477. console.log("没有图片")
  478. this.checkAndRequestImagePermission()
  479. }
  480. // this.setData({
  481. // scr:json,
  482. // })
  483. let _this = this;
  484. EventManager.addNotification(CmdEvent.eventName, function (event) {
  485. let name = event.cmdEvent;
  486. let value = event.wallpaper;
  487. let kind = event.heiJiaoKind;
  488. console.log("裁剪页:", name, value, kind)
  489. switch (name) {
  490. case EnumCmdEvent.onoffline:
  491. _this.disconnect(event)
  492. break;
  493. case EnumCmdEvent.wallpaper:
  494. //0x78
  495. // 小程序:0x78, 1。 开启壁纸
  496. // 黑胶回:0x78, 2, 1 ,1。 0x78, 2, 0, 2 。 1成功,2失败
  497. // 小程序:0x80, [参考wifi指令], [0x22,总长度,0x33,包长,包数,0x44,md5长,md5]
  498. // 黑胶回:0x80, 1, 1。 0x80, 0, 2。 1成功,2失败
  499. // 小程序:0x79, 数据长度,数据
  500. // 黑胶回:0x79, 1, 1 。0x79, 0, 2 。 1成功,2失败
  501. // 小程序:0x78, 0。结束了
  502. // 黑胶回:0x78, 2, 0, 1 。 0x78, 2, 0, 3 。 0流程成功,3流程失败
  503. // 小程序: 0x78, 2。异常结束
  504. if (value === 1 && kind == 1) {
  505. // 开始校验md5
  506. _this.sendImageMD5()
  507. // _this.sendImage()
  508. _this.startProgress()
  509. } else if (value === 0 && (kind == 2 || kind == 3)) {
  510. // 发送失败
  511. _this.failSend(true)
  512. } else if (value === 0 && kind == 1) {
  513. // 发送完成
  514. _this.successSend()
  515. } else {
  516. // 发送失败
  517. _this.failSend(true)
  518. }
  519. break;
  520. case EnumCmdEvent.wallPaperMD5:
  521. // 0x80
  522. console.log("开始发送MD5回复", kind, value)
  523. // 收到回复md5,开始发送图片
  524. if (kind == 1 && value === 1) {
  525. _this.sendImage(0)
  526. } else {
  527. _this.failSend(true)
  528. }
  529. break;
  530. case EnumCmdEvent.wallPaperData:
  531. // 0x79
  532. // 收到回复md5,开始发送图片
  533. if (kind == 1 && value === 1) {
  534. _this.sendImage(_this.data._imgIndex)
  535. } else {
  536. _this.failSend(true)
  537. }
  538. break;
  539. default:
  540. break;
  541. }
  542. }, this)
  543. },
  544. /**
  545. * 生命周期函数--监听页面初次渲染完成
  546. */
  547. onReady() {
  548. },
  549. /**
  550. * 生命周期函数--监听页面显示
  551. */
  552. onShow() {
  553. },
  554. /**
  555. * 生命周期函数--监听页面隐藏
  556. */
  557. onHide() {
  558. },
  559. /**
  560. * 生命周期函数--监听页面卸载
  561. */
  562. onUnload() {
  563. EventManager.removeNotification(CmdEvent.eventName, this);
  564. },
  565. /**
  566. * 页面相关事件处理函数--监听用户下拉动作
  567. */
  568. onPullDownRefresh() {
  569. },
  570. /**
  571. * 页面上拉触底事件的处理函数
  572. */
  573. onReachBottom() {
  574. },
  575. /**
  576. * 用户点击右上角分享
  577. */
  578. onShareAppMessage() {
  579. }
  580. })