l-clipper.vue 20 KB

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