invinbg-image-cropper.vue 25 KB

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