16bce11d4165360c73997278bfe18af755e8743d6f4b498f43f4d5f13f5a4d5ceefc4988643fc94644b25c6e365dd399ee6a78bd0f5be38c20151b48e9b625 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087
  1. <template>
  2. <div class="vue-cropper" ref="cropper" @mouseover="scaleImg" @mouseout="cancelScale">
  3. <div class="cropper-box" v-if="imgs">
  4. <div
  5. class="cropper-box-canvas"
  6. v-show="!loading"
  7. :style="{
  8. 'width': trueWidth + 'px',
  9. 'height': trueHeight + 'px',
  10. 'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ x / scale + 'px,' + y / scale + 'px,' + '0)'
  11. + 'rotateZ('+ rotate * 90 +'deg)'
  12. }"
  13. >
  14. <img :src="imgs" alt="cropper-img" ref="cropperImg">
  15. </div>
  16. </div>
  17. <div
  18. class="cropper-drag-box"
  19. :class="{'cropper-move': move && !crop, 'cropper-crop': crop, 'cropper-modal': cropping}"
  20. @mousedown="startMove"
  21. @touchstart="startMove"
  22. ></div>
  23. <div
  24. v-show="cropping"
  25. class="cropper-crop-box"
  26. :style="{
  27. 'width': cropW + 'px',
  28. 'height': cropH + 'px',
  29. 'transform': 'translate3d('+ cropOffsertX + 'px,' + cropOffsertY + 'px,' + '0)'
  30. }"
  31. >
  32. <span class="cropper-view-box">
  33. <img
  34. :style="{
  35. 'width': trueWidth + 'px',
  36. 'height': trueHeight + 'px',
  37. 'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ (x - cropOffsertX) / scale + 'px,' + (y - cropOffsertY) / scale + 'px,' + '0)'
  38. + 'rotateZ('+ rotate * 90 +'deg)'
  39. }"
  40. :src="imgs"
  41. alt="cropper-img"
  42. >
  43. </span>
  44. <span class="cropper-face cropper-move" @mousedown="cropMove" @touchstart="cropMove"></span>
  45. <span
  46. class="crop-info"
  47. v-if="info"
  48. :style="{'top': cropInfo.top}"
  49. >{{ this.cropInfo.width }} × {{ this.cropInfo.height }}</span>
  50. <span v-if="!fixedBox">
  51. <span
  52. class="crop-line line-w"
  53. @mousedown="changeCropSize($event, false, true, 0, 1)"
  54. @touchstart="changeCropSize($event, false, true, 0, 1)"
  55. ></span>
  56. <span
  57. class="crop-line line-a"
  58. @mousedown="changeCropSize($event, true, false, 1, 0)"
  59. @touchstart="changeCropSize($event, true, false, 1, 0)"
  60. ></span>
  61. <span
  62. class="crop-line line-s"
  63. @mousedown="changeCropSize($event, false, true, 0, 2)"
  64. @touchstart="changeCropSize($event, false, true, 0, 2)"
  65. ></span>
  66. <span
  67. class="crop-line line-d"
  68. @mousedown="changeCropSize($event, true, false, 2, 0)"
  69. @touchstart="changeCropSize($event, true, false, 2, 0)"
  70. ></span>
  71. <span
  72. class="crop-point point1"
  73. @mousedown="changeCropSize($event, true, true, 1, 1)"
  74. @touchstart="changeCropSize($event, true, true, 1, 1)"
  75. ></span>
  76. <span
  77. class="crop-point point2"
  78. @mousedown="changeCropSize($event, false, true, 0, 1)"
  79. @touchstart="changeCropSize($event, false, true, 0, 1)"
  80. ></span>
  81. <span
  82. class="crop-point point3"
  83. @mousedown="changeCropSize($event, true, true, 2, 1)"
  84. @touchstart="changeCropSize($event, true, true, 2, 1)"
  85. ></span>
  86. <span
  87. class="crop-point point4"
  88. @mousedown="changeCropSize($event, true, false, 1, 0)"
  89. @touchstart="changeCropSize($event, true, false, 1, 0)"
  90. ></span>
  91. <span
  92. class="crop-point point5"
  93. @mousedown="changeCropSize($event, true, false, 2, 0)"
  94. @touchstart="changeCropSize($event, true, false, 2, 0)"
  95. ></span>
  96. <span
  97. class="crop-point point6"
  98. @mousedown="changeCropSize($event, true, true, 1, 2)"
  99. @touchstart="changeCropSize($event, true, true, 1, 2)"
  100. ></span>
  101. <span
  102. class="crop-point point7"
  103. @mousedown="changeCropSize($event, false, true, 0, 2)"
  104. @touchstart="changeCropSize($event, false, true, 0, 2)"
  105. ></span>
  106. <span
  107. class="crop-point point8"
  108. @mousedown="changeCropSize($event, true, true, 2, 2)"
  109. @touchstart="changeCropSize($event, true, true, 2, 2)"
  110. ></span>
  111. </span>
  112. </div>
  113. </div>
  114. </template>
  115. <script>
  116. import exifmin from "./exif-js-min";
  117. export default {
  118. data: function() {
  119. return {
  120. // 容器高宽
  121. w: 0,
  122. h: 0,
  123. // 图片缩放比例
  124. scale: 1,
  125. // 图片偏移x轴
  126. x: 0,
  127. // 图片偏移y轴
  128. y: 0,
  129. // 图片加载
  130. loading: true,
  131. // 图片真实宽度
  132. trueWidth: 0,
  133. // 图片真实高度
  134. trueHeight: 0,
  135. move: true,
  136. // 移动的x
  137. moveX: 0,
  138. // 移动的y
  139. moveY: 0,
  140. // 开启截图
  141. crop: false,
  142. // 正在截图
  143. cropping: false,
  144. // 裁剪框大小
  145. cropW: 0,
  146. cropH: 0,
  147. cropOldW: 0,
  148. cropOldH: 0,
  149. // 判断是否能够改变
  150. canChangeX: false,
  151. canChangeY: false,
  152. // 改变的基准点
  153. changeCropTypeX: 1,
  154. changeCropTypeY: 1,
  155. // 裁剪框的坐标轴
  156. cropX: 0,
  157. cropY: 0,
  158. cropChangeX: 0,
  159. cropChangeY: 0,
  160. cropOffsertX: 0,
  161. cropOffsertY: 0,
  162. // 支持的滚动事件
  163. support: "",
  164. // 移动端手指缩放
  165. touches: [],
  166. touchNow: false,
  167. // 图片旋转
  168. rotate: 0,
  169. isIos: false,
  170. orientation: 0,
  171. imgs: "",
  172. // 图片缩放系数
  173. coe: 0.2,
  174. // 是否正在多次缩放
  175. scaling: false,
  176. scalingSet: "",
  177. coeStatus: "",
  178. // 控制emit触发频率
  179. isCanShow: true
  180. };
  181. },
  182. props: {
  183. img: {
  184. type: [String, Blob, null, File],
  185. default: ""
  186. },
  187. // 输出图片压缩比
  188. outputSize: {
  189. type: Number,
  190. default: 1
  191. },
  192. outputType: {
  193. type: String,
  194. default: "jpeg"
  195. },
  196. info: {
  197. type: Boolean,
  198. default: true
  199. },
  200. // 是否开启滚轮放大缩小
  201. canScale: {
  202. type: Boolean,
  203. default: true
  204. },
  205. // 是否自成截图框
  206. autoCrop: {
  207. type: Boolean,
  208. default: false
  209. },
  210. autoCropWidth: {
  211. type: [Number, String],
  212. default: 0
  213. },
  214. autoCropHeight: {
  215. type: [Number, String],
  216. default: 0
  217. },
  218. // 是否开启固定宽高比
  219. fixed: {
  220. type: Boolean,
  221. default: false
  222. },
  223. // 宽高比 w/h
  224. fixedNumber: {
  225. type: Array,
  226. default: () => {
  227. return [1, 1];
  228. }
  229. },
  230. // 固定大小 禁止改变截图框大小
  231. fixedBox: {
  232. type: Boolean,
  233. default: false
  234. },
  235. // 输出截图是否缩放
  236. full: {
  237. type: Boolean,
  238. default: false
  239. },
  240. // 是否可以拖动图片
  241. canMove: {
  242. type: Boolean,
  243. default: true
  244. },
  245. // 是否可以拖动截图框
  246. canMoveBox: {
  247. type: Boolean,
  248. default: true
  249. },
  250. // 上传图片按照原始比例显示
  251. original: {
  252. type: Boolean,
  253. default: false
  254. },
  255. // 截图框能否超过图片
  256. centerBox: {
  257. type: Boolean,
  258. default: false
  259. },
  260. // 是否根据dpr输出高清图片
  261. high: {
  262. type: Boolean,
  263. default: true
  264. },
  265. // 截图框展示宽高类型
  266. infoTrue: {
  267. type: Boolean,
  268. default: false
  269. },
  270. // 可以压缩图片宽高 默认不超过200
  271. maxImgSize: {
  272. type: [Number, String],
  273. default: 2000
  274. },
  275. // 倍数 可渲染当前截图框的n倍 0 - 1000;
  276. enlarge: {
  277. type: [Number, String],
  278. default: 1
  279. },
  280. // 自动预览的固定宽度
  281. preW: {
  282. type: [Number, String],
  283. default: 0
  284. },
  285. /*
  286. 图片布局方式 mode 实现和css背景一样的效果
  287. contain 居中布局 默认不会缩放 保证图片在容器里面 mode: 'contain'
  288. cover 拉伸布局 填充整个容器 mode: 'cover'
  289. 如果仅有一个数值被给定,这个数值将作为宽度值大小,高度值将被设定为auto。 mode: '50px'
  290. 如果有两个数值被给定,第一个将作为宽度值大小,第二个作为高度值大小。 mode: '50px 60px'
  291. */
  292. mode: {
  293. type: String,
  294. default: "contain"
  295. },
  296. //限制最小区域,可传1以上的数字和字符串,限制长宽都是这么大
  297. // 也可以传数组[90,90]
  298. limitMinSize: {
  299. type: [Number, Array, String],
  300. default: () => {
  301. return 10;
  302. }
  303. },
  304. },
  305. computed: {
  306. cropInfo() {
  307. let obj = {};
  308. obj.top = this.cropOffsertY > 21 ? "-21px" : "0px";
  309. obj.width = this.cropW > 0 ? this.cropW : 0;
  310. obj.height = this.cropH > 0 ? this.cropH : 0;
  311. if (this.infoTrue) {
  312. let dpr = 1;
  313. if (this.high && !this.full) {
  314. dpr = window.devicePixelRatio;
  315. }
  316. if ((this.enlarge !== 1) & !this.full) {
  317. dpr = Math.abs(Number(this.enlarge));
  318. }
  319. obj.width = obj.width * dpr;
  320. obj.height = obj.height * dpr;
  321. if (this.full) {
  322. obj.width = obj.width / this.scale;
  323. obj.height = obj.height / this.scale;
  324. }
  325. }
  326. obj.width = obj.width.toFixed(0);
  327. obj.height = obj.height.toFixed(0);
  328. return obj;
  329. },
  330. isIE() {
  331. var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
  332. const isIE = !!window.ActiveXObject || 'ActiveXObject' in window; //判断是否IE浏览器
  333. return isIE;
  334. },
  335. passive () {
  336. return this.isIE ? null : {
  337. passive: false
  338. }
  339. }
  340. },
  341. watch: {
  342. // 如果图片改变, 重新布局
  343. img() {
  344. // 当传入图片时, 读取图片信息同时展示
  345. this.checkedImg();
  346. },
  347. imgs(val) {
  348. if (val === "") {
  349. return;
  350. }
  351. this.reload();
  352. },
  353. cropW() {
  354. this.showPreview();
  355. },
  356. cropH() {
  357. this.showPreview();
  358. },
  359. cropOffsertX() {
  360. this.showPreview();
  361. },
  362. cropOffsertY() {
  363. this.showPreview();
  364. },
  365. scale(val, oldVal) {
  366. this.showPreview();
  367. },
  368. x() {
  369. this.showPreview();
  370. },
  371. y() {
  372. this.showPreview();
  373. },
  374. autoCrop(val) {
  375. if (val) {
  376. this.goAutoCrop();
  377. }
  378. },
  379. // 修改了自动截图框
  380. autoCropWidth() {
  381. if (this.autoCrop) {
  382. this.goAutoCrop();
  383. }
  384. },
  385. autoCropHeight() {
  386. if (this.autoCrop) {
  387. this.goAutoCrop();
  388. }
  389. },
  390. mode() {
  391. this.checkedImg();
  392. },
  393. rotate() {
  394. this.showPreview();
  395. if (this.autoCrop) {
  396. this.goAutoCrop(this.cropW, this.cropH);
  397. } else {
  398. if (this.cropW > 0 || this.cropH > 0) {
  399. this.goAutoCrop(this.cropW, this.cropH);
  400. }
  401. }
  402. }
  403. },
  404. methods: {
  405. getVersion (name) {
  406. var arr = navigator.userAgent.split(' ');
  407. var chromeVersion = '';
  408. let result = 0;
  409. const reg = new RegExp(name, 'i')
  410. for(var i=0;i < arr.length;i++){
  411. if(reg.test(arr[i]))
  412. chromeVersion = arr[i]
  413. }
  414. if(chromeVersion){
  415. result = chromeVersion.split('/')[1].split('.');
  416. } else {
  417. result = ['0', '0', '0'];
  418. }
  419. return result
  420. },
  421. checkOrientationImage(img, orientation, width, height) {
  422. // 如果是 chrome内核版本在81 safari 在 605 以上不处理图片旋转
  423. // alert(navigator.userAgent)
  424. if (this.getVersion('chrome')[0] >= 81) {
  425. orientation = -1
  426. } else {
  427. if (this.getVersion('safari')[0] >= 605 ) {
  428. const safariVersion = this.getVersion('version')
  429. if (safariVersion[0] > 13 && safariVersion[1] > 1) {
  430. orientation = -1
  431. }
  432. } else {
  433. // 判断 ios 版本进行处理
  434. // 针对 ios 版本大于 13.4的系统不做图片旋转
  435. const isIos = navigator.userAgent.toLowerCase().match(/cpu iphone os (.*?) like mac os/)
  436. if (isIos) {
  437. let version = isIos[1]
  438. version = version.split('_')
  439. if (version[0] > 13 || (version[0] >= 13 && version[1] >= 4)) {
  440. orientation = -1
  441. }
  442. }
  443. }
  444. }
  445. // alert(`当前处理的orientation${orientation}`)
  446. let canvas = document.createElement("canvas");
  447. let ctx = canvas.getContext("2d");
  448. ctx.save();
  449. switch (orientation) {
  450. case 2:
  451. canvas.width = width;
  452. canvas.height = height;
  453. // horizontal flip
  454. ctx.translate(width, 0);
  455. ctx.scale(-1, 1);
  456. break;
  457. case 3:
  458. canvas.width = width;
  459. canvas.height = height;
  460. //180 graus
  461. ctx.translate(width / 2, height / 2);
  462. ctx.rotate((180 * Math.PI) / 180);
  463. ctx.translate(-width / 2, -height / 2);
  464. break;
  465. case 4:
  466. canvas.width = width;
  467. canvas.height = height;
  468. // vertical flip
  469. ctx.translate(0, height);
  470. ctx.scale(1, -1);
  471. break;
  472. case 5:
  473. // vertical flip + 90 rotate right
  474. canvas.height = width;
  475. canvas.width = height;
  476. ctx.rotate(0.5 * Math.PI);
  477. ctx.scale(1, -1);
  478. break;
  479. case 6:
  480. canvas.width = height;
  481. canvas.height = width;
  482. //90 graus
  483. ctx.translate(height / 2, width / 2);
  484. ctx.rotate((90 * Math.PI) / 180);
  485. ctx.translate(-width / 2, -height / 2);
  486. break;
  487. case 7:
  488. // horizontal flip + 90 rotate right
  489. canvas.height = width;
  490. canvas.width = height;
  491. ctx.rotate(0.5 * Math.PI);
  492. ctx.translate(width, -height);
  493. ctx.scale(-1, 1);
  494. break;
  495. case 8:
  496. canvas.height = width;
  497. canvas.width = height;
  498. //-90 graus
  499. ctx.translate(height / 2, width / 2);
  500. ctx.rotate((-90 * Math.PI) / 180);
  501. ctx.translate(-width / 2, -height / 2);
  502. break;
  503. default:
  504. canvas.width = width;
  505. canvas.height = height;
  506. }
  507. ctx.drawImage(img, 0, 0, width, height);
  508. ctx.restore();
  509. canvas.toBlob(
  510. blob => {
  511. let data = URL.createObjectURL(blob);
  512. URL.revokeObjectURL(this.imgs)
  513. this.imgs = data;
  514. },
  515. "image/" + this.outputType,
  516. 1
  517. );
  518. },
  519. // checkout img
  520. checkedImg() {
  521. if (this.img === null || this.img === '') {
  522. this.imgs = ''
  523. this.clearCrop()
  524. return
  525. }
  526. this.loading = true;
  527. this.scale = 1;
  528. this.rotate = 0;
  529. this.clearCrop();
  530. let img = new Image();
  531. img.onload = () => {
  532. if (this.img === "") {
  533. this.$emit("imgLoad", "error");
  534. this.$emit("img-load", "error");
  535. return false;
  536. }
  537. let width = img.width;
  538. let height = img.height;
  539. exifmin.getData(img).then(data => {
  540. this.orientation = data.orientation || 1;
  541. let max = this.maxImgSize;
  542. if (!this.orientation && (width < max) & (height < max)) {
  543. this.imgs = this.img;
  544. return;
  545. }
  546. if (width > max) {
  547. height = (height / width) * max;
  548. width = max;
  549. }
  550. if (height > max) {
  551. width = (width / height) * max;
  552. height = max;
  553. }
  554. this.checkOrientationImage(img, this.orientation, width, height);
  555. });
  556. };
  557. img.onerror = () => {
  558. this.$emit("imgLoad", "error");
  559. this.$emit("img-load", "error");
  560. };
  561. // 判断如果不是base64图片 再添加crossOrigin属性,否则会导致iOS低版本(10.2)无法显示图片
  562. if (this.img.substr(0, 4) !== "data") {
  563. img.crossOrigin = "";
  564. }
  565. if (this.isIE) {
  566. var xhr = new XMLHttpRequest();
  567. xhr.onload = function() {
  568. var url = URL.createObjectURL(this.response);
  569. img.src = url;
  570. };
  571. xhr.open("GET", this.img, true);
  572. xhr.responseType = "blob";
  573. xhr.send();
  574. } else {
  575. img.src = this.img;
  576. }
  577. },
  578. // 当按下鼠标键
  579. startMove(e) {
  580. e.preventDefault();
  581. // 如果move 为true 表示当前可以拖动
  582. if (this.move && !this.crop) {
  583. if (!this.canMove) {
  584. return false;
  585. }
  586. // 开始移动
  587. this.moveX = (e.clientX ? e.clientX : e.touches[0].clientX) - this.x;
  588. this.moveY = (e.clientY ? e.clientY : e.touches[0].clientY) - this.y;
  589. if (e.touches) {
  590. window.addEventListener("touchmove", this.moveImg);
  591. window.addEventListener("touchend", this.leaveImg);
  592. if (e.touches.length == 2) {
  593. // 记录手指刚刚放上去
  594. this.touches = e.touches;
  595. window.addEventListener("touchmove", this.touchScale);
  596. window.addEventListener("touchend", this.cancelTouchScale);
  597. }
  598. } else {
  599. window.addEventListener("mousemove", this.moveImg);
  600. window.addEventListener("mouseup", this.leaveImg);
  601. }
  602. // 触发图片移动事件
  603. this.$emit("imgMoving", {
  604. moving: true,
  605. axis: this.getImgAxis()
  606. });
  607. this.$emit("img-moving", {
  608. moving: true,
  609. axis: this.getImgAxis()
  610. });
  611. } else {
  612. // 截图ing
  613. this.cropping = true;
  614. // 绑定截图事件
  615. window.addEventListener("mousemove", this.createCrop);
  616. window.addEventListener("mouseup", this.endCrop);
  617. window.addEventListener("touchmove", this.createCrop);
  618. window.addEventListener("touchend", this.endCrop);
  619. this.cropOffsertX = e.offsetX
  620. ? e.offsetX
  621. : e.touches[0].pageX - this.$refs.cropper.offsetLeft;
  622. this.cropOffsertY = e.offsetY
  623. ? e.offsetY
  624. : e.touches[0].pageY - this.$refs.cropper.offsetTop;
  625. this.cropX = e.clientX ? e.clientX : e.touches[0].clientX;
  626. this.cropY = e.clientY ? e.clientY : e.touches[0].clientY;
  627. this.cropChangeX = this.cropOffsertX;
  628. this.cropChangeY = this.cropOffsertY;
  629. this.cropW = 0;
  630. this.cropH = 0;
  631. }
  632. },
  633. // 移动端缩放
  634. touchScale(e) {
  635. e.preventDefault();
  636. let scale = this.scale;
  637. // 记录变化量
  638. // 第一根手指
  639. var oldTouch1 = {
  640. x: this.touches[0].clientX,
  641. y: this.touches[0].clientY
  642. };
  643. var newTouch1 = {
  644. x: e.touches[0].clientX,
  645. y: e.touches[0].clientY
  646. };
  647. // 第二根手指
  648. var oldTouch2 = {
  649. x: this.touches[1].clientX,
  650. y: this.touches[1].clientY
  651. };
  652. var newTouch2 = {
  653. x: e.touches[1].clientX,
  654. y: e.touches[1].clientY
  655. };
  656. var oldL = Math.sqrt(
  657. Math.pow(oldTouch1.x - oldTouch2.x, 2) +
  658. Math.pow(oldTouch1.y - oldTouch2.y, 2)
  659. );
  660. var newL = Math.sqrt(
  661. Math.pow(newTouch1.x - newTouch2.x, 2) +
  662. Math.pow(newTouch1.y - newTouch2.y, 2)
  663. );
  664. var cha = newL - oldL;
  665. // 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
  666. // 1px - 0.2
  667. var coe = 1;
  668. coe =
  669. coe / this.trueWidth > coe / this.trueHeight
  670. ? coe / this.trueHeight
  671. : coe / this.trueWidth;
  672. coe = coe > 0.1 ? 0.1 : coe;
  673. var num = coe * cha;
  674. if (!this.touchNow) {
  675. this.touchNow = true;
  676. if (cha > 0) {
  677. scale += Math.abs(num);
  678. } else if (cha < 0) {
  679. scale > Math.abs(num) ? (scale -= Math.abs(num)) : scale;
  680. }
  681. this.touches = e.touches;
  682. setTimeout(() => {
  683. this.touchNow = false;
  684. }, 8);
  685. if (!this.checkoutImgAxis(this.x, this.y, scale)) {
  686. return false;
  687. }
  688. this.scale = scale;
  689. }
  690. },
  691. cancelTouchScale(e) {
  692. window.removeEventListener("touchmove", this.touchScale);
  693. },
  694. // 移动图片
  695. moveImg(e) {
  696. e.preventDefault();
  697. if (e.touches && e.touches.length === 2) {
  698. this.touches = e.touches;
  699. window.addEventListener("touchmove", this.touchScale);
  700. window.addEventListener("touchend", this.cancelTouchScale);
  701. window.removeEventListener("touchmove", this.moveImg);
  702. return false;
  703. }
  704. let nowX = e.clientX ? e.clientX : e.touches[0].clientX;
  705. let nowY = e.clientY ? e.clientY : e.touches[0].clientY;
  706. let changeX, changeY;
  707. changeX = nowX - this.moveX;
  708. changeY = nowY - this.moveY;
  709. this.$nextTick(() => {
  710. if (this.centerBox) {
  711. let axis = this.getImgAxis(changeX, changeY, this.scale);
  712. let cropAxis = this.getCropAxis();
  713. let imgW = this.trueHeight * this.scale;
  714. let imgH = this.trueWidth * this.scale;
  715. let maxLeft, maxTop, maxRight, maxBottom;
  716. switch (this.rotate) {
  717. case 1:
  718. case -1:
  719. case 3:
  720. case -3:
  721. maxLeft =
  722. this.cropOffsertX -
  723. (this.trueWidth * (1 - this.scale)) / 2 +
  724. (imgW - imgH) / 2;
  725. maxTop =
  726. this.cropOffsertY -
  727. (this.trueHeight * (1 - this.scale)) / 2 +
  728. (imgH - imgW) / 2;
  729. maxRight = maxLeft - imgW + this.cropW;
  730. maxBottom = maxTop - imgH + this.cropH;
  731. break;
  732. default:
  733. maxLeft =
  734. this.cropOffsertX - (this.trueWidth * (1 - this.scale)) / 2;
  735. maxTop =
  736. this.cropOffsertY - (this.trueHeight * (1 - this.scale)) / 2;
  737. maxRight = maxLeft - imgH + this.cropW;
  738. maxBottom = maxTop - imgW + this.cropH;
  739. break;
  740. }
  741. // 图片左边 图片不能超过截图框
  742. if (axis.x1 >= cropAxis.x1) {
  743. changeX = maxLeft;
  744. }
  745. // 图片上边 图片不能超过截图框
  746. if (axis.y1 >= cropAxis.y1) {
  747. changeY = maxTop;
  748. }
  749. // 图片右边
  750. if (axis.x2 <= cropAxis.x2) {
  751. changeX = maxRight;
  752. }
  753. // 图片下边
  754. if (axis.y2 <= cropAxis.y2) {
  755. changeY = maxBottom;
  756. }
  757. }
  758. this.x = changeX;
  759. this.y = changeY;
  760. // 触发图片移动事件
  761. this.$emit("imgMoving", {
  762. moving: true,
  763. axis: this.getImgAxis()
  764. });
  765. this.$emit("img-moving", {
  766. moving: true,
  767. axis: this.getImgAxis()
  768. });
  769. });
  770. },
  771. // 移动图片结束
  772. leaveImg(e) {
  773. window.removeEventListener("mousemove", this.moveImg);
  774. window.removeEventListener("touchmove", this.moveImg);
  775. window.removeEventListener("mouseup", this.leaveImg);
  776. window.removeEventListener("touchend", this.leaveImg);
  777. // 触发图片移动事件
  778. this.$emit("imgMoving", {
  779. moving: false,
  780. axis: this.getImgAxis()
  781. });
  782. this.$emit("img-moving", {
  783. moving: false,
  784. axis: this.getImgAxis()
  785. });
  786. },
  787. // 缩放图片
  788. scaleImg() {
  789. if (this.canScale) {
  790. window.addEventListener(this.support, this.changeSize, this.passive);
  791. }
  792. },
  793. // 移出框
  794. cancelScale() {
  795. if (this.canScale) {
  796. window.removeEventListener(this.support, this.changeSize);
  797. }
  798. },
  799. // 改变大小函数
  800. changeSize(e) {
  801. e.preventDefault();
  802. let scale = this.scale;
  803. var change = e.deltaY || e.wheelDelta;
  804. // 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
  805. var isFirefox = navigator.userAgent.indexOf("Firefox");
  806. change = isFirefox > 0 ? change * 30 : change;
  807. // 修复ie的滚动缩放
  808. if (this.isIE) {
  809. change = -change;
  810. }
  811. // 1px - 0.2
  812. var coe = this.coe;
  813. coe =
  814. coe / this.trueWidth > coe / this.trueHeight
  815. ? coe / this.trueHeight
  816. : coe / this.trueWidth;
  817. var num = coe * change;
  818. num < 0
  819. ? (scale += Math.abs(num))
  820. : scale > Math.abs(num)
  821. ? (scale -= Math.abs(num))
  822. : scale;
  823. // 延迟0.1s 每次放大大或者缩小的范围
  824. let status = num < 0 ? "add" : "reduce";
  825. if (status !== this.coeStatus) {
  826. this.coeStatus = status;
  827. this.coe = 0.2;
  828. }
  829. if (!this.scaling) {
  830. this.scalingSet = setTimeout(() => {
  831. this.scaling = false;
  832. this.coe = this.coe += 0.01;
  833. }, 50);
  834. }
  835. this.scaling = true;
  836. if (!this.checkoutImgAxis(this.x, this.y, scale)) {
  837. return false;
  838. }
  839. this.scale = scale;
  840. },
  841. // 修改图片大小函数
  842. changeScale(num) {
  843. let scale = this.scale;
  844. num = num || 1;
  845. var coe = 20;
  846. coe =
  847. coe / this.trueWidth > coe / this.trueHeight
  848. ? coe / this.trueHeight
  849. : coe / this.trueWidth;
  850. num = num * coe;
  851. num > 0
  852. ? (scale += Math.abs(num))
  853. : scale > Math.abs(num)
  854. ? (scale -= Math.abs(num))
  855. : scale;
  856. if (!this.checkoutImgAxis(this.x, this.y, scale)) {
  857. return false;
  858. }
  859. this.scale = scale;
  860. },
  861. // 创建截图框
  862. createCrop(e) {
  863. e.preventDefault();
  864. // 移动生成大小
  865. var nowX = e.clientX ? e.clientX : e.touches ? e.touches[0].clientX : 0;
  866. var nowY = e.clientY ? e.clientY : e.touches ? e.touches[0].clientY : 0;
  867. this.$nextTick(() => {
  868. var fw = nowX - this.cropX;
  869. var fh = nowY - this.cropY;
  870. if (fw > 0) {
  871. this.cropW =
  872. fw + this.cropChangeX > this.w ? this.w - this.cropChangeX : fw;
  873. this.cropOffsertX = this.cropChangeX;
  874. } else {
  875. this.cropW =
  876. this.w - this.cropChangeX + Math.abs(fw) > this.w
  877. ? this.cropChangeX
  878. : Math.abs(fw);
  879. this.cropOffsertX =
  880. this.cropChangeX + fw > 0 ? this.cropChangeX + fw : 0;
  881. }
  882. if (!this.fixed) {
  883. if (fh > 0) {
  884. this.cropH =
  885. fh + this.cropChangeY > this.h ? this.h - this.cropChangeY : fh;
  886. this.cropOffsertY = this.cropChangeY;
  887. } else {
  888. this.cropH =
  889. this.h - this.cropChangeY + Math.abs(fh) > this.h
  890. ? this.cropChangeY
  891. : Math.abs(fh);
  892. this.cropOffsertY =
  893. this.cropChangeY + fh > 0 ? this.cropChangeY + fh : 0;
  894. }
  895. } else {
  896. var fixedHeight =
  897. (this.cropW / this.fixedNumber[0]) * this.fixedNumber[1];
  898. if (fixedHeight + this.cropOffsertY > this.h) {
  899. this.cropH = this.h - this.cropOffsertY;
  900. this.cropW =
  901. (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
  902. if (fw > 0) {
  903. this.cropOffsertX = this.cropChangeX;
  904. } else {
  905. this.cropOffsertX = this.cropChangeX - this.cropW;
  906. }
  907. } else {
  908. this.cropH = fixedHeight;
  909. }
  910. this.cropOffsertY = this.cropOffsertY;
  911. }
  912. });
  913. },
  914. // 改变截图框大小
  915. changeCropSize(e, w, h, typeW, typeH) {
  916. e.preventDefault();
  917. window.addEventListener("mousemove", this.changeCropNow);
  918. window.addEventListener("mouseup", this.changeCropEnd);
  919. window.addEventListener("touchmove", this.changeCropNow);
  920. window.addEventListener("touchend", this.changeCropEnd);
  921. this.canChangeX = w;
  922. this.canChangeY = h;
  923. this.changeCropTypeX = typeW;
  924. this.changeCropTypeY = typeH;
  925. this.cropX = e.clientX ? e.clientX : e.touches[0].clientX;
  926. this.cropY = e.clientY ? e.clientY : e.touches[0].clientY;
  927. this.cropOldW = this.cropW;
  928. this.cropOldH = this.cropH;
  929. this.cropChangeX = this.cropOffsertX;
  930. this.cropChangeY = this.cropOffsertY;
  931. if (this.fixed) {
  932. if (this.canChangeX && this.canChangeY) {
  933. this.canChangeY = 0;
  934. }
  935. }
  936. this.$emit('change-crop-size', {
  937. width: this.cropW,
  938. height: this.cropH
  939. })
  940. },
  941. // 正在改变
  942. changeCropNow(e) {
  943. e.preventDefault();
  944. var nowX = e.clientX ? e.clientX : e.touches ? e.touches[0].clientX : 0;
  945. var nowY = e.clientY ? e.clientY : e.touches ? e.touches[0].clientY : 0;
  946. // 容器的宽高
  947. let wrapperW = this.w;
  948. let wrapperH = this.h;
  949. // 不能超过的坐标轴
  950. let minX = 0;
  951. let minY = 0;
  952. if (this.centerBox) {
  953. let axis = this.getImgAxis();
  954. let imgW = axis.x2;
  955. let imgH = axis.y2;
  956. minX = axis.x1 > 0 ? axis.x1 : 0;
  957. minY = axis.y1 > 0 ? axis.y1 : 0;
  958. if (wrapperW > imgW) {
  959. wrapperW = imgW;
  960. }
  961. if (wrapperH > imgH) {
  962. wrapperH = imgH;
  963. }
  964. }
  965. this.$nextTick(() => {
  966. var fw = nowX - this.cropX;
  967. var fh = nowY - this.cropY;
  968. if (this.canChangeX) {
  969. if (this.changeCropTypeX === 1) {
  970. if (this.cropOldW - fw > 0) {
  971. this.cropW =
  972. wrapperW - this.cropChangeX - fw <= wrapperW - minX
  973. ? this.cropOldW - fw
  974. : this.cropOldW + this.cropChangeX - minX;
  975. this.cropOffsertX =
  976. wrapperW - this.cropChangeX - fw <= wrapperW - minX
  977. ? this.cropChangeX + fw
  978. : minX;
  979. } else {
  980. this.cropW =
  981. Math.abs(fw) + this.cropChangeX <= wrapperW
  982. ? Math.abs(fw) - this.cropOldW
  983. : wrapperW - this.cropOldW - this.cropChangeX;
  984. this.cropOffsertX = this.cropChangeX + this.cropOldW;
  985. }
  986. } else if (this.changeCropTypeX === 2) {
  987. if (this.cropOldW + fw > 0) {
  988. this.cropW =
  989. this.cropOldW + fw + this.cropOffsertX <= wrapperW
  990. ? this.cropOldW + fw
  991. : wrapperW - this.cropOffsertX;
  992. this.cropOffsertX = this.cropChangeX;
  993. } else {
  994. // 右侧坐标抽 超过左侧
  995. this.cropW =
  996. wrapperW - this.cropChangeX + Math.abs(fw + this.cropOldW) <=
  997. wrapperW - minX
  998. ? Math.abs(fw + this.cropOldW)
  999. : this.cropChangeX - minX;
  1000. this.cropOffsertX =
  1001. wrapperW - this.cropChangeX + Math.abs(fw + this.cropOldW) <=
  1002. wrapperW - minX
  1003. ? this.cropChangeX - Math.abs(fw + this.cropOldW)
  1004. : minX;
  1005. }
  1006. }
  1007. }
  1008. if (this.canChangeY) {
  1009. if (this.changeCropTypeY === 1) {
  1010. if (this.cropOldH - fh > 0) {
  1011. this.cropH =
  1012. wrapperH - this.cropChangeY - fh <= wrapperH - minY
  1013. ? this.cropOldH - fh
  1014. : this.cropOldH + this.cropChangeY - minY;
  1015. this.cropOffsertY =
  1016. wrapperH - this.cropChangeY - fh <= wrapperH - minY
  1017. ? this.cropChangeY + fh
  1018. : minY;
  1019. } else {
  1020. this.cropH =
  1021. Math.abs(fh) + this.cropChangeY <= wrapperH
  1022. ? Math.abs(fh) - this.cropOldH
  1023. : wrapperH - this.cropOldH - this.cropChangeY;
  1024. this.cropOffsertY = this.cropChangeY + this.cropOldH;
  1025. }
  1026. } else if (this.changeCropTypeY === 2) {
  1027. if (this.cropOldH + fh > 0) {
  1028. this.cropH =
  1029. this.cropOldH + fh + this.cropOffsertY <= wrapperH
  1030. ? this.cropOldH + fh
  1031. : wrapperH - this.cropOffsertY;
  1032. this.cropOffsertY = this.cropChangeY;
  1033. } else {
  1034. this.cropH =
  1035. wrapperH - this.cropChangeY + Math.abs(fh + this.cropOldH) <=
  1036. wrapperH - minY
  1037. ? Math.abs(fh + this.cropOldH)
  1038. : this.cropChangeY - minY;
  1039. this.cropOffsertY =
  1040. wrapperH - this.cropChangeY + Math.abs(fh + this.cropOldH) <=
  1041. wrapperH - minY
  1042. ? this.cropChangeY - Math.abs(fh + this.cropOldH)
  1043. : minY;
  1044. }
  1045. }
  1046. }
  1047. if (this.canChangeX && this.fixed) {
  1048. var fixedHeight =
  1049. (this.cropW / this.fixedNumber[0]) * this.fixedNumber[1];
  1050. if (fixedHeight + this.cropOffsertY > wrapperH) {
  1051. this.cropH = wrapperH - this.cropOffsertY;
  1052. this.cropW =
  1053. (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
  1054. } else {
  1055. this.cropH = fixedHeight;
  1056. }
  1057. }
  1058. if (this.canChangeY && this.fixed) {
  1059. var fixedWidth =
  1060. (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
  1061. if (fixedWidth + this.cropOffsertX > wrapperW) {
  1062. this.cropW = wrapperW - this.cropOffsertX;
  1063. this.cropH =
  1064. (this.cropW / this.fixedNumber[0]) * this.fixedNumber[1];
  1065. } else {
  1066. this.cropW = fixedWidth;
  1067. }
  1068. }
  1069. });
  1070. },
  1071. checkCropLimitSize () {
  1072. let { cropW, cropH, limitMinSize } = this;
  1073. let limitMinNum = new Array;
  1074. if (!Array.isArray[limitMinSize]) {
  1075. limitMinNum = [limitMinSize, limitMinSize]
  1076. } else {
  1077. limitMinNum = limitMinSize
  1078. }
  1079. //限制最小宽度和高度
  1080. cropW = parseFloat(limitMinNum[0])
  1081. cropH = parseFloat(limitMinNum[1])
  1082. return [cropW, cropH]
  1083. },
  1084. // 结束改变
  1085. changeCropEnd(e) {
  1086. window.removeEventListener("mousemove", this.changeCropNow);
  1087. window.removeEventListener("mouseup", this.changeCropEnd);
  1088. window.removeEventListener("touchmove", this.changeCropNow);
  1089. window.removeEventListener("touchend", this.changeCropEnd);
  1090. },
  1091. // 创建完成
  1092. endCrop() {
  1093. if (this.cropW === 0 && this.cropH === 0) {
  1094. this.cropping = false;
  1095. }
  1096. window.removeEventListener("mousemove", this.createCrop);
  1097. window.removeEventListener("mouseup", this.endCrop);
  1098. window.removeEventListener("touchmove", this.createCrop);
  1099. window.removeEventListener("touchend", this.endCrop);
  1100. },
  1101. // 开始截图
  1102. startCrop() {
  1103. this.crop = true;
  1104. },
  1105. // 停止截图
  1106. stopCrop() {
  1107. this.crop = false;
  1108. },
  1109. // 清除截图
  1110. clearCrop() {
  1111. this.cropping = false;
  1112. this.cropW = 0;
  1113. this.cropH = 0;
  1114. },
  1115. // 截图移动
  1116. cropMove(e) {
  1117. e.preventDefault();
  1118. if (!this.canMoveBox) {
  1119. this.crop = false;
  1120. this.startMove(e);
  1121. return false;
  1122. }
  1123. if (e.touches && e.touches.length === 2) {
  1124. this.crop = false;
  1125. this.startMove(e);
  1126. this.leaveCrop();
  1127. return false;
  1128. }
  1129. window.addEventListener("mousemove", this.moveCrop);
  1130. window.addEventListener("mouseup", this.leaveCrop);
  1131. window.addEventListener("touchmove", this.moveCrop);
  1132. window.addEventListener("touchend", this.leaveCrop);
  1133. let x = e.clientX ? e.clientX : e.touches[0].clientX;
  1134. let y = e.clientY ? e.clientY : e.touches[0].clientY;
  1135. let newX, newY;
  1136. newX = x - this.cropOffsertX;
  1137. newY = y - this.cropOffsertY;
  1138. this.cropX = newX;
  1139. this.cropY = newY;
  1140. // 触发截图框移动事件
  1141. this.$emit("cropMoving", {
  1142. moving: true,
  1143. axis: this.getCropAxis()
  1144. });
  1145. this.$emit("crop-moving", {
  1146. moving: true,
  1147. axis: this.getCropAxis()
  1148. });
  1149. },
  1150. moveCrop(e, isMove) {
  1151. let nowX = 0;
  1152. let nowY = 0;
  1153. if (e) {
  1154. e.preventDefault();
  1155. nowX = e.clientX ? e.clientX : e.touches[0].clientX;
  1156. nowY = e.clientY ? e.clientY : e.touches[0].clientY;
  1157. }
  1158. this.$nextTick(() => {
  1159. let cx, cy;
  1160. let fw = nowX - this.cropX;
  1161. let fh = nowY - this.cropY;
  1162. if (isMove) {
  1163. fw = this.cropOffsertX;
  1164. fh = this.cropOffsertY;
  1165. }
  1166. // 不能超过外层容器
  1167. if (fw <= 0) {
  1168. cx = 0;
  1169. } else if (fw + this.cropW > this.w) {
  1170. cx = this.w - this.cropW;
  1171. } else {
  1172. cx = fw;
  1173. }
  1174. if (fh <= 0) {
  1175. cy = 0;
  1176. } else if (fh + this.cropH > this.h) {
  1177. cy = this.h - this.cropH;
  1178. } else {
  1179. cy = fh;
  1180. }
  1181. // 不能超过图片
  1182. if (this.centerBox) {
  1183. let axis = this.getImgAxis();
  1184. // 横坐标判断
  1185. if (cx <= axis.x1) {
  1186. cx = axis.x1;
  1187. }
  1188. if (cx + this.cropW > axis.x2) {
  1189. cx = axis.x2 - this.cropW;
  1190. }
  1191. // 纵坐标纵轴
  1192. if (cy <= axis.y1) {
  1193. cy = axis.y1;
  1194. }
  1195. if (cy + this.cropH > axis.y2) {
  1196. cy = axis.y2 - this.cropH;
  1197. }
  1198. }
  1199. this.cropOffsertX = cx;
  1200. this.cropOffsertY = cy;
  1201. // 触发截图框移动事件
  1202. this.$emit("cropMoving", {
  1203. moving: true,
  1204. axis: this.getCropAxis()
  1205. });
  1206. this.$emit("crop-moving", {
  1207. moving: true,
  1208. axis: this.getCropAxis()
  1209. });
  1210. });
  1211. },
  1212. // 算出不同场景下面 图片相对于外层容器的坐标轴
  1213. getImgAxis(x, y, scale) {
  1214. x = x || this.x;
  1215. y = y || this.y;
  1216. scale = scale || this.scale;
  1217. // 如果设置了截图框在图片内, 那么限制截图框不能超过图片的坐标
  1218. // 图片的坐标
  1219. let obj = {
  1220. x1: 0,
  1221. x2: 0,
  1222. y1: 0,
  1223. y2: 0
  1224. };
  1225. let imgW = this.trueWidth * scale;
  1226. let imgH = this.trueHeight * scale;
  1227. switch (this.rotate) {
  1228. case 0:
  1229. obj.x1 = x + (this.trueWidth * (1 - scale)) / 2;
  1230. obj.x2 = obj.x1 + this.trueWidth * scale;
  1231. obj.y1 = y + (this.trueHeight * (1 - scale)) / 2;
  1232. obj.y2 = obj.y1 + this.trueHeight * scale;
  1233. break;
  1234. case 1:
  1235. case -1:
  1236. case 3:
  1237. case -3:
  1238. obj.x1 = x + (this.trueWidth * (1 - scale)) / 2 + (imgW - imgH) / 2;
  1239. obj.x2 = obj.x1 + this.trueHeight * scale;
  1240. obj.y1 = y + (this.trueHeight * (1 - scale)) / 2 + (imgH - imgW) / 2;
  1241. obj.y2 = obj.y1 + this.trueWidth * scale;
  1242. break;
  1243. default:
  1244. obj.x1 = x + (this.trueWidth * (1 - scale)) / 2;
  1245. obj.x2 = obj.x1 + this.trueWidth * scale;
  1246. obj.y1 = y + (this.trueHeight * (1 - scale)) / 2;
  1247. obj.y2 = obj.y1 + this.trueHeight * scale;
  1248. break;
  1249. }
  1250. return obj;
  1251. },
  1252. // 获取截图框的坐标轴
  1253. getCropAxis() {
  1254. let obj = {
  1255. x1: 0,
  1256. x2: 0,
  1257. y1: 0,
  1258. y2: 0
  1259. };
  1260. obj.x1 = this.cropOffsertX;
  1261. obj.x2 = obj.x1 + this.cropW;
  1262. obj.y1 = this.cropOffsertY;
  1263. obj.y2 = obj.y1 + this.cropH;
  1264. return obj;
  1265. },
  1266. leaveCrop(e) {
  1267. window.removeEventListener("mousemove", this.moveCrop);
  1268. window.removeEventListener("mouseup", this.leaveCrop);
  1269. window.removeEventListener("touchmove", this.moveCrop);
  1270. window.removeEventListener("touchend", this.leaveCrop);
  1271. // 触发截图框移动事件
  1272. this.$emit("cropMoving", {
  1273. moving: false,
  1274. axis: this.getCropAxis()
  1275. });
  1276. this.$emit("crop-moving", {
  1277. moving: false,
  1278. axis: this.getCropAxis()
  1279. });
  1280. },
  1281. getCropChecked(cb) {
  1282. let canvas = document.createElement("canvas");
  1283. let img = new Image();
  1284. let rotate = this.rotate;
  1285. let trueWidth = this.trueWidth;
  1286. let trueHeight = this.trueHeight;
  1287. let cropOffsertX = this.cropOffsertX;
  1288. let cropOffsertY = this.cropOffsertY;
  1289. img.onload = () => {
  1290. if (this.cropW !== 0) {
  1291. let ctx = canvas.getContext("2d");
  1292. let dpr = 1;
  1293. if (this.high & !this.full) {
  1294. dpr = window.devicePixelRatio;
  1295. }
  1296. if ((this.enlarge !== 1) & !this.full) {
  1297. dpr = Math.abs(Number(this.enlarge));
  1298. }
  1299. let width = this.cropW * dpr;
  1300. let height = this.cropH * dpr;
  1301. let imgW = trueWidth * this.scale * dpr;
  1302. let imgH = trueHeight * this.scale * dpr;
  1303. // 图片x轴偏移
  1304. let dx =
  1305. (this.x - cropOffsertX + (this.trueWidth * (1 - this.scale)) / 2) *
  1306. dpr;
  1307. // 图片y轴偏移
  1308. let dy =
  1309. (this.y - cropOffsertY + (this.trueHeight * (1 - this.scale)) / 2) *
  1310. dpr;
  1311. //保存状态
  1312. setCanvasSize(width, height);
  1313. ctx.save();
  1314. switch (rotate) {
  1315. case 0:
  1316. if (!this.full) {
  1317. ctx.drawImage(img, dx, dy, imgW, imgH);
  1318. } else {
  1319. // 输出原图比例截图
  1320. setCanvasSize(width / this.scale, height / this.scale);
  1321. ctx.drawImage(
  1322. img,
  1323. dx / this.scale,
  1324. dy / this.scale,
  1325. imgW / this.scale,
  1326. imgH / this.scale
  1327. );
  1328. }
  1329. break;
  1330. case 1:
  1331. case -3:
  1332. if (!this.full) {
  1333. // 换算图片旋转后的坐标弥补
  1334. dx = dx + (imgW - imgH) / 2;
  1335. dy = dy + (imgH - imgW) / 2;
  1336. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1337. ctx.drawImage(img, dy, -dx - imgH, imgW, imgH);
  1338. } else {
  1339. setCanvasSize(width / this.scale, height / this.scale);
  1340. // 换算图片旋转后的坐标弥补
  1341. dx =
  1342. dx / this.scale + (imgW / this.scale - imgH / this.scale) / 2;
  1343. dy =
  1344. dy / this.scale + (imgH / this.scale - imgW / this.scale) / 2;
  1345. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1346. ctx.drawImage(
  1347. img,
  1348. dy,
  1349. -dx - imgH / this.scale,
  1350. imgW / this.scale,
  1351. imgH / this.scale
  1352. );
  1353. }
  1354. break;
  1355. case 2:
  1356. case -2:
  1357. if (!this.full) {
  1358. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1359. ctx.drawImage(img, -dx - imgW, -dy - imgH, imgW, imgH);
  1360. } else {
  1361. setCanvasSize(width / this.scale, height / this.scale);
  1362. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1363. dx = dx / this.scale;
  1364. dy = dy / this.scale;
  1365. ctx.drawImage(
  1366. img,
  1367. -dx - imgW / this.scale,
  1368. -dy - imgH / this.scale,
  1369. imgW / this.scale,
  1370. imgH / this.scale
  1371. );
  1372. }
  1373. break;
  1374. case 3:
  1375. case -1:
  1376. if (!this.full) {
  1377. // 换算图片旋转后的坐标弥补
  1378. dx = dx + (imgW - imgH) / 2;
  1379. dy = dy + (imgH - imgW) / 2;
  1380. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1381. ctx.drawImage(img, -dy - imgW, dx, imgW, imgH);
  1382. } else {
  1383. setCanvasSize(width / this.scale, height / this.scale);
  1384. // 换算图片旋转后的坐标弥补
  1385. dx =
  1386. dx / this.scale + (imgW / this.scale - imgH / this.scale) / 2;
  1387. dy =
  1388. dy / this.scale + (imgH / this.scale - imgW / this.scale) / 2;
  1389. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1390. ctx.drawImage(
  1391. img,
  1392. -dy - imgW / this.scale,
  1393. dx,
  1394. imgW / this.scale,
  1395. imgH / this.scale
  1396. );
  1397. }
  1398. break;
  1399. default:
  1400. if (!this.full) {
  1401. ctx.drawImage(img, dx, dy, imgW, imgH);
  1402. } else {
  1403. // 输出原图比例截图
  1404. setCanvasSize(width / this.scale, height / this.scale);
  1405. ctx.drawImage(
  1406. img,
  1407. dx / this.scale,
  1408. dy / this.scale,
  1409. imgW / this.scale,
  1410. imgH / this.scale
  1411. );
  1412. }
  1413. }
  1414. ctx.restore();
  1415. } else {
  1416. let width = trueWidth * this.scale;
  1417. let height = trueHeight * this.scale;
  1418. let ctx = canvas.getContext("2d");
  1419. ctx.save();
  1420. switch (rotate) {
  1421. case 0:
  1422. setCanvasSize(width, height);
  1423. ctx.drawImage(img, 0, 0, width, height);
  1424. break;
  1425. case 1:
  1426. case -3:
  1427. // 旋转90度 或者-270度 宽度和高度对调
  1428. setCanvasSize(height, width);
  1429. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1430. ctx.drawImage(img, 0, -height, width, height);
  1431. break;
  1432. case 2:
  1433. case -2:
  1434. setCanvasSize(width, height);
  1435. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1436. ctx.drawImage(img, -width, -height, width, height);
  1437. break;
  1438. case 3:
  1439. case -1:
  1440. setCanvasSize(height, width);
  1441. ctx.rotate((rotate * 90 * Math.PI) / 180);
  1442. ctx.drawImage(img, -width, 0, width, height);
  1443. break;
  1444. default:
  1445. setCanvasSize(width, height);
  1446. ctx.drawImage(img, 0, 0, width, height);
  1447. }
  1448. ctx.restore();
  1449. }
  1450. cb(canvas);
  1451. };
  1452. // 判断图片是否是base64
  1453. var s = this.img.substr(0, 4);
  1454. if (s !== "data") {
  1455. img.crossOrigin = "Anonymous";
  1456. }
  1457. img.src = this.imgs;
  1458. function setCanvasSize(width, height) {
  1459. canvas.width = Math.round(width);
  1460. canvas.height = Math.round(height);
  1461. }
  1462. },
  1463. // 获取转换成base64 的图片信息
  1464. getCropData(cb) {
  1465. this.getCropChecked(data => {
  1466. cb(data.toDataURL("image/" + this.outputType, this.outputSize));
  1467. });
  1468. },
  1469. //canvas获取为blob对象
  1470. getCropBlob(cb) {
  1471. this.getCropChecked(data => {
  1472. data.toBlob(
  1473. blob => cb(blob),
  1474. "image/" + this.outputType,
  1475. this.outputSize
  1476. );
  1477. });
  1478. },
  1479. // 自动预览函数
  1480. showPreview() {
  1481. // 优化不要多次触发
  1482. if (this.isCanShow) {
  1483. this.isCanShow = false;
  1484. setTimeout(() => {
  1485. this.isCanShow = true;
  1486. }, 16);
  1487. } else {
  1488. return false;
  1489. }
  1490. let w = this.cropW;
  1491. let h = this.cropH;
  1492. let scale = this.scale;
  1493. var obj = {};
  1494. obj.div = {
  1495. width: `${w}px`,
  1496. height: `${h}px`
  1497. };
  1498. let transformX = (this.x - this.cropOffsertX) / scale;
  1499. let transformY = (this.y - this.cropOffsertY) / scale;
  1500. let transformZ = 0;
  1501. obj.w = w;
  1502. obj.h = h;
  1503. obj.url = this.imgs;
  1504. obj.img = {
  1505. width: `${this.trueWidth}px`,
  1506. height: `${this.trueHeight}px`,
  1507. transform: `scale(${scale})translate3d(${transformX}px, ${transformY}px, ${transformZ}px)rotateZ(${this
  1508. .rotate * 90}deg)`
  1509. };
  1510. obj.html = `
  1511. <div class="show-preview" style="width: ${obj.w}px; height: ${
  1512. obj.h
  1513. }px,; overflow: hidden">
  1514. <div style="width: ${w}px; height: ${h}px">
  1515. <img src=${obj.url} style="width: ${this.trueWidth}px; height: ${
  1516. this.trueHeight
  1517. }px; transform:
  1518. scale(${scale})translate3d(${transformX}px, ${transformY}px, ${transformZ}px)rotateZ(${this
  1519. .rotate * 90}deg)">
  1520. </div>
  1521. </div>`;
  1522. this.$emit("realTime", obj);
  1523. this.$emit("real-time", obj);
  1524. },
  1525. // reload 图片布局函数
  1526. reload() {
  1527. let img = new Image();
  1528. img.onload = () => {
  1529. // 读取图片的信息原始信息, 解析是否需要旋转
  1530. // 读取图片的旋转信息
  1531. // 得到外层容器的宽度高度
  1532. this.w = parseFloat(window.getComputedStyle(this.$refs.cropper).width);
  1533. this.h = parseFloat(window.getComputedStyle(this.$refs.cropper).height);
  1534. // 存入图片真实高度
  1535. this.trueWidth = img.width;
  1536. this.trueHeight = img.height;
  1537. // 判断是否需要压缩大图
  1538. if (!this.original) {
  1539. // 判断布局方式 mode
  1540. this.scale = this.checkedMode();
  1541. } else {
  1542. this.scale = 1;
  1543. }
  1544. this.$nextTick(() => {
  1545. this.x =
  1546. -(this.trueWidth - this.trueWidth * this.scale) / 2 +
  1547. (this.w - this.trueWidth * this.scale) / 2;
  1548. this.y =
  1549. -(this.trueHeight - this.trueHeight * this.scale) / 2 +
  1550. (this.h - this.trueHeight * this.scale) / 2;
  1551. this.loading = false;
  1552. // // 获取是否开启了自动截图
  1553. if (this.autoCrop) {
  1554. this.goAutoCrop();
  1555. }
  1556. // 图片加载成功的回调
  1557. this.$emit("img-load", "success");
  1558. this.$emit("imgLoad", "success");
  1559. setTimeout(() => {
  1560. this.showPreview();
  1561. }, 20);
  1562. });
  1563. };
  1564. img.onerror = () => {
  1565. this.$emit("imgLoad", "error");
  1566. this.$emit("img-load", "error");
  1567. };
  1568. img.src = this.imgs;
  1569. },
  1570. // 背景布局的函数
  1571. checkedMode() {
  1572. let scale = 1;
  1573. // 通过字符串分割
  1574. let imgW = this.trueWidth;
  1575. let imgH = this.trueHeight;
  1576. const arr = this.mode.split(" ");
  1577. switch (arr[0]) {
  1578. case "contain":
  1579. if (this.trueWidth > this.w) {
  1580. // 如果图片宽度大于容器宽度
  1581. scale = this.w / this.trueWidth;
  1582. }
  1583. if (this.trueHeight * scale > this.h) {
  1584. scale = this.h / this.trueHeight;
  1585. }
  1586. break;
  1587. case "cover":
  1588. // 扩展布局 默认填充满整个容器
  1589. // 图片宽度大于容器
  1590. imgW = this.w;
  1591. scale = imgW / this.trueWidth;
  1592. imgH = imgH * scale;
  1593. // 如果扩展之后高度小于容器的外层高度 继续扩展高度
  1594. if (imgH < this.h) {
  1595. imgH = this.h;
  1596. scale = imgH / this.trueHeight;
  1597. }
  1598. break;
  1599. default:
  1600. try {
  1601. let str = arr[0];
  1602. if (str.search("px") !== -1) {
  1603. str = str.replace("px", "");
  1604. imgW = parseFloat(str);
  1605. scale = imgW / this.trueWidth;
  1606. }
  1607. if (str.search("%") !== -1) {
  1608. str = str.replace("%", "");
  1609. imgW = (parseFloat(str) / 100) * this.w;
  1610. scale = imgW / this.trueWidth;
  1611. }
  1612. if (arr.length === 2 && str === "auto") {
  1613. let str2 = arr[1];
  1614. if (str2.search("px") !== -1) {
  1615. str2 = str2.replace("px", "");
  1616. imgH = parseFloat(str2);
  1617. scale = imgH / this.trueHeight;
  1618. }
  1619. if (str2.search("%") !== -1) {
  1620. str2 = str2.replace("%", "");
  1621. imgH = (parseFloat(str2) / 100) * this.h;
  1622. scale = imgH / this.trueHeight;
  1623. }
  1624. }
  1625. } catch (error) {
  1626. scale = 1;
  1627. }
  1628. }
  1629. return scale;
  1630. },
  1631. // 自动截图函数
  1632. goAutoCrop(cw, ch) {
  1633. if (this.imgs === '' || this.imgs === null) return
  1634. this.clearCrop();
  1635. this.cropping = true;
  1636. let maxWidth = this.w;
  1637. let maxHeight = this.h;
  1638. if (this.centerBox) {
  1639. let imgW = this.trueWidth * this.scale;
  1640. let imgH = this.trueHeight * this.scale;
  1641. maxWidth = imgW < maxWidth ? imgW : maxWidth;
  1642. maxHeight = imgH < maxHeight ? imgH : maxHeight;
  1643. }
  1644. // 截图框默认大小
  1645. // 如果为0 那么计算容器大小 默认为80%
  1646. var w = cw ? cw : parseFloat(this.autoCropWidth);
  1647. var h = ch ? ch : parseFloat(this.autoCropHeight);
  1648. if (w === 0 || h === 0) {
  1649. w = maxWidth * 0.8;
  1650. h = maxHeight * 0.8;
  1651. }
  1652. w = w > maxWidth ? maxWidth : w;
  1653. h = h > maxHeight ? maxHeight : h;
  1654. if (this.fixed) {
  1655. h = (w / this.fixedNumber[0]) * this.fixedNumber[1];
  1656. }
  1657. // 如果比例之后 高度大于h
  1658. if (h > this.h) {
  1659. h = this.h;
  1660. w = (h / this.fixedNumber[1]) * this.fixedNumber[0];
  1661. }
  1662. this.changeCrop(w, h);
  1663. },
  1664. // 手动改变截图框大小函数
  1665. changeCrop(w, h) {
  1666. if (this.centerBox) {
  1667. // 修复初始化时候在centerBox=true情况下
  1668. let axis = this.getImgAxis();
  1669. if (w > axis.x2 - axis.x1) {
  1670. // 宽度超标
  1671. w = axis.x2 - axis.x1;
  1672. h = (w / this.fixedNumber[0]) * this.fixedNumber[1];
  1673. }
  1674. if (h > axis.y2 - axis.y1) {
  1675. // 高度超标
  1676. h = axis.y2 - axis.y1;
  1677. w = (h / this.fixedNumber[1]) * this.fixedNumber[0];
  1678. }
  1679. }
  1680. // 判断是否大于容器
  1681. this.cropW = w;
  1682. this.cropH = h;
  1683. this.checkCropLimitSize()
  1684. this.$nextTick(() => {
  1685. // 居中走一走
  1686. this.cropOffsertX = (this.w - this.cropW) / 2;
  1687. this.cropOffsertY = (this.h - this.cropH) / 2;
  1688. if (this.centerBox) {
  1689. this.moveCrop(null, true);
  1690. }
  1691. });
  1692. },
  1693. // 重置函数, 恢复组件置初始状态
  1694. refresh() {
  1695. let img = this.img;
  1696. this.imgs = "";
  1697. this.scale = 1;
  1698. this.crop = false;
  1699. this.rotate = 0;
  1700. this.w = 0;
  1701. this.h = 0;
  1702. this.trueWidth = 0;
  1703. this.trueHeight = 0;
  1704. this.clearCrop();
  1705. this.$nextTick(() => {
  1706. this.checkedImg();
  1707. });
  1708. },
  1709. // 向左边旋转
  1710. rotateLeft() {
  1711. this.rotate = this.rotate <= -3 ? 0 : this.rotate - 1;
  1712. },
  1713. // 向右边旋转
  1714. rotateRight() {
  1715. this.rotate = this.rotate >= 3 ? 0 : this.rotate + 1;
  1716. },
  1717. // 清除旋转
  1718. rotateClear() {
  1719. this.rotate = 0;
  1720. },
  1721. // 图片坐标点校验
  1722. checkoutImgAxis(x, y, scale) {
  1723. x = x || this.x;
  1724. y = y || this.y;
  1725. scale = scale || this.scale;
  1726. let canGo = true;
  1727. // 开始校验 如果说缩放之后的坐标在截图框外 则阻止缩放
  1728. if (this.centerBox) {
  1729. let axis = this.getImgAxis(x, y, scale);
  1730. let cropAxis = this.getCropAxis();
  1731. // 左边的横坐标 图片不能超过截图框
  1732. if (axis.x1 >= cropAxis.x1) {
  1733. canGo = false;
  1734. }
  1735. // 右边横坐标
  1736. if (axis.x2 <= cropAxis.x2) {
  1737. canGo = false;
  1738. }
  1739. // 纵坐标上面
  1740. if (axis.y1 >= cropAxis.y1) {
  1741. canGo = false;
  1742. }
  1743. // 纵坐标下面
  1744. if (axis.y2 <= cropAxis.y2) {
  1745. canGo = false;
  1746. }
  1747. }
  1748. return canGo;
  1749. }
  1750. },
  1751. mounted() {
  1752. this.support =
  1753. "onwheel" in document.createElement("div")
  1754. ? "wheel"
  1755. : document.onmousewheel !== undefined
  1756. ? "mousewheel"
  1757. : "DOMMouseScroll";
  1758. let that = this;
  1759. var u = navigator.userAgent;
  1760. this.isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
  1761. // 兼容blob
  1762. if (!HTMLCanvasElement.prototype.toBlob) {
  1763. Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
  1764. value: function(callback, type, quality) {
  1765. var binStr = atob(this.toDataURL(type, quality).split(",")[1]),
  1766. len = binStr.length,
  1767. arr = new Uint8Array(len);
  1768. for (var i = 0; i < len; i++) {
  1769. arr[i] = binStr.charCodeAt(i);
  1770. }
  1771. callback(new Blob([arr], { type: that.type || "image/png" }));
  1772. }
  1773. });
  1774. }
  1775. this.showPreview();
  1776. this.checkedImg();
  1777. },
  1778. destroyed() {
  1779. window.removeEventListener("mousemove", this.moveCrop);
  1780. window.removeEventListener("mouseup", this.leaveCrop);
  1781. window.removeEventListener("touchmove", this.moveCrop);
  1782. window.removeEventListener("touchend", this.leaveCrop);
  1783. }
  1784. };
  1785. </script>
  1786. <style scoped lang="css">
  1787. .vue-cropper {
  1788. position: relative;
  1789. width: 100%;
  1790. height: 100%;
  1791. box-sizing: border-box;
  1792. user-select: none;
  1793. -webkit-user-select: none;
  1794. -moz-user-select: none;
  1795. -ms-user-select: none;
  1796. direction: ltr;
  1797. touch-action: none;
  1798. text-align: left;
  1799. background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
  1800. }
  1801. .cropper-box,
  1802. .cropper-box-canvas,
  1803. .cropper-drag-box,
  1804. .cropper-crop-box,
  1805. .cropper-face {
  1806. position: absolute;
  1807. top: 0;
  1808. right: 0;
  1809. bottom: 0;
  1810. left: 0;
  1811. user-select: none;
  1812. }
  1813. .cropper-box-canvas img {
  1814. position: relative;
  1815. text-align: left;
  1816. user-select: none;
  1817. transform: none;
  1818. max-width: none;
  1819. max-height: none;
  1820. }
  1821. .cropper-box {
  1822. overflow: hidden;
  1823. }
  1824. .cropper-move {
  1825. cursor: move;
  1826. }
  1827. .cropper-crop {
  1828. cursor: crosshair;
  1829. }
  1830. .cropper-modal {
  1831. background: rgba(0, 0, 0, 0.5);
  1832. }
  1833. .cropper-crop-box {
  1834. /*border: 2px solid #39f;*/
  1835. }
  1836. .cropper-view-box {
  1837. display: block;
  1838. overflow: hidden;
  1839. width: 100%;
  1840. height: 100%;
  1841. outline: 1px solid #39f;
  1842. outline-color: rgba(51, 153, 255, 0.75);
  1843. user-select: none;
  1844. }
  1845. .cropper-view-box img {
  1846. user-select: none;
  1847. text-align: left;
  1848. max-width: none;
  1849. max-height: none;
  1850. }
  1851. .cropper-face {
  1852. top: 0;
  1853. left: 0;
  1854. background-color: #fff;
  1855. opacity: 0.1;
  1856. }
  1857. .crop-info {
  1858. position: absolute;
  1859. left: 0px;
  1860. min-width: 65px;
  1861. text-align: center;
  1862. color: white;
  1863. line-height: 20px;
  1864. background-color: rgba(0, 0, 0, 0.8);
  1865. font-size: 12px;
  1866. }
  1867. .crop-line {
  1868. position: absolute;
  1869. display: block;
  1870. width: 100%;
  1871. height: 100%;
  1872. opacity: 0.1;
  1873. }
  1874. .line-w {
  1875. top: -3px;
  1876. left: 0;
  1877. height: 5px;
  1878. cursor: n-resize;
  1879. }
  1880. .line-a {
  1881. top: 0;
  1882. left: -3px;
  1883. width: 5px;
  1884. cursor: w-resize;
  1885. }
  1886. .line-s {
  1887. bottom: -3px;
  1888. left: 0;
  1889. height: 5px;
  1890. cursor: s-resize;
  1891. }
  1892. .line-d {
  1893. top: 0;
  1894. right: -3px;
  1895. width: 5px;
  1896. cursor: e-resize;
  1897. }
  1898. .crop-point {
  1899. position: absolute;
  1900. width: 8px;
  1901. height: 8px;
  1902. opacity: 0.75;
  1903. background-color: #39f;
  1904. border-radius: 100%;
  1905. }
  1906. .point1 {
  1907. top: -4px;
  1908. left: -4px;
  1909. cursor: nw-resize;
  1910. }
  1911. .point2 {
  1912. top: -5px;
  1913. left: 50%;
  1914. margin-left: -3px;
  1915. cursor: n-resize;
  1916. }
  1917. .point3 {
  1918. top: -4px;
  1919. right: -4px;
  1920. cursor: ne-resize;
  1921. }
  1922. .point4 {
  1923. top: 50%;
  1924. left: -4px;
  1925. margin-top: -3px;
  1926. cursor: w-resize;
  1927. }
  1928. .point5 {
  1929. top: 50%;
  1930. right: -4px;
  1931. margin-top: -3px;
  1932. cursor: e-resize;
  1933. }
  1934. .point6 {
  1935. bottom: -5px;
  1936. left: -4px;
  1937. cursor: sw-resize;
  1938. }
  1939. .point7 {
  1940. bottom: -5px;
  1941. left: 50%;
  1942. margin-left: -3px;
  1943. cursor: s-resize;
  1944. }
  1945. .point8 {
  1946. bottom: -5px;
  1947. right: -4px;
  1948. cursor: se-resize;
  1949. }
  1950. @media screen and (max-width: 500px) {
  1951. .crop-point {
  1952. position: absolute;
  1953. width: 20px;
  1954. height: 20px;
  1955. opacity: 0.45;
  1956. background-color: #39f;
  1957. border-radius: 100%;
  1958. }
  1959. .point1 {
  1960. top: -10px;
  1961. left: -10px;
  1962. }
  1963. .point2,
  1964. .point4,
  1965. .point5,
  1966. .point7 {
  1967. display: none;
  1968. }
  1969. .point3 {
  1970. top: -10px;
  1971. right: -10px;
  1972. }
  1973. .point4 {
  1974. top: 0;
  1975. left: 0;
  1976. }
  1977. .point6 {
  1978. bottom: -10px;
  1979. left: -10px;
  1980. }
  1981. .point8 {
  1982. bottom: -10px;
  1983. right: -10px;
  1984. }
  1985. }
  1986. </style>