limeClipper.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816
  1. <template>
  2. <view class="l-clipper" :class="{open: value}" disable-scroll :style="'z-index: ' + zIndex + ';' + customStyle">
  3. <view class="l-clipper-mask" @touchstart.stop.prevent="clipTouchStart" @touchmove.stop.prevent="clipTouchMove" @touchend.stop.prevent="clipTouchEnd">
  4. <view class="l-clipper__content" :style="clipStyle"><view class="l-clipper__edge" v-for="(item, index) in [0, 0, 0, 0]" :key="index"></view></view>
  5. </view>
  6. <image
  7. class="l-clipper-image"
  8. @error="imageLoad"
  9. @load="imageLoad"
  10. @touchstart.stop.prevent="imageTouchStart"
  11. @touchmove.stop.prevent="imageTouchMove"
  12. @touchend.stop.prevent="imageTouchEnd"
  13. :src="image"
  14. :mode="imageWidth == 'auto' ? 'widthFix' : ''"
  15. v-if="image"
  16. :style="imageStyle"
  17. />
  18. <canvas
  19. :canvas-id="canvasId"
  20. id="l-clipper"
  21. disable-scroll
  22. :style="'width: ' + canvasWidth * scaleRatio + 'px; height:' + canvasHeight * scaleRatio + 'px;'"
  23. class="l-clipper-canvas"
  24. ></canvas>
  25. <view class="l-clipper-tools">
  26. <view class="l-clipper-tools__btns">
  27. <view v-if="isShowCancelBtn" @tap="cancel">
  28. <slot name="cancel" v-if="$slots.cancel" />
  29. <view v-else class="cancel">取消</view>
  30. </view>
  31. <view v-if="isShowPhotoBtn" @tap="uploadImage">
  32. <slot name="photo" v-if="$slots.photo" />
  33. <image v-else src="uni_modules/uni-id-pages/static/limeClipper/photo.svg" />
  34. </view>
  35. <view v-if="isShowRotateBtn" @tap="rotate">
  36. <slot name="rotate" v-if="$slots.rotate" />
  37. <image v-else src="uni_modules/uni-id-pages/static/limeClipper/rotate.svg" data-type="inverse" />
  38. </view>
  39. <view v-if="isShowConfirmBtn" @tap="confirm">
  40. <slot name="confirm" v-if="$slots.confirm" />
  41. <view v-else class="confirm">确定</view>
  42. </view>
  43. </view>
  44. <slot></slot>
  45. </view>
  46. </view>
  47. </template>
  48. <script>
  49. import { determineDirection, calcImageOffset, calcImageScale, calcImageSize, calcPythagoreanTheorem, clipTouchMoveOfCalculate, imageTouchMoveOfCalcOffset } from './utils';
  50. const cache = {}
  51. export default {
  52. // version: '0.6.3',
  53. name: 'l-clipper',
  54. props: {
  55. value: {
  56. type: Boolean,
  57. default: true
  58. },
  59. // #ifdef MP-WEIXIN
  60. type: {
  61. type: String,
  62. default: '2d'
  63. },
  64. // #endif
  65. customStyle: {
  66. type: String,
  67. },
  68. canvasId: {
  69. type: String,
  70. default: 'l-clipper'
  71. },
  72. zIndex: {
  73. type: Number,
  74. default: 99
  75. },
  76. imageUrl: {
  77. type: String
  78. },
  79. fileType: {
  80. type: String,
  81. default: 'png'
  82. },
  83. quality: {
  84. type: Number,
  85. default: 1
  86. },
  87. width: {
  88. type: Number,
  89. default: 400
  90. },
  91. height: {
  92. type: Number,
  93. default: 400
  94. },
  95. minWidth: {
  96. type: Number,
  97. default: 200
  98. },
  99. maxWidth: {
  100. type: Number,
  101. default: 600
  102. },
  103. minHeight: {
  104. type: Number,
  105. default: 200
  106. },
  107. maxHeight: {
  108. type: Number,
  109. default: 600
  110. },
  111. isLockWidth: {
  112. type: Boolean,
  113. default: false
  114. },
  115. isLockHeight: {
  116. type: Boolean,
  117. default: false
  118. },
  119. isLockRatio: {
  120. type: Boolean,
  121. default: true
  122. },
  123. scaleRatio: {
  124. type: Number,
  125. default: 1
  126. },
  127. minRatio: {
  128. type: Number,
  129. default: 0.5
  130. },
  131. maxRatio: {
  132. type: Number,
  133. default: 2
  134. },
  135. isDisableScale: {
  136. type: Boolean,
  137. default: false
  138. },
  139. isDisableRotate: {
  140. type: Boolean,
  141. default: false
  142. },
  143. isLimitMove: {
  144. type: Boolean,
  145. default: false
  146. },
  147. isShowPhotoBtn: {
  148. type: Boolean,
  149. default: true
  150. },
  151. isShowRotateBtn: {
  152. type: Boolean,
  153. default: true
  154. },
  155. isShowConfirmBtn: {
  156. type: Boolean,
  157. default: true
  158. },
  159. isShowCancelBtn: {
  160. type: Boolean,
  161. default: true
  162. },
  163. rotateAngle: {
  164. type: Number,
  165. default: 90
  166. },
  167. source: {
  168. type: Object,
  169. default: () => ({
  170. album: '从相册中选择',
  171. camera: '拍照',
  172. // #ifdef MP-WEIXIN
  173. message: '从微信中选择'
  174. // #endif
  175. })
  176. }
  177. },
  178. data() {
  179. return {
  180. canvasWidth: 0,
  181. canvasHeight: 0,
  182. clipX: 0,
  183. clipY: 0,
  184. clipWidth: 0,
  185. clipHeight: 0,
  186. animation: false,
  187. imageWidth: 0,
  188. imageHeight: 0,
  189. imageTop: 0,
  190. imageLeft: 0,
  191. scale: 1,
  192. angle: 0,
  193. image: this.imageUrl,
  194. sysinfo: {},
  195. throttleTimer: null,
  196. throttleFlag: true,
  197. timeClipCenter: null,
  198. flagClipTouch: false,
  199. flagEndTouch: false,
  200. clipStart: {},
  201. animationTimer: null,
  202. touchRelative: [{x: 0,y: 0}],
  203. hypotenuseLength: 0,
  204. ctx: null
  205. };
  206. },
  207. computed: {
  208. clipStyle() {
  209. const {clipWidth, clipHeight, clipY, clipX, animation} = this
  210. return `
  211. width: ${clipWidth}px;
  212. height:${clipHeight}px;
  213. transition-property: ${animation ? '' : 'background'};
  214. left: ${clipX}px;
  215. top: ${clipY}px
  216. `
  217. },
  218. imageStyle() {
  219. const {imageWidth, imageHeight, imageLeft, imageTop, animation, scale, angle} = this
  220. return `
  221. width: ${imageWidth ? imageWidth + 'px' : 'auto'};
  222. height: ${imageHeight ? imageHeight + 'px' : 'auto'};
  223. transform: translate3d(${imageLeft - imageWidth / 2}px, ${imageTop - imageHeight / 2}px, 0) scale(${scale}) rotate(${angle}deg);
  224. transition-duration: ${animation ? 0.35 : 0}s
  225. `
  226. },
  227. clipSize() {
  228. const { clipWidth, clipHeight } = this;
  229. return { clipWidth, clipHeight };
  230. },
  231. clipPoint() {
  232. const { clipY, clipX } = this;
  233. return { clipY, clipX };
  234. }
  235. },
  236. watch: {
  237. value(val) {
  238. if(!val) {
  239. this.animation = 0
  240. this.angle = 0
  241. } else {
  242. if(this.imageUrl) {
  243. const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight, path} = cache?.[this.imageUrl] || {}
  244. if(path != this.image) {
  245. this.image = this.imageUrl;
  246. } else {
  247. this.setDiffData({imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight})
  248. }
  249. }
  250. }
  251. },
  252. imageUrl(url) {
  253. this.image = url
  254. },
  255. image:{
  256. handler: async function(url) {
  257. this.getImageInfo(url)
  258. },
  259. // immediate: true,
  260. },
  261. clipSize({ widthVal, heightVal }) {
  262. let { minWidth, minHeight } = this;
  263. minWidth = minWidth / 2;
  264. minHeight = minHeight / 2;
  265. if (widthVal < minWidth) {
  266. this.setDiffData({clipWidth: minWidth})
  267. }
  268. if (heightVal < minHeight) {
  269. this.setDiffData({clipHeight: minHeight})
  270. }
  271. this.calcClipSize();
  272. },
  273. angle(val) {
  274. this.animation = true;
  275. this.moveStop();
  276. const { isLimitMove } = this;
  277. if (isLimitMove && val % 90) {
  278. this.setDiffData({
  279. angle: Math.round(val / 90) * 90
  280. })
  281. }
  282. this.imgMarginDetectionScale();
  283. },
  284. animation(val) {
  285. clearTimeout(this.animationTimer);
  286. if (val) {
  287. let animationTimer = setTimeout(() => {
  288. this.setDiffData({
  289. animation: false
  290. })
  291. }, 260);
  292. this.setDiffData({animationTimer})
  293. this.animationTimer = animationTimer;
  294. }
  295. },
  296. isLimitMove(val) {
  297. if (val) {
  298. if (this.angle % 90) {
  299. this.setDiffData({
  300. angle : Math.round(this.angle / 90) * 90
  301. })
  302. }
  303. this.imgMarginDetectionScale();
  304. }
  305. },
  306. clipPoint() {
  307. this.cutDetectionPosition();
  308. },
  309. width(width, oWidth) {
  310. if (width !== oWidth) {
  311. this.setDiffData({
  312. clipWidth: width / 2
  313. })
  314. }
  315. },
  316. height(height, oHeight) {
  317. if (height !== oHeight) {
  318. this.setDiffData({
  319. clipHeight: height / 2
  320. })
  321. }
  322. }
  323. },
  324. mounted() {
  325. const sysinfo = uni.getSystemInfoSync();
  326. this.sysinfo = sysinfo;
  327. this.setClipInfo();
  328. if(this.image) {
  329. this.getImageInfo(this.image)
  330. }
  331. this.setClipCenter();
  332. this.calcClipSize();
  333. this.cutDetectionPosition();
  334. },
  335. methods: {
  336. setDiffData(data) {
  337. Object.keys(data).forEach(key => {
  338. if (this[key] !== data[key]) {
  339. this[key] = data[key];
  340. }
  341. });
  342. },
  343. getImageInfo(url) {
  344. if (!url) return;
  345. if(this.value) {
  346. uni.showLoading({
  347. title: '请稍候...',
  348. mask: true
  349. });
  350. }
  351. uni.getImageInfo({
  352. src: url,
  353. success: res => {
  354. this.imgComputeSize(res.width, res.height);
  355. this.image = res.path;
  356. if (this.isLimitMove) {
  357. this.imgMarginDetectionScale();
  358. this.$emit('ready', res);
  359. }
  360. const {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight} = this
  361. cache[url] = Object.assign(res, {imageWidth, imageHeight, imageLeft, imageTop, scale, clipX, clipY, clipWidth, clipHeight});
  362. },
  363. fail: (err) => {
  364. this.imgComputeSize();
  365. if (this.isLimitMove) {
  366. this.imgMarginDetectionScale();
  367. }
  368. }
  369. });
  370. },
  371. setClipInfo() {
  372. const { width, height, sysinfo, canvasId } = this;
  373. const clipWidth = width / 2;
  374. const clipHeight = height / 2;
  375. const clipY = (sysinfo.windowHeight - clipHeight) / 2;
  376. const clipX = (sysinfo.windowWidth - clipWidth) / 2;
  377. const imageLeft = sysinfo.windowWidth / 2;
  378. const imageTop = sysinfo.windowHeight / 2;
  379. this.ctx = uni.createCanvasContext(canvasId, this);
  380. this.clipWidth = clipWidth;
  381. this.clipHeight = clipHeight;
  382. this.clipX = clipX;
  383. this.clipY = clipY;
  384. this.canvasHeight = clipHeight;
  385. this.canvasWidth = clipWidth;
  386. this.imageLeft = imageLeft;
  387. this.imageTop = imageTop;
  388. },
  389. setClipCenter() {
  390. const { sysInfo, clipHeight, clipWidth, imageTop, imageLeft } = this;
  391. let sys = sysInfo || uni.getSystemInfoSync();
  392. let clipY = (sys.windowHeight - clipHeight) * 0.5;
  393. let clipX = (sys.windowWidth - clipWidth) * 0.5;
  394. this.imageTop = imageTop - this.clipY + clipY;
  395. this.imageLeft = imageLeft - this.clipX + clipX;
  396. this.clipY = clipY;
  397. this.clipX = clipX;
  398. },
  399. calcClipSize() {
  400. const { clipHeight, clipWidth, sysinfo, clipX, clipY } = this;
  401. if (clipWidth > sysinfo.windowWidth) {
  402. this.setDiffData({
  403. clipWidth: sysinfo.windowWidth
  404. })
  405. } else if (clipWidth + clipX > sysinfo.windowWidth) {
  406. this.setDiffData({
  407. clipX: sysinfo.windowWidth - clipX
  408. })
  409. }
  410. if (clipHeight > sysinfo.windowHeight) {
  411. this.setDiffData({
  412. clipHeight: sysinfo.windowHeight
  413. })
  414. } else if (clipHeight + clipY > sysinfo.windowHeight) {
  415. this.clipY = sysinfo.windowHeight - clipY;
  416. this.setDiffData({
  417. clipY: sysinfo.windowHeight - clipY
  418. })
  419. }
  420. },
  421. cutDetectionPosition() {
  422. const { clipX, clipY, sysinfo, clipHeight, clipWidth } = this;
  423. let cutDetectionPositionTop = () => {
  424. if (clipY < 0) {
  425. this.setDiffData({clipY: 0})
  426. }
  427. if (clipY > sysinfo.windowHeight - clipHeight) {
  428. this.setDiffData({clipY: sysinfo.windowHeight - clipHeight})
  429. }
  430. },
  431. cutDetectionPositionLeft = () => {
  432. if (clipX < 0) {
  433. this.setDiffData({clipX: 0})
  434. }
  435. if (clipX > sysinfo.windowWidth - clipWidth) {
  436. this.setDiffData({clipX: sysinfo.windowWidth - clipWidth})
  437. }
  438. };
  439. if (clipY === null && clipX === null) {
  440. let newClipY = (sysinfo.windowHeight - clipHeight) * 0.5;
  441. let newClipX = (sysinfo.windowWidth - clipWidth) * 0.5;
  442. this.setDiffData({
  443. clipX: newClipX,
  444. clipY: newClipY
  445. })
  446. } else if (clipY !== null && clipX !== null) {
  447. cutDetectionPositionTop();
  448. cutDetectionPositionLeft();
  449. } else if (clipY !== null && clipX === null) {
  450. cutDetectionPositionTop();
  451. this.setDiffData({
  452. clipX: (sysinfo.windowWidth - clipWidth) / 2
  453. })
  454. } else if (clipY === null && clipX !== null) {
  455. cutDetectionPositionLeft();
  456. this.setDiffData({
  457. clipY: (sysinfo.windowHeight - clipHeight) / 2
  458. })
  459. }
  460. },
  461. imgComputeSize(width, height) {
  462. const { imageWidth, imageHeight } = calcImageSize(width, height, this);
  463. this.imageWidth = imageWidth;
  464. this.imageHeight = imageHeight;
  465. },
  466. imgMarginDetectionScale(scale) {
  467. if (!this.isLimitMove) return;
  468. const currentScale = calcImageScale(this, scale);
  469. this.imgMarginDetectionPosition(currentScale);
  470. },
  471. imgMarginDetectionPosition(scale) {
  472. if (!this.isLimitMove) return;
  473. const { scale: currentScale, left, top } = calcImageOffset(this, scale);
  474. this.setDiffData({
  475. imageLeft: left,
  476. imageTop: top,
  477. scale: currentScale
  478. })
  479. },
  480. throttle() {
  481. this.setDiffData({
  482. throttleFlag: true
  483. })
  484. },
  485. moveDuring() {
  486. clearTimeout(this.timeClipCenter);
  487. },
  488. moveStop() {
  489. clearTimeout(this.timeClipCenter);
  490. const timeClipCenter = setTimeout(() => {
  491. if (!this.animation) {
  492. this.setDiffData({animation: true})
  493. }
  494. this.setClipCenter();
  495. }, 800);
  496. this.setDiffData({timeClipCenter})
  497. },
  498. clipTouchStart(event) {
  499. // #ifdef H5
  500. event.preventDefault()
  501. // #endif
  502. if (!this.image) {
  503. uni.showToast({
  504. title: '请选择图片',
  505. icon: 'none'
  506. });
  507. return;
  508. }
  509. const currentX = event.touches[0].clientX;
  510. const currentY = event.touches[0].clientY;
  511. const { clipX, clipY, clipWidth, clipHeight } = this;
  512. const corner = determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY);
  513. this.moveDuring();
  514. if(!corner) {return}
  515. this.clipStart = {
  516. width: clipWidth,
  517. height: clipHeight,
  518. x: currentX,
  519. y: currentY,
  520. clipY,
  521. clipX,
  522. corner
  523. };
  524. this.flagClipTouch = true;
  525. this.flagEndTouch = true;
  526. },
  527. clipTouchMove(event) {
  528. // #ifdef H5
  529. event.stopPropagation()
  530. event.preventDefault()
  531. // #endif
  532. if (!this.image) {
  533. uni.showToast({
  534. title: '请选择图片',
  535. icon: 'none'
  536. });
  537. return;
  538. }
  539. // 只针对单指点击做处理
  540. if (event.touches.length !== 1) {
  541. return;
  542. }
  543. const { flagClipTouch, throttleFlag } = this;
  544. if (flagClipTouch && throttleFlag) {
  545. const { isLockRatio, isLockHeight, isLockWidth } = this;
  546. if (isLockRatio && (isLockWidth || isLockHeight)) return;
  547. this.setDiffData({
  548. throttleFlag: false
  549. })
  550. this.throttle();
  551. const clipData = clipTouchMoveOfCalculate(this, event);
  552. if(clipData) {
  553. const { width, height, clipX, clipY } = clipData;
  554. if (!isLockWidth && !isLockHeight) {
  555. this.setDiffData({
  556. clipWidth: width,
  557. clipHeight: height,
  558. clipX,
  559. clipY
  560. })
  561. } else if (!isLockWidth) {
  562. this.setDiffData({
  563. clipWidth: width,
  564. clipX
  565. })
  566. } else if (!isLockHeight) {
  567. this.setDiffData({
  568. clipHeight: height,
  569. clipY
  570. })
  571. }
  572. this.imgMarginDetectionScale();
  573. }
  574. }
  575. },
  576. clipTouchEnd() {
  577. this.moveStop();
  578. this.flagClipTouch = false;
  579. },
  580. imageTouchStart(e) {
  581. // #ifdef H5
  582. event.preventDefault()
  583. // #endif
  584. this.flagEndTouch = false;
  585. const { imageLeft, imageTop } = this;
  586. const clientXForLeft = e.touches[0].clientX;
  587. const clientYForLeft = e.touches[0].clientY;
  588. let touchRelative = [];
  589. if (e.touches.length === 1) {
  590. touchRelative[0] = {
  591. x: clientXForLeft - imageLeft,
  592. y: clientYForLeft - imageTop
  593. };
  594. this.touchRelative = touchRelative;
  595. } else {
  596. const clientXForRight = e.touches[1].clientX;
  597. const clientYForRight = e.touches[1].clientY;
  598. let width = Math.abs(clientXForLeft - clientXForRight);
  599. let height = Math.abs(clientYForLeft - clientYForRight);
  600. const hypotenuseLength = calcPythagoreanTheorem(width, height);
  601. touchRelative = [
  602. {
  603. x: clientXForLeft - imageLeft,
  604. y: clientYForLeft - imageTop
  605. },
  606. {
  607. x: clientXForRight - imageLeft,
  608. y: clientYForRight - imageTop
  609. }
  610. ];
  611. this.touchRelative = touchRelative;
  612. this.hypotenuseLength = hypotenuseLength;
  613. }
  614. },
  615. imageTouchMove(e) {
  616. // #ifdef H5
  617. event.preventDefault()
  618. // #endif
  619. const { flagEndTouch, throttleFlag } = this;
  620. if (flagEndTouch || !throttleFlag) return;
  621. const clientXForLeft = e.touches[0].clientX;
  622. const clientYForLeft = e.touches[0].clientY;
  623. this.setDiffData({throttleFlag: false})
  624. this.throttle();
  625. this.moveDuring();
  626. if (e.touches.length === 1) {
  627. const { left: imageLeft, top: imageTop} = imageTouchMoveOfCalcOffset(this, clientXForLeft, clientYForLeft);
  628. this.setDiffData({
  629. imageLeft,
  630. imageTop
  631. })
  632. this.imgMarginDetectionPosition();
  633. } else {
  634. const clientXForRight = e.touches[1].clientX;
  635. const clientYForRight = e.touches[1].clientY;
  636. let width = Math.abs(clientXForLeft - clientXForRight),
  637. height = Math.abs(clientYForLeft - clientYForRight),
  638. hypotenuse = calcPythagoreanTheorem(width, height),
  639. scale = this.scale * (hypotenuse / this.hypotenuseLength);
  640. if (this.isDisableScale) {
  641. scale = 1;
  642. } else {
  643. scale = scale <= this.minRatio ? this.minRatio : scale;
  644. scale = scale >= this.maxRatio ? this.maxRatio : scale;
  645. this.$emit('change', {
  646. width: this.imageWidth * scale,
  647. height: this.imageHeight * scale
  648. });
  649. }
  650. this.imgMarginDetectionScale(scale);
  651. this.hypotenuseLength = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
  652. this.scale = scale;
  653. }
  654. },
  655. imageTouchEnd() {
  656. this.setDiffData({
  657. flagEndTouch: true
  658. })
  659. this.moveStop();
  660. },
  661. uploadImage() {
  662. const itemList = Object.entries(this.source)
  663. const sizeType = ['original', 'compressed']
  664. const success = ({tempFilePaths:a, tempFiles: b}) => {
  665. this.image = a ? a[0] : b[0].path
  666. };
  667. const _uploadImage = (type) => {
  668. if(type !== 'message') {
  669. uni.chooseImage({
  670. count: 1,
  671. sizeType,
  672. sourceType: [type],
  673. success
  674. });
  675. }
  676. // #ifdef MP-WEIXIN
  677. if(type == 'message') {
  678. wx.chooseMessageFile({
  679. count: 1,
  680. type: 'image',
  681. success
  682. })
  683. }
  684. // #endif
  685. }
  686. if(itemList.length > 1) {
  687. uni.showActionSheet({
  688. itemList: itemList.map(v => v[1]),
  689. success: ({tapIndex: i}) => {
  690. _uploadImage(itemList[i][0])
  691. }
  692. })
  693. } else {
  694. _uploadImage(itemList[0][0])
  695. }
  696. },
  697. imageReset() {
  698. const sys = this.sysinfo || uni.getSystemInfoSync();
  699. this.scale = 1;
  700. this.angle = 0;
  701. this.imageTop = sys.windowHeight / 2;
  702. this.imageLeft = sys.windowWidth / 2;
  703. },
  704. imageLoad(e) {
  705. this.imageReset();
  706. uni.hideLoading();
  707. this.$emit('ready', e.detail);
  708. },
  709. rotate(event) {
  710. if (this.isDisableRotate) return;
  711. if (!this.image) {
  712. uni.showToast({
  713. title: '请选择图片',
  714. icon: 'none'
  715. });
  716. return;
  717. }
  718. const { rotateAngle } = this;
  719. const originAngle = this.angle
  720. const type = event.currentTarget.dataset.type;
  721. if (type === 'along') {
  722. this.angle = originAngle + rotateAngle
  723. } else {
  724. this.angle = originAngle - rotateAngle
  725. }
  726. this.$emit('rotate', this.angle);
  727. },
  728. confirm() {
  729. if (!this.image) {
  730. uni.showToast({
  731. title: '请选择图片',
  732. icon: 'none'
  733. });
  734. return;
  735. }
  736. uni.showLoading({
  737. title: '加载中'
  738. });
  739. const { canvasHeight, canvasWidth, clipHeight, clipWidth, ctx, scale, imageLeft, imageTop, clipX, clipY, angle, scaleRatio: dpr, image, quality, fileType, type: imageType, canvasId } = this;
  740. const draw = () => {
  741. const imageWidth = this.imageWidth * scale * dpr;
  742. const imageHeight = this.imageHeight * scale * dpr;
  743. const xpos = imageLeft - clipX;
  744. const ypos = imageTop - clipY;
  745. ctx.translate(xpos * dpr, ypos * dpr);
  746. ctx.rotate((angle * Math.PI) / 180);
  747. ctx.drawImage(image, -imageWidth / 2, -imageHeight / 2, imageWidth, imageHeight);
  748. ctx.draw(false, () => {
  749. const width = clipWidth * dpr
  750. const height = clipHeight * dpr
  751. let params = {
  752. x: 0,
  753. y: 0,
  754. width,
  755. height,
  756. destWidth: width,
  757. destHeight: height,
  758. canvasId: canvasId,
  759. fileType,
  760. quality,
  761. success: (res) => {
  762. data.url = res.tempFilePath;
  763. uni.hideLoading();
  764. this.$emit('success', data);
  765. this.$emit('input', false)
  766. },
  767. fail: (error) => {
  768. console.error('error', error)
  769. this.$emit('fail', error);
  770. this.$emit('input', false)
  771. }
  772. };
  773. let data = {
  774. url: '',
  775. width,
  776. height
  777. };
  778. uni.canvasToTempFilePath(params, this)
  779. });
  780. };
  781. if (canvasWidth !== clipWidth || canvasHeight !== clipHeight) {
  782. this.canvasWidth = clipWidth;
  783. this.canvasHeight = clipHeight;
  784. ctx.draw();
  785. this.$nextTick(() => {
  786. setTimeout(() => {
  787. draw();
  788. }, 100);
  789. })
  790. } else {
  791. draw();
  792. }
  793. },
  794. cancel() {
  795. this.$emit('cancel', false)
  796. this.$emit('input', false)
  797. },
  798. }
  799. };
  800. </script>
  801. <style scoped>
  802. @import './index'
  803. </style>