graceImageCrop.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. <template>
  2. <view class="vue-cropper" ref="cropper" :style="{top:`${containerTop}px`}" v-show="show">
  3. <view class="cropper-box">
  4. <view class="cropper-box-canvas" @touchstart.stop.prevent="imgTouchStart" @touchmove.stop.prevent="imgMoveing" @touchend.stop.prevent="imgMoveEnd" :style="{
  5. 'width': imageWidth + 'px',
  6. 'height': imageHeight + 'px',
  7. 'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ x / scale + 'px,' + y / scale + 'px,' + '0)'
  8. + 'rotateZ('+ rotate * 90 +'deg)'}">
  9. <image :src="src" alt="cropper-img" ref="cropperImg" mode="scaleToFill" class="uni-image"></image>
  10. </view>
  11. </view>
  12. <view class="cropper-drag-box cropper-modal cropper-move pointer-events"></view>
  13. <view class="cropper-crop-box" :class="{'pointer-events': cropFixed}" :style="{'width': cropW + 'px','height': cropH + 'px','transform': 'translate3d('+ cropOffsertX + 'px,' + cropOffsertY + 'px,' + '0)'}">
  14. <view class="cropper-view-box">
  15. <image :style="{'width': imageWidth + 'px','height': imageHeight + 'px','transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ (x - cropOffsertX) / scale + 'px,' + (y - cropOffsertY) / scale + 'px,' + '0)' + 'rotateZ('+ rotate * 90 +'deg)'}" mode="scaleToFill" :src="src" alt="cropper-img"></image>
  16. </view>
  17. <view v-if="!cropFixed" class="cropper-face cropper-move" @touchstart.stop.prevent="touchStart" @touchmove.stop.prevent="cropMoveing"></view>
  18. <view class="crop-line line-w"></view>
  19. <view class="crop-line line-a"></view>
  20. <view class="crop-line line-s"></view>
  21. <view class="crop-line line-d"></view>
  22. </view>
  23. <canvas id="myCanvas" canvas-id="myCanvas" class="cropper-canvas" :style="{ 'width': cropW + 'px','height': cropH + 'px' }"></canvas>
  24. <view class="btn-group">
  25. <view class="btn-item reset-btn" v-show="showResetBtn" @tap="init"></view>
  26. <view class="btn-item rotate-btn" v-show="showRotateBtn" @tap="rotateHandler"></view>
  27. </view>
  28. <view class="uni-info__ft" :style="{paddingBottom:iPhoneXBottomHeightRpx+'rpx', background:'#FFFFFF'}">
  29. <view class="uni-modal__btn uni-modal__btn_default" style="color: rgb(0, 0, 0);" @tap="cancel">取消</view>
  30. <view class="uni-modal__btn uni-modal__btn_primary" style="color: rgb(0, 122, 255);" @tap="confirm">确定</view>
  31. </view>
  32. </view>
  33. </template>
  34. <script>
  35. export default {
  36. name: 'image-cropper',
  37. props: {
  38. cropWidth: {
  39. type: Number,
  40. default: 200,
  41. },
  42. cropHeight: {
  43. type: Number,
  44. default: 200
  45. },
  46. cropFixed: {
  47. type: Boolean,
  48. default: false,
  49. },
  50. src: {
  51. type: String,
  52. },
  53. showResetBtn: {
  54. type: Boolean,
  55. default: true,
  56. },
  57. showRotateBtn: {
  58. type: Boolean,
  59. default: true,
  60. }
  61. },
  62. data() {
  63. const sysInfo = uni.getSystemInfoSync();
  64. const pixelRatio = sysInfo.pixelRatio;
  65. return {
  66. show: false,
  67. scale: 1,
  68. rotate: 0,
  69. cropW: 0,
  70. cropH: 0,
  71. cropOldW: 0,
  72. cropOldH: 0,
  73. sysInfo: sysInfo,
  74. pixelRatio: pixelRatio,
  75. imageRealWidth: 0,
  76. imageRealHeight: 0,
  77. cropOffsertX: 0,
  78. cropOffsertY: 0,
  79. startX: 0,
  80. startY: 0,
  81. // 裁剪框与边界间距
  82. border: 5,
  83. x: 0,
  84. y: 0,
  85. startL: 0,
  86. oldScale: 1,
  87. iPhoneXBottomHeightRpx:0
  88. }
  89. },
  90. created:function(){
  91. const sysInfo = uni.getSystemInfoSync();
  92. const pixelRatio = sysInfo.pixelRatio;
  93. var iPhoneXBottom = 0;
  94. sysInfo.model = sysInfo.model.replace(' ', '');
  95. sysInfo.model = sysInfo.model.toLowerCase();
  96. if(sysInfo.model.indexOf('iphonex') != -1 || sysInfo.model.indexOf('iphone11') != -1){
  97. this.iPhoneXBottomHeightRpx = 50;
  98. }else{
  99. this.iPhoneXBottomHeightRpx = 0;
  100. }
  101. },
  102. watch: {
  103. src(val) {if(val.length > 0) { this.init();}},
  104. show(val) {if(!val){}}
  105. },
  106. computed: {
  107. containerTop() {
  108. let top = 0
  109. // #ifdef H5
  110. top = 44
  111. // #endif
  112. return top;
  113. },
  114. // 容器高度
  115. containerHeight() {
  116. return this.windowHeight - 48;
  117. },
  118. // 屏幕宽度
  119. windowWidth() {
  120. return this.sysInfo.windowWidth;
  121. },
  122. windowHeight() {
  123. return this.sysInfo.windowHeight;
  124. },
  125. // 图片宽高比
  126. imageRatio() {
  127. if (this.imageRealHeight > 0) {
  128. return this.imageRealWidth / this.imageRealHeight;;
  129. }
  130. return 0;
  131. },
  132. // 等比缩放后的宽度
  133. imageWidth() {
  134. if (this.imageRatio >= 1) {
  135. return this.windowWidth;
  136. }
  137. var imageWidth = this.windowWidth * this.imageRatio;
  138. if(imageWidth < this.cropWidth){return this.cropWidth;}
  139. return this.windowWidth * this.imageRatio;
  140. },
  141. // 等比缩放后的高度
  142. imageHeight() {
  143. if (this.imageRatio >= 1) {
  144. return this.windowWidth / this.imageRatio
  145. }
  146. return this.windowWidth;
  147. },
  148. },
  149. methods: {
  150. rotateHandler() {
  151. if(this.rotate == 3) {
  152. this.rotate = 0;
  153. }else {
  154. ++this.rotate
  155. }
  156. },
  157. init() {
  158. this.rotate = 0;
  159. this.scale = 1;
  160. this.cropW = this.cropWidth
  161. this.cropH = this.cropHeight
  162. uni.showLoading({title: '图片加载中...',})
  163. this.loadImage(this.src).then((e) => {
  164. uni.hideLoading()
  165. }).catch((e) => {
  166. uni.hideLoading()
  167. uni.showModal({
  168. title: '标题',
  169. content: '图片加载失败'
  170. })
  171. })
  172. },
  173. loadImage(src) {
  174. const _this = this
  175. return new Promise((resolve, reject) => {
  176. uni.getImageInfo({
  177. src: src,
  178. success: (res) => {
  179. _this.imageRealWidth = res.width
  180. _this.imageRealHeight = res.height
  181. _this.cropOffsertX = _this.windowWidth / 2 - _this.cropW / 2
  182. _this.cropOffsertY = _this.windowHeight / 2 - _this.cropH / 2
  183. _this.show = true
  184. _this.$nextTick(() => {
  185. _this.x = _this.windowWidth / 2 - _this.imageWidth / 2
  186. _this.y = _this.containerHeight / 2 - _this.imageHeight / 2
  187. });
  188. resolve(res)
  189. },
  190. fail: (e) => {
  191. _this.show = false
  192. reject(e)
  193. }
  194. })
  195. }).catch((e) => {});
  196. },
  197. cancel() {
  198. this.show = false
  199. this.$emit('cancel')
  200. },
  201. confirm(event) {
  202. uni.showLoading({
  203. title: '裁剪中...',
  204. })
  205. const _this = this
  206. const ctx = uni.createCanvasContext('myCanvas', _this);
  207. const pixelRatio = _this.pixelRatio;
  208. const imgage = _this.src;
  209. const imgW = _this.imageWidth * _this.scale;
  210. const imgH = _this.imageHeight * _this.scale
  211. const rotate = _this.rotate;
  212. let dx = _this.cropOffsertX - _this.x - (_this.imageWidth - imgW) / 2;
  213. let dy = _this.cropOffsertY - _this.y - (_this.imageHeight - imgH) / 2;
  214. ctx.setFillStyle('white');
  215. ctx.fillRect(0, 0, imgW, imgH);
  216. ctx.save();
  217. ctx.rotate((rotate * 90 * Math.PI) / 180);
  218. switch (rotate) {
  219. case 1:
  220. dx += (imgH-imgW) / 2
  221. dy -= (imgH-imgW) / 2
  222. ctx.drawImage(imgage, -dy, dx, imgW, -imgH);
  223. break;
  224. case 2:
  225. ctx.drawImage(imgage, dx, dy, -imgW, -imgH);
  226. break;
  227. case 3:
  228. dx += (imgH-imgW) / 2
  229. dy -= (imgH-imgW) / 2
  230. ctx.drawImage(imgage, dy, -dx, -imgW, imgH);
  231. break;
  232. default:
  233. ctx.drawImage(imgage, -dx, -dy, imgW, imgH);
  234. //ctx.drawImage(imgage, 2, 2, 375,375);
  235. break;
  236. }
  237. ctx.restore()
  238. // #ifdef MP-ALIPAY
  239. ctx.draw(true, () => {
  240. ctx.toTempFilePath({
  241. destWidth: _this.cropW * pixelRatio,
  242. destHeight: _this.cropH * pixelRatio,
  243. success: (res) => {
  244. uni.hideLoading()
  245. event.detail.tempFilePath =res.apFilePath
  246. _this.show = false
  247. _this.$emit('confirm', event)
  248. },
  249. fail: (e) => {
  250. uni.hideLoading()
  251. uni.showModal({
  252. title: '提示',
  253. content: '裁剪失败'
  254. })
  255. }
  256. }, _this);
  257. });
  258. // #endif
  259. // #ifndef MP-ALIPAY
  260. ctx.draw(false, () => {
  261. uni.canvasToTempFilePath({
  262. canvasId: 'myCanvas',
  263. destWidth: _this.cropW * pixelRatio,
  264. destHeight: _this.cropH * pixelRatio,
  265. success: (res) => {
  266. uni.hideLoading()
  267. event.detail.tempFilePath = res.tempFilePath;
  268. _this.show = false
  269. _this.$emit('confirm', event)
  270. },
  271. fail: (e) => {
  272. uni.hideLoading()
  273. uni.showModal({
  274. title: '提示',
  275. content: '裁剪失败'
  276. })
  277. }
  278. }, _this);
  279. })
  280. // #endif
  281. },
  282. imgTouchStart(e) {
  283. if(e.touches.length == 2) {
  284. this.oldScale = this.scale
  285. this.scaling = true
  286. const x = e.touches[0].pageX - e.touches[1].pageX
  287. const y = e.touches[0].pageY - e.touches[1].pageY
  288. const hypotenuse = Math.sqrt(
  289. Math.pow(x, 2) +
  290. Math.pow(y, 2)
  291. )
  292. this.startL = Math.max(x, y, hypotenuse)
  293. uni.showModal({content: this.startL});
  294. } else {
  295. this.startX = e.touches[0].pageX - this.x
  296. this.startY = e.touches[0].pageY - this.y
  297. }
  298. },
  299. imgMoveing(e) {
  300. if(this.scaling) {
  301. let scale = this.oldScale;
  302. const x = e.touches[0].pageX - e.touches[1].pageX
  303. const y = e.touches[0].pageY - e.touches[1].pageY
  304. const hypotenuse = Math.sqrt(
  305. Math.pow(x, 2) +
  306. Math.pow(y, 2)
  307. )
  308. const newL = Math.max(x, y, hypotenuse)
  309. const cha = newL - this.startL;
  310. // 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
  311. // 1px - 0.2
  312. let coe = 1;
  313. coe =
  314. coe / this.imageWidth > coe / this.imageHeight
  315. ? coe / this.imageHeight
  316. : coe / this.imageWidth;
  317. coe = coe > 0.1 ? 0.1 : coe;
  318. const num = coe * cha;
  319. if (cha > 0) {
  320. scale += Math.abs(num);
  321. } else if (cha < 0) {
  322. scale > Math.abs(num) ? (scale -= Math.abs(num)) : scale;
  323. }
  324. this.scale = scale;
  325. } else {
  326. const moveX = e.touches[0].pageX - this.startX
  327. const moveY = e.touches[0].pageY - this.startY
  328. this.x = moveX
  329. this.y = moveY
  330. }
  331. },
  332. imgMoveEnd() {
  333. setTimeout(() => {
  334. this.scaling = false
  335. }, 100)
  336. },
  337. touchStart(e) {
  338. this.startX = e.touches[0].pageX - this.cropOffsertX;
  339. this.startY = e.touches[0].pageY - this.cropOffsertY;
  340. this.cropOldW = this.cropW
  341. this.cropOldH = this.cropH
  342. },
  343. cropMoveing(e) {
  344. const moveX = this._cropX(e.touches[0].pageX - this.startX)
  345. const moveY = this._cropY(e.touches[0].pageY - this.startY)
  346. this.cropOffsertX = moveX
  347. this.cropOffsertY = moveY
  348. },
  349. dragMove(e, type) {
  350. if(this.cropFixed) {
  351. return false
  352. }
  353. const moveX = e.touches[0].pageX - this.startX
  354. const moveY = e.touches[0].pageY - this.startY
  355. switch (type) {
  356. case 'left-top':
  357. this._cropMoveLeft(moveX)
  358. this._cropMoveTop(moveY)
  359. break;
  360. case 'middle-top':
  361. this._cropMoveTop(moveY)
  362. break;
  363. case 'right-top':
  364. this._cropMoveTop(moveY)
  365. this._cropMoveRight(moveX)
  366. break;
  367. case 'middle-right':
  368. this._cropMoveRight(moveX)
  369. break;
  370. case 'right-bottom':
  371. this._cropMoveRight(moveX)
  372. this._cropMoveBottom(moveY)
  373. break;
  374. case 'middle-bottom':
  375. this._cropMoveBottom(moveY)
  376. break;
  377. case 'left-bottom':
  378. this._cropMoveBottom(moveY)
  379. this._cropMoveLeft(moveX)
  380. break;
  381. case 'middle-left':
  382. this._cropMoveLeft(moveX)
  383. break;
  384. default:
  385. break;
  386. }
  387. },
  388. _cropMoveTop(y) {
  389. const topY = this._cropY(y)
  390. this.cropH += this.cropOffsertY - topY
  391. this.cropOffsertY = topY
  392. },
  393. _cropMoveRight(x) {
  394. if(this.cropOldW + x >= this.windowWidth - this.border) {
  395. return false;
  396. }
  397. this.cropW = this.cropOldW + (x - this.cropOffsertX)
  398. },
  399. _cropMoveBottom(y) {
  400. if(this.cropOldH + y >= this.windowHeight - this.containerTop - this.border) {
  401. return false;
  402. }
  403. this.cropH = this.cropOldH + (y - this.cropOffsertY)
  404. },
  405. _cropMoveLeft(x) {
  406. const leftX = this._cropY(x)
  407. this.cropW += this.cropOffsertX - leftX
  408. this.cropOffsertX = leftX
  409. },
  410. _cropX(x) {
  411. if(x <= this.border) {
  412. return this.border
  413. }
  414. if(x + this.cropW >= this.windowWidth - this.border) {
  415. return this.windowWidth - this.cropW - this.border
  416. }
  417. return x
  418. },
  419. _cropY(y) {
  420. if(y <= this.border) {
  421. return this.border
  422. }
  423. if(y + this.cropH >= this.windowHeight - this.containerTop - this.border) {
  424. return this.windowHeight - this.cropH - this.containerTop - this.border
  425. }
  426. return y
  427. }
  428. }
  429. }
  430. </script>
  431. <style scoped lang="css">
  432. @font-face {
  433. font-family: "iconfont";
  434. src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAR4AAsAAAAACKgAAAQsAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqEfIRGATYCJAMMCwgABCAFhG0HShugB8gOJUHBwAAAAAFEBNmwzd4dtatSmmpFoVAEhUThEAYkCozFKDCqCVO6RfH/89v869awDnTR1qrSANFt4GG4SNxreBn91fmV9f3+53J613ieHba+N1zmGM8PA7oXTaCAxpjei8IoLWFsGLu4jPME6vWJJdovqmgAO4U2LRBnep0K7GJmpYQWanXVOWuLuAFrtenK4haAa/f38QnKsCOpyrRFh6eFWsh5KXnfYcn958BGQNKfE8wmMmaAQpzkuo9Z+ukZluoltVV5abUipL5i/ysArlhWVut/eCRBVNPUjYg6oUo7JTHFoaYDSvdacnKTq9GAB4AY5y2dtL3qpFh1DENdnJC6Hq+xYb7pyRMDMzc/fYoJjY8flwO3m98rMucF+IZHj6Cagw5UeKpxyFbt2rHGY/8jpa7CYMvLfcIesLjY3bdqhaf+nqgQs2qT/+rjCH/VfA0VFGuAC3iE8NEr/Vau8vZsXiUy7+V3c3tQQXMAuNjDCC89KDIHH0OFhnUi81GEPwyc7wZUaN7DnUf4g+ZLQsMKYV/94NjK7R7TEM4niTY1oJ5zEU62aNVaasUub08YLUEam5EnT6a61/I17dNk+vTu9jpJjXhsTFwjqTtpCBxBIIgS6iQnc/Zod1YGKp0rAwsD8kkyP6AwcK0hcAwkiQmBhWvxPZWKDu86aUH2nLEdi9rGX1eXq5P6A1SrnAucMVMdZH/GKi/jyfCqJyucfK3mXpVujXOPfFf5LC4Dvx0X/943JyOq4HuCTZ8KiIPPAb6ro8akpT6ufiq39BQrNlk5mp8pO0JlJLk8f5QalRjoP60IMx0N8n7wGhSD3n6/F1zlcTVz/cR+Ev0lkLSTd7UiPbD/wCxGRMA2Krwro2O0bTQtImbwhjAJc0S3N4ROx15/PH60IzaIOjCbEelqkDOfETNxb/FMixnWNzeJp2KPQw9A5d76jGUOQOUvH7RE/o2RfkNatd3OGf9q0QKbnq8WB7qy+hVqJRjJn1BQgP/iErks0yy5iGJTrOayW7C/z0IoZH0qNH+7N+31XXc7G2p1hZDU6IWs1ghaqDNQpcEKVKu1BfWmFW9u0IFhKUodpswCEFodgqTZHWStbqOF+hqqdPsG1VrDEuodhfueDcZCj+QzuIrFtZh6BNNraIowbCzi1dbhOlOfionKXHoTzgzoY5hCKk/minEKZ/pYMDCoU7IsgREM3Y8Vgcvwvj4aMzK0AdewUpJljWkyGZH3IKmG7gfEHgZOhYXTwqiNwOhp0CiE3ZiFpL5fB6dj0keFKcGV+JvgGAP0vWMUpOQ10GI1VQt3LoMHDNJRYrEIPInAoPXDFEEnrk9P0zDG/FEGOA2WFNkiaZRGhuoRddXS8bX917cL6mn9c6TIUXSekybKHKQfJXFq2KSiRklLYU8dNKWDIX0cAA==') format('woff2');
  435. }
  436. .vue-cropper {
  437. position: fixed;
  438. left: 0;
  439. right: 0;
  440. bottom: 0;
  441. z-index: 998;
  442. box-sizing: border-box;
  443. user-select: none;
  444. -webkit-user-select: none;
  445. -moz-user-select: none;
  446. -ms-user-select: none;
  447. direction: ltr;
  448. touch-action: none;
  449. text-align: left;
  450. background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
  451. }
  452. .cropper-canvas {
  453. position: absolute;
  454. top: -9999px;
  455. left:-9999px;
  456. z-index: -998;
  457. }
  458. .vue-cropper .uni-info__ft {
  459. position: absolute;
  460. line-height: 48px;
  461. font-size: 18px;
  462. display: -webkit-box;
  463. display: -webkit-flex;
  464. display: flex;
  465. bottom: 0;
  466. left: 0;
  467. right: 0;
  468. z-index: 998;
  469. }
  470. .btn-group {
  471. position: absolute;
  472. right: 30px;
  473. bottom: 98px;
  474. z-index: 998;
  475. }
  476. .btn-item {
  477. position: relative;
  478. width:40px;
  479. height:40px;
  480. background:#fff;
  481. border-radius: 20px;
  482. display: inline-block;
  483. margin-left: 10px;
  484. text-align:center; line-height:40px;
  485. }
  486. .btn-item:active {background: #ccc;}
  487. .rotate-btn {
  488. font-family: "iconfont" !important;
  489. font-size:30px;
  490. font-style: normal;
  491. -webkit-font-smoothing: antialiased;
  492. -moz-osx-font-smoothing: grayscale;
  493. }
  494. .rotate-btn:before {
  495. content: "\e65c";
  496. margin-left: -2px;
  497. }
  498. .reset-btn {
  499. font-family: "iconfont" !important;
  500. font-size:30px;
  501. font-style: normal;
  502. -webkit-font-smoothing: antialiased;
  503. -moz-osx-font-smoothing: grayscale;
  504. }
  505. .reset-btn:before {
  506. content: "\e648";
  507. margin-left: -2px;
  508. }
  509. .vue-cropper .uni-info__ft:after {
  510. content: " ";
  511. position: absolute;
  512. left: 0;
  513. top: 0;
  514. right: 0;
  515. height: 1px;
  516. border-top: 1px solid #d5d5d6;
  517. color: #d5d5d6;
  518. -webkit-transform-origin: 0 0;
  519. transform-origin: 0 0;
  520. -webkit-transform: scaleY(.5);
  521. transform: scaleY(.5);
  522. z-index: 998;
  523. }
  524. .vue-cropper .uni-modal__btn {
  525. display: block;
  526. -webkit-box-flex: 1;
  527. -webkit-flex: 1;
  528. flex: 1;
  529. color: #3cc51f;
  530. text-decoration: none;
  531. -webkit-tap-highlight-color: rgba(0,0,0,0);
  532. position: relative;
  533. text-align: center;
  534. background-color: #fff;
  535. z-index: 998;
  536. }
  537. .vue-cropper .uni-modal__btn:first-child:after { display: none }
  538. .vue-cropper .uni-modal__btn:after {
  539. content: " ";
  540. position: absolute;
  541. left: 0;
  542. top: 0;
  543. width: 1px;
  544. bottom: 0;
  545. border-left: 1px solid #d5d5d6;
  546. color: #d5d5d6;
  547. -webkit-transform-origin: 0 0;
  548. transform-origin: 0 0;
  549. -webkit-transform: scaleX(.5);
  550. transform: scaleX(.5);
  551. z-index: 998;
  552. }
  553. .vue-cropper .uni-modal__btn:active {
  554. background-color: #eee;
  555. }
  556. .cropper-box,
  557. .cropper-box-canvas,
  558. .cropper-drag-box,
  559. .cropper-crop-box,
  560. .cropper-face {
  561. position: absolute;
  562. top: 0;
  563. right: 0;
  564. bottom: 0;
  565. left: 0;
  566. user-select: none;
  567. z-index: 998;
  568. }
  569. .uni-image {
  570. width: 100%;
  571. height: 100%;
  572. }
  573. .cropper-box-canvas image {
  574. position: relative;
  575. text-align: left;
  576. user-select: none;
  577. transform: none;
  578. max-width: none;
  579. max-height: none;
  580. z-index: 998;
  581. }
  582. .cropper-box {
  583. overflow: hidden;
  584. }
  585. .cropper-move {
  586. cursor: move;
  587. }
  588. .cropper-crop {
  589. cursor: crosshair;
  590. }
  591. .cropper-modal {
  592. background: rgba(0, 0, 0, 0.5);
  593. }
  594. .pointer-events {
  595. pointer-events:none;
  596. }
  597. .cropper-crop-box {
  598. /*border: 2px solid #39f;*/
  599. }
  600. .cropper-view-box {
  601. display: block;
  602. overflow: hidden;
  603. width: 100%;
  604. height: 100%;
  605. outline: 1px solid #39f;
  606. outline-color: rgba(51, 153, 255, 0.75);
  607. user-select: none;
  608. }
  609. .cropper-view-box image {
  610. user-select: none;
  611. text-align: left;
  612. max-width: none;
  613. max-height: none;
  614. }
  615. .cropper-face {
  616. top: 0;
  617. left: 0;
  618. background-color: #fff;
  619. opacity: 0.1;
  620. }
  621. .crop-line {
  622. position: absolute;
  623. display: block;
  624. width: 100%;
  625. height: 100%;
  626. opacity: 0.1;
  627. z-index: 998;
  628. }
  629. .line-w {
  630. top: -3px;
  631. left: 0;
  632. height: 5px;
  633. cursor: n-resize;
  634. }
  635. .line-a {
  636. top: 0;
  637. left: -3px;
  638. width: 5px;
  639. cursor: w-resize;
  640. }
  641. .line-s {
  642. bottom: -3px;
  643. left: 0;
  644. height: 5px;
  645. cursor: s-resize;
  646. }
  647. .line-d {
  648. top: 0;
  649. right: -3px;
  650. width: 5px;
  651. cursor: e-resize;
  652. }
  653. .crop-point {
  654. position: absolute;
  655. width: 8px;
  656. height: 8px;
  657. opacity: 0.75;
  658. background-color: #39f;
  659. border-radius: 100%;
  660. z-index: 998;
  661. }
  662. .point-lt {
  663. top: -4px;
  664. left: -4px;
  665. cursor: nw-resize;
  666. }
  667. .point-mt {
  668. top: -5px;
  669. left: 50%;
  670. margin-left: -3px;
  671. cursor: n-resize;
  672. }
  673. .point-rt {
  674. top: -4px;
  675. right: -4px;
  676. cursor: ne-resize;
  677. }
  678. .point-ml {
  679. top: 50%;
  680. left: -4px;
  681. margin-top: -3px;
  682. cursor: w-resize;
  683. }
  684. .point-mr {
  685. top: 50%;
  686. right: -4px;
  687. margin-top: -3px;
  688. cursor: e-resize;
  689. }
  690. .point-lb {
  691. bottom: -5px;
  692. left: -4px;
  693. cursor: sw-resize;
  694. }
  695. .point-mb {
  696. bottom: -5px;
  697. left: 50%;
  698. margin-left: -3px;
  699. cursor: s-resize;
  700. }
  701. .point-rb {
  702. bottom: -5px;
  703. right: -4px;
  704. cursor: se-resize;
  705. }
  706. </style>