cropper.js 19 KB

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