cropper.js 20 KB

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