cropper.js 19 KB

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