12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087 |
- <template>
- <div class="vue-cropper" ref="cropper" @mouseover="scaleImg" @mouseout="cancelScale">
- <div class="cropper-box" v-if="imgs">
- <div
- class="cropper-box-canvas"
- v-show="!loading"
- :style="{
- 'width': trueWidth + 'px',
- 'height': trueHeight + 'px',
- 'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ x / scale + 'px,' + y / scale + 'px,' + '0)'
- + 'rotateZ('+ rotate * 90 +'deg)'
- }"
- >
- <img :src="imgs" alt="cropper-img" ref="cropperImg">
- </div>
- </div>
- <div
- class="cropper-drag-box"
- :class="{'cropper-move': move && !crop, 'cropper-crop': crop, 'cropper-modal': cropping}"
- @mousedown="startMove"
- @touchstart="startMove"
- ></div>
- <div
- v-show="cropping"
- class="cropper-crop-box"
- :style="{
- 'width': cropW + 'px',
- 'height': cropH + 'px',
- 'transform': 'translate3d('+ cropOffsertX + 'px,' + cropOffsertY + 'px,' + '0)'
- }"
- >
- <span class="cropper-view-box">
- <img
- :style="{
- 'width': trueWidth + 'px',
- 'height': trueHeight + 'px',
- 'transform': 'scale(' + scale + ',' + scale + ') ' + 'translate3d('+ (x - cropOffsertX) / scale + 'px,' + (y - cropOffsertY) / scale + 'px,' + '0)'
- + 'rotateZ('+ rotate * 90 +'deg)'
- }"
- :src="imgs"
- alt="cropper-img"
- >
- </span>
- <span class="cropper-face cropper-move" @mousedown="cropMove" @touchstart="cropMove"></span>
- <span
- class="crop-info"
- v-if="info"
- :style="{'top': cropInfo.top}"
- >{{ this.cropInfo.width }} × {{ this.cropInfo.height }}</span>
- <span v-if="!fixedBox">
- <span
- class="crop-line line-w"
- @mousedown="changeCropSize($event, false, true, 0, 1)"
- @touchstart="changeCropSize($event, false, true, 0, 1)"
- ></span>
- <span
- class="crop-line line-a"
- @mousedown="changeCropSize($event, true, false, 1, 0)"
- @touchstart="changeCropSize($event, true, false, 1, 0)"
- ></span>
- <span
- class="crop-line line-s"
- @mousedown="changeCropSize($event, false, true, 0, 2)"
- @touchstart="changeCropSize($event, false, true, 0, 2)"
- ></span>
- <span
- class="crop-line line-d"
- @mousedown="changeCropSize($event, true, false, 2, 0)"
- @touchstart="changeCropSize($event, true, false, 2, 0)"
- ></span>
- <span
- class="crop-point point1"
- @mousedown="changeCropSize($event, true, true, 1, 1)"
- @touchstart="changeCropSize($event, true, true, 1, 1)"
- ></span>
- <span
- class="crop-point point2"
- @mousedown="changeCropSize($event, false, true, 0, 1)"
- @touchstart="changeCropSize($event, false, true, 0, 1)"
- ></span>
- <span
- class="crop-point point3"
- @mousedown="changeCropSize($event, true, true, 2, 1)"
- @touchstart="changeCropSize($event, true, true, 2, 1)"
- ></span>
- <span
- class="crop-point point4"
- @mousedown="changeCropSize($event, true, false, 1, 0)"
- @touchstart="changeCropSize($event, true, false, 1, 0)"
- ></span>
- <span
- class="crop-point point5"
- @mousedown="changeCropSize($event, true, false, 2, 0)"
- @touchstart="changeCropSize($event, true, false, 2, 0)"
- ></span>
- <span
- class="crop-point point6"
- @mousedown="changeCropSize($event, true, true, 1, 2)"
- @touchstart="changeCropSize($event, true, true, 1, 2)"
- ></span>
- <span
- class="crop-point point7"
- @mousedown="changeCropSize($event, false, true, 0, 2)"
- @touchstart="changeCropSize($event, false, true, 0, 2)"
- ></span>
- <span
- class="crop-point point8"
- @mousedown="changeCropSize($event, true, true, 2, 2)"
- @touchstart="changeCropSize($event, true, true, 2, 2)"
- ></span>
- </span>
- </div>
- </div>
- </template>
- <script>
- import exifmin from "./exif-js-min";
- export default {
- data: function() {
- return {
- // 容器高宽
- w: 0,
- h: 0,
- // 图片缩放比例
- scale: 1,
- // 图片偏移x轴
- x: 0,
- // 图片偏移y轴
- y: 0,
- // 图片加载
- loading: true,
- // 图片真实宽度
- trueWidth: 0,
- // 图片真实高度
- trueHeight: 0,
- move: true,
- // 移动的x
- moveX: 0,
- // 移动的y
- moveY: 0,
- // 开启截图
- crop: false,
- // 正在截图
- cropping: false,
- // 裁剪框大小
- cropW: 0,
- cropH: 0,
- cropOldW: 0,
- cropOldH: 0,
- // 判断是否能够改变
- canChangeX: false,
- canChangeY: false,
- // 改变的基准点
- changeCropTypeX: 1,
- changeCropTypeY: 1,
- // 裁剪框的坐标轴
- cropX: 0,
- cropY: 0,
- cropChangeX: 0,
- cropChangeY: 0,
- cropOffsertX: 0,
- cropOffsertY: 0,
- // 支持的滚动事件
- support: "",
- // 移动端手指缩放
- touches: [],
- touchNow: false,
- // 图片旋转
- rotate: 0,
- isIos: false,
- orientation: 0,
- imgs: "",
- // 图片缩放系数
- coe: 0.2,
- // 是否正在多次缩放
- scaling: false,
- scalingSet: "",
- coeStatus: "",
- // 控制emit触发频率
- isCanShow: true
- };
- },
- props: {
- img: {
- type: [String, Blob, null, File],
- default: ""
- },
- // 输出图片压缩比
- outputSize: {
- type: Number,
- default: 1
- },
- outputType: {
- type: String,
- default: "jpeg"
- },
- info: {
- type: Boolean,
- default: true
- },
- // 是否开启滚轮放大缩小
- canScale: {
- type: Boolean,
- default: true
- },
- // 是否自成截图框
- autoCrop: {
- type: Boolean,
- default: false
- },
- autoCropWidth: {
- type: [Number, String],
- default: 0
- },
- autoCropHeight: {
- type: [Number, String],
- default: 0
- },
- // 是否开启固定宽高比
- fixed: {
- type: Boolean,
- default: false
- },
- // 宽高比 w/h
- fixedNumber: {
- type: Array,
- default: () => {
- return [1, 1];
- }
- },
- // 固定大小 禁止改变截图框大小
- fixedBox: {
- type: Boolean,
- default: false
- },
- // 输出截图是否缩放
- full: {
- type: Boolean,
- default: false
- },
- // 是否可以拖动图片
- canMove: {
- type: Boolean,
- default: true
- },
- // 是否可以拖动截图框
- canMoveBox: {
- type: Boolean,
- default: true
- },
- // 上传图片按照原始比例显示
- original: {
- type: Boolean,
- default: false
- },
- // 截图框能否超过图片
- centerBox: {
- type: Boolean,
- default: false
- },
- // 是否根据dpr输出高清图片
- high: {
- type: Boolean,
- default: true
- },
- // 截图框展示宽高类型
- infoTrue: {
- type: Boolean,
- default: false
- },
- // 可以压缩图片宽高 默认不超过200
- maxImgSize: {
- type: [Number, String],
- default: 2000
- },
- // 倍数 可渲染当前截图框的n倍 0 - 1000;
- enlarge: {
- type: [Number, String],
- default: 1
- },
- // 自动预览的固定宽度
- preW: {
- type: [Number, String],
- default: 0
- },
- /*
- 图片布局方式 mode 实现和css背景一样的效果
- contain 居中布局 默认不会缩放 保证图片在容器里面 mode: 'contain'
- cover 拉伸布局 填充整个容器 mode: 'cover'
- 如果仅有一个数值被给定,这个数值将作为宽度值大小,高度值将被设定为auto。 mode: '50px'
- 如果有两个数值被给定,第一个将作为宽度值大小,第二个作为高度值大小。 mode: '50px 60px'
- */
- mode: {
- type: String,
- default: "contain"
- },
- //限制最小区域,可传1以上的数字和字符串,限制长宽都是这么大
- // 也可以传数组[90,90]
- limitMinSize: {
- type: [Number, Array, String],
- default: () => {
- return 10;
- }
- },
- },
- computed: {
- cropInfo() {
- let obj = {};
- obj.top = this.cropOffsertY > 21 ? "-21px" : "0px";
- obj.width = this.cropW > 0 ? this.cropW : 0;
- obj.height = this.cropH > 0 ? this.cropH : 0;
- if (this.infoTrue) {
- let dpr = 1;
- if (this.high && !this.full) {
- dpr = window.devicePixelRatio;
- }
- if ((this.enlarge !== 1) & !this.full) {
- dpr = Math.abs(Number(this.enlarge));
- }
- obj.width = obj.width * dpr;
- obj.height = obj.height * dpr;
- if (this.full) {
- obj.width = obj.width / this.scale;
- obj.height = obj.height / this.scale;
- }
- }
- obj.width = obj.width.toFixed(0);
- obj.height = obj.height.toFixed(0);
- return obj;
- },
- isIE() {
- var userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
- const isIE = !!window.ActiveXObject || 'ActiveXObject' in window; //判断是否IE浏览器
- return isIE;
- },
- passive () {
- return this.isIE ? null : {
- passive: false
- }
- }
- },
- watch: {
- // 如果图片改变, 重新布局
- img() {
- // 当传入图片时, 读取图片信息同时展示
- this.checkedImg();
- },
- imgs(val) {
- if (val === "") {
- return;
- }
- this.reload();
- },
- cropW() {
- this.showPreview();
- },
- cropH() {
- this.showPreview();
- },
- cropOffsertX() {
- this.showPreview();
- },
- cropOffsertY() {
- this.showPreview();
- },
- scale(val, oldVal) {
- this.showPreview();
- },
- x() {
- this.showPreview();
- },
- y() {
- this.showPreview();
- },
- autoCrop(val) {
- if (val) {
- this.goAutoCrop();
- }
- },
- // 修改了自动截图框
- autoCropWidth() {
- if (this.autoCrop) {
- this.goAutoCrop();
- }
- },
- autoCropHeight() {
- if (this.autoCrop) {
- this.goAutoCrop();
- }
- },
- mode() {
- this.checkedImg();
- },
- rotate() {
- this.showPreview();
- if (this.autoCrop) {
- this.goAutoCrop(this.cropW, this.cropH);
- } else {
- if (this.cropW > 0 || this.cropH > 0) {
- this.goAutoCrop(this.cropW, this.cropH);
- }
- }
- }
- },
- methods: {
- getVersion (name) {
- var arr = navigator.userAgent.split(' ');
- var chromeVersion = '';
- let result = 0;
- const reg = new RegExp(name, 'i')
- for(var i=0;i < arr.length;i++){
- if(reg.test(arr[i]))
- chromeVersion = arr[i]
- }
- if(chromeVersion){
- result = chromeVersion.split('/')[1].split('.');
- } else {
- result = ['0', '0', '0'];
- }
- return result
- },
- checkOrientationImage(img, orientation, width, height) {
- // 如果是 chrome内核版本在81 safari 在 605 以上不处理图片旋转
- // alert(navigator.userAgent)
- if (this.getVersion('chrome')[0] >= 81) {
- orientation = -1
- } else {
- if (this.getVersion('safari')[0] >= 605 ) {
- const safariVersion = this.getVersion('version')
- if (safariVersion[0] > 13 && safariVersion[1] > 1) {
- orientation = -1
- }
- } else {
- // 判断 ios 版本进行处理
- // 针对 ios 版本大于 13.4的系统不做图片旋转
- const isIos = navigator.userAgent.toLowerCase().match(/cpu iphone os (.*?) like mac os/)
- if (isIos) {
- let version = isIos[1]
- version = version.split('_')
- if (version[0] > 13 || (version[0] >= 13 && version[1] >= 4)) {
- orientation = -1
- }
- }
- }
- }
-
- // alert(`当前处理的orientation${orientation}`)
- let canvas = document.createElement("canvas");
- let ctx = canvas.getContext("2d");
- ctx.save();
-
- switch (orientation) {
- case 2:
- canvas.width = width;
- canvas.height = height;
- // horizontal flip
- ctx.translate(width, 0);
- ctx.scale(-1, 1);
- break;
- case 3:
- canvas.width = width;
- canvas.height = height;
- //180 graus
- ctx.translate(width / 2, height / 2);
- ctx.rotate((180 * Math.PI) / 180);
- ctx.translate(-width / 2, -height / 2);
- break;
- case 4:
- canvas.width = width;
- canvas.height = height;
- // vertical flip
- ctx.translate(0, height);
- ctx.scale(1, -1);
- break;
- case 5:
- // vertical flip + 90 rotate right
- canvas.height = width;
- canvas.width = height;
- ctx.rotate(0.5 * Math.PI);
- ctx.scale(1, -1);
- break;
- case 6:
- canvas.width = height;
- canvas.height = width;
- //90 graus
- ctx.translate(height / 2, width / 2);
- ctx.rotate((90 * Math.PI) / 180);
- ctx.translate(-width / 2, -height / 2);
- break;
- case 7:
- // horizontal flip + 90 rotate right
- canvas.height = width;
- canvas.width = height;
- ctx.rotate(0.5 * Math.PI);
- ctx.translate(width, -height);
- ctx.scale(-1, 1);
- break;
- case 8:
- canvas.height = width;
- canvas.width = height;
- //-90 graus
- ctx.translate(height / 2, width / 2);
- ctx.rotate((-90 * Math.PI) / 180);
- ctx.translate(-width / 2, -height / 2);
- break;
- default:
- canvas.width = width;
- canvas.height = height;
- }
- ctx.drawImage(img, 0, 0, width, height);
- ctx.restore();
- canvas.toBlob(
- blob => {
- let data = URL.createObjectURL(blob);
- URL.revokeObjectURL(this.imgs)
- this.imgs = data;
- },
- "image/" + this.outputType,
- 1
- );
- },
- // checkout img
- checkedImg() {
- if (this.img === null || this.img === '') {
- this.imgs = ''
- this.clearCrop()
- return
- }
- this.loading = true;
- this.scale = 1;
- this.rotate = 0;
- this.clearCrop();
- let img = new Image();
- img.onload = () => {
- if (this.img === "") {
- this.$emit("imgLoad", "error");
- this.$emit("img-load", "error");
- return false;
- }
- let width = img.width;
- let height = img.height;
- exifmin.getData(img).then(data => {
- this.orientation = data.orientation || 1;
- let max = this.maxImgSize;
- if (!this.orientation && (width < max) & (height < max)) {
- this.imgs = this.img;
- return;
- }
- if (width > max) {
- height = (height / width) * max;
- width = max;
- }
- if (height > max) {
- width = (width / height) * max;
- height = max;
- }
- this.checkOrientationImage(img, this.orientation, width, height);
- });
- };
- img.onerror = () => {
- this.$emit("imgLoad", "error");
- this.$emit("img-load", "error");
- };
- // 判断如果不是base64图片 再添加crossOrigin属性,否则会导致iOS低版本(10.2)无法显示图片
- if (this.img.substr(0, 4) !== "data") {
- img.crossOrigin = "";
- }
- if (this.isIE) {
- var xhr = new XMLHttpRequest();
- xhr.onload = function() {
- var url = URL.createObjectURL(this.response);
- img.src = url;
- };
- xhr.open("GET", this.img, true);
- xhr.responseType = "blob";
- xhr.send();
- } else {
- img.src = this.img;
- }
- },
- // 当按下鼠标键
- startMove(e) {
- e.preventDefault();
- // 如果move 为true 表示当前可以拖动
- if (this.move && !this.crop) {
- if (!this.canMove) {
- return false;
- }
- // 开始移动
- this.moveX = (e.clientX ? e.clientX : e.touches[0].clientX) - this.x;
- this.moveY = (e.clientY ? e.clientY : e.touches[0].clientY) - this.y;
- if (e.touches) {
- window.addEventListener("touchmove", this.moveImg);
- window.addEventListener("touchend", this.leaveImg);
- if (e.touches.length == 2) {
- // 记录手指刚刚放上去
- this.touches = e.touches;
- window.addEventListener("touchmove", this.touchScale);
- window.addEventListener("touchend", this.cancelTouchScale);
- }
- } else {
- window.addEventListener("mousemove", this.moveImg);
- window.addEventListener("mouseup", this.leaveImg);
- }
- // 触发图片移动事件
- this.$emit("imgMoving", {
- moving: true,
- axis: this.getImgAxis()
- });
- this.$emit("img-moving", {
- moving: true,
- axis: this.getImgAxis()
- });
- } else {
- // 截图ing
- this.cropping = true;
- // 绑定截图事件
- window.addEventListener("mousemove", this.createCrop);
- window.addEventListener("mouseup", this.endCrop);
- window.addEventListener("touchmove", this.createCrop);
- window.addEventListener("touchend", this.endCrop);
- this.cropOffsertX = e.offsetX
- ? e.offsetX
- : e.touches[0].pageX - this.$refs.cropper.offsetLeft;
- this.cropOffsertY = e.offsetY
- ? e.offsetY
- : e.touches[0].pageY - this.$refs.cropper.offsetTop;
- this.cropX = e.clientX ? e.clientX : e.touches[0].clientX;
- this.cropY = e.clientY ? e.clientY : e.touches[0].clientY;
- this.cropChangeX = this.cropOffsertX;
- this.cropChangeY = this.cropOffsertY;
- this.cropW = 0;
- this.cropH = 0;
- }
- },
- // 移动端缩放
- touchScale(e) {
- e.preventDefault();
- let scale = this.scale;
- // 记录变化量
- // 第一根手指
- var oldTouch1 = {
- x: this.touches[0].clientX,
- y: this.touches[0].clientY
- };
- var newTouch1 = {
- x: e.touches[0].clientX,
- y: e.touches[0].clientY
- };
- // 第二根手指
- var oldTouch2 = {
- x: this.touches[1].clientX,
- y: this.touches[1].clientY
- };
- var newTouch2 = {
- x: e.touches[1].clientX,
- y: e.touches[1].clientY
- };
- var oldL = Math.sqrt(
- Math.pow(oldTouch1.x - oldTouch2.x, 2) +
- Math.pow(oldTouch1.y - oldTouch2.y, 2)
- );
- var newL = Math.sqrt(
- Math.pow(newTouch1.x - newTouch2.x, 2) +
- Math.pow(newTouch1.y - newTouch2.y, 2)
- );
- var cha = newL - oldL;
- // 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
- // 1px - 0.2
- var coe = 1;
- coe =
- coe / this.trueWidth > coe / this.trueHeight
- ? coe / this.trueHeight
- : coe / this.trueWidth;
- coe = coe > 0.1 ? 0.1 : coe;
- var num = coe * cha;
- if (!this.touchNow) {
- this.touchNow = true;
- if (cha > 0) {
- scale += Math.abs(num);
- } else if (cha < 0) {
- scale > Math.abs(num) ? (scale -= Math.abs(num)) : scale;
- }
- this.touches = e.touches;
- setTimeout(() => {
- this.touchNow = false;
- }, 8);
- if (!this.checkoutImgAxis(this.x, this.y, scale)) {
- return false;
- }
- this.scale = scale;
- }
- },
- cancelTouchScale(e) {
- window.removeEventListener("touchmove", this.touchScale);
- },
- // 移动图片
- moveImg(e) {
- e.preventDefault();
- if (e.touches && e.touches.length === 2) {
- this.touches = e.touches;
- window.addEventListener("touchmove", this.touchScale);
- window.addEventListener("touchend", this.cancelTouchScale);
- window.removeEventListener("touchmove", this.moveImg);
- return false;
- }
- let nowX = e.clientX ? e.clientX : e.touches[0].clientX;
- let nowY = e.clientY ? e.clientY : e.touches[0].clientY;
- let changeX, changeY;
- changeX = nowX - this.moveX;
- changeY = nowY - this.moveY;
- this.$nextTick(() => {
- if (this.centerBox) {
- let axis = this.getImgAxis(changeX, changeY, this.scale);
- let cropAxis = this.getCropAxis();
- let imgW = this.trueHeight * this.scale;
- let imgH = this.trueWidth * this.scale;
- let maxLeft, maxTop, maxRight, maxBottom;
- switch (this.rotate) {
- case 1:
- case -1:
- case 3:
- case -3:
- maxLeft =
- this.cropOffsertX -
- (this.trueWidth * (1 - this.scale)) / 2 +
- (imgW - imgH) / 2;
- maxTop =
- this.cropOffsertY -
- (this.trueHeight * (1 - this.scale)) / 2 +
- (imgH - imgW) / 2;
- maxRight = maxLeft - imgW + this.cropW;
- maxBottom = maxTop - imgH + this.cropH;
- break;
- default:
- maxLeft =
- this.cropOffsertX - (this.trueWidth * (1 - this.scale)) / 2;
- maxTop =
- this.cropOffsertY - (this.trueHeight * (1 - this.scale)) / 2;
- maxRight = maxLeft - imgH + this.cropW;
- maxBottom = maxTop - imgW + this.cropH;
- break;
- }
- // 图片左边 图片不能超过截图框
- if (axis.x1 >= cropAxis.x1) {
- changeX = maxLeft;
- }
- // 图片上边 图片不能超过截图框
- if (axis.y1 >= cropAxis.y1) {
- changeY = maxTop;
- }
- // 图片右边
- if (axis.x2 <= cropAxis.x2) {
- changeX = maxRight;
- }
- // 图片下边
- if (axis.y2 <= cropAxis.y2) {
- changeY = maxBottom;
- }
- }
- this.x = changeX;
- this.y = changeY;
- // 触发图片移动事件
- this.$emit("imgMoving", {
- moving: true,
- axis: this.getImgAxis()
- });
- this.$emit("img-moving", {
- moving: true,
- axis: this.getImgAxis()
- });
- });
- },
- // 移动图片结束
- leaveImg(e) {
- window.removeEventListener("mousemove", this.moveImg);
- window.removeEventListener("touchmove", this.moveImg);
- window.removeEventListener("mouseup", this.leaveImg);
- window.removeEventListener("touchend", this.leaveImg);
- // 触发图片移动事件
- this.$emit("imgMoving", {
- moving: false,
- axis: this.getImgAxis()
- });
- this.$emit("img-moving", {
- moving: false,
- axis: this.getImgAxis()
- });
- },
- // 缩放图片
- scaleImg() {
- if (this.canScale) {
- window.addEventListener(this.support, this.changeSize, this.passive);
- }
- },
- // 移出框
- cancelScale() {
- if (this.canScale) {
- window.removeEventListener(this.support, this.changeSize);
- }
- },
- // 改变大小函数
- changeSize(e) {
- e.preventDefault();
- let scale = this.scale;
- var change = e.deltaY || e.wheelDelta;
- // 根据图片本身大小 决定每次改变大小的系数, 图片越大系数越小
- var isFirefox = navigator.userAgent.indexOf("Firefox");
- change = isFirefox > 0 ? change * 30 : change;
- // 修复ie的滚动缩放
- if (this.isIE) {
- change = -change;
- }
- // 1px - 0.2
- var coe = this.coe;
- coe =
- coe / this.trueWidth > coe / this.trueHeight
- ? coe / this.trueHeight
- : coe / this.trueWidth;
- var num = coe * change;
- num < 0
- ? (scale += Math.abs(num))
- : scale > Math.abs(num)
- ? (scale -= Math.abs(num))
- : scale;
- // 延迟0.1s 每次放大大或者缩小的范围
- let status = num < 0 ? "add" : "reduce";
- if (status !== this.coeStatus) {
- this.coeStatus = status;
- this.coe = 0.2;
- }
- if (!this.scaling) {
- this.scalingSet = setTimeout(() => {
- this.scaling = false;
- this.coe = this.coe += 0.01;
- }, 50);
- }
- this.scaling = true;
- if (!this.checkoutImgAxis(this.x, this.y, scale)) {
- return false;
- }
- this.scale = scale;
- },
- // 修改图片大小函数
- changeScale(num) {
- let scale = this.scale;
- num = num || 1;
- var coe = 20;
- coe =
- coe / this.trueWidth > coe / this.trueHeight
- ? coe / this.trueHeight
- : coe / this.trueWidth;
- num = num * coe;
- num > 0
- ? (scale += Math.abs(num))
- : scale > Math.abs(num)
- ? (scale -= Math.abs(num))
- : scale;
- if (!this.checkoutImgAxis(this.x, this.y, scale)) {
- return false;
- }
- this.scale = scale;
- },
- // 创建截图框
- createCrop(e) {
- e.preventDefault();
- // 移动生成大小
- var nowX = e.clientX ? e.clientX : e.touches ? e.touches[0].clientX : 0;
- var nowY = e.clientY ? e.clientY : e.touches ? e.touches[0].clientY : 0;
- this.$nextTick(() => {
- var fw = nowX - this.cropX;
- var fh = nowY - this.cropY;
- if (fw > 0) {
- this.cropW =
- fw + this.cropChangeX > this.w ? this.w - this.cropChangeX : fw;
- this.cropOffsertX = this.cropChangeX;
- } else {
- this.cropW =
- this.w - this.cropChangeX + Math.abs(fw) > this.w
- ? this.cropChangeX
- : Math.abs(fw);
- this.cropOffsertX =
- this.cropChangeX + fw > 0 ? this.cropChangeX + fw : 0;
- }
- if (!this.fixed) {
- if (fh > 0) {
- this.cropH =
- fh + this.cropChangeY > this.h ? this.h - this.cropChangeY : fh;
- this.cropOffsertY = this.cropChangeY;
- } else {
- this.cropH =
- this.h - this.cropChangeY + Math.abs(fh) > this.h
- ? this.cropChangeY
- : Math.abs(fh);
- this.cropOffsertY =
- this.cropChangeY + fh > 0 ? this.cropChangeY + fh : 0;
- }
- } else {
- var fixedHeight =
- (this.cropW / this.fixedNumber[0]) * this.fixedNumber[1];
- if (fixedHeight + this.cropOffsertY > this.h) {
- this.cropH = this.h - this.cropOffsertY;
- this.cropW =
- (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
- if (fw > 0) {
- this.cropOffsertX = this.cropChangeX;
- } else {
- this.cropOffsertX = this.cropChangeX - this.cropW;
- }
- } else {
- this.cropH = fixedHeight;
- }
- this.cropOffsertY = this.cropOffsertY;
- }
- });
- },
- // 改变截图框大小
- changeCropSize(e, w, h, typeW, typeH) {
- e.preventDefault();
- window.addEventListener("mousemove", this.changeCropNow);
- window.addEventListener("mouseup", this.changeCropEnd);
- window.addEventListener("touchmove", this.changeCropNow);
- window.addEventListener("touchend", this.changeCropEnd);
- this.canChangeX = w;
- this.canChangeY = h;
- this.changeCropTypeX = typeW;
- this.changeCropTypeY = typeH;
- this.cropX = e.clientX ? e.clientX : e.touches[0].clientX;
- this.cropY = e.clientY ? e.clientY : e.touches[0].clientY;
- this.cropOldW = this.cropW;
- this.cropOldH = this.cropH;
- this.cropChangeX = this.cropOffsertX;
- this.cropChangeY = this.cropOffsertY;
- if (this.fixed) {
- if (this.canChangeX && this.canChangeY) {
- this.canChangeY = 0;
- }
- }
- this.$emit('change-crop-size', {
- width: this.cropW,
- height: this.cropH
- })
- },
- // 正在改变
- changeCropNow(e) {
- e.preventDefault();
- var nowX = e.clientX ? e.clientX : e.touches ? e.touches[0].clientX : 0;
- var nowY = e.clientY ? e.clientY : e.touches ? e.touches[0].clientY : 0;
- // 容器的宽高
- let wrapperW = this.w;
- let wrapperH = this.h;
- // 不能超过的坐标轴
- let minX = 0;
- let minY = 0;
- if (this.centerBox) {
- let axis = this.getImgAxis();
- let imgW = axis.x2;
- let imgH = axis.y2;
- minX = axis.x1 > 0 ? axis.x1 : 0;
- minY = axis.y1 > 0 ? axis.y1 : 0;
- if (wrapperW > imgW) {
- wrapperW = imgW;
- }
- if (wrapperH > imgH) {
- wrapperH = imgH;
- }
- }
- this.$nextTick(() => {
- var fw = nowX - this.cropX;
- var fh = nowY - this.cropY;
- if (this.canChangeX) {
- if (this.changeCropTypeX === 1) {
- if (this.cropOldW - fw > 0) {
- this.cropW =
- wrapperW - this.cropChangeX - fw <= wrapperW - minX
- ? this.cropOldW - fw
- : this.cropOldW + this.cropChangeX - minX;
- this.cropOffsertX =
- wrapperW - this.cropChangeX - fw <= wrapperW - minX
- ? this.cropChangeX + fw
- : minX;
- } else {
- this.cropW =
- Math.abs(fw) + this.cropChangeX <= wrapperW
- ? Math.abs(fw) - this.cropOldW
- : wrapperW - this.cropOldW - this.cropChangeX;
- this.cropOffsertX = this.cropChangeX + this.cropOldW;
- }
- } else if (this.changeCropTypeX === 2) {
- if (this.cropOldW + fw > 0) {
- this.cropW =
- this.cropOldW + fw + this.cropOffsertX <= wrapperW
- ? this.cropOldW + fw
- : wrapperW - this.cropOffsertX;
- this.cropOffsertX = this.cropChangeX;
- } else {
- // 右侧坐标抽 超过左侧
- this.cropW =
- wrapperW - this.cropChangeX + Math.abs(fw + this.cropOldW) <=
- wrapperW - minX
- ? Math.abs(fw + this.cropOldW)
- : this.cropChangeX - minX;
- this.cropOffsertX =
- wrapperW - this.cropChangeX + Math.abs(fw + this.cropOldW) <=
- wrapperW - minX
- ? this.cropChangeX - Math.abs(fw + this.cropOldW)
- : minX;
- }
- }
- }
- if (this.canChangeY) {
- if (this.changeCropTypeY === 1) {
- if (this.cropOldH - fh > 0) {
- this.cropH =
- wrapperH - this.cropChangeY - fh <= wrapperH - minY
- ? this.cropOldH - fh
- : this.cropOldH + this.cropChangeY - minY;
- this.cropOffsertY =
- wrapperH - this.cropChangeY - fh <= wrapperH - minY
- ? this.cropChangeY + fh
- : minY;
- } else {
- this.cropH =
- Math.abs(fh) + this.cropChangeY <= wrapperH
- ? Math.abs(fh) - this.cropOldH
- : wrapperH - this.cropOldH - this.cropChangeY;
- this.cropOffsertY = this.cropChangeY + this.cropOldH;
- }
- } else if (this.changeCropTypeY === 2) {
- if (this.cropOldH + fh > 0) {
- this.cropH =
- this.cropOldH + fh + this.cropOffsertY <= wrapperH
- ? this.cropOldH + fh
- : wrapperH - this.cropOffsertY;
- this.cropOffsertY = this.cropChangeY;
- } else {
- this.cropH =
- wrapperH - this.cropChangeY + Math.abs(fh + this.cropOldH) <=
- wrapperH - minY
- ? Math.abs(fh + this.cropOldH)
- : this.cropChangeY - minY;
- this.cropOffsertY =
- wrapperH - this.cropChangeY + Math.abs(fh + this.cropOldH) <=
- wrapperH - minY
- ? this.cropChangeY - Math.abs(fh + this.cropOldH)
- : minY;
- }
- }
- }
- if (this.canChangeX && this.fixed) {
- var fixedHeight =
- (this.cropW / this.fixedNumber[0]) * this.fixedNumber[1];
- if (fixedHeight + this.cropOffsertY > wrapperH) {
- this.cropH = wrapperH - this.cropOffsertY;
- this.cropW =
- (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
- } else {
- this.cropH = fixedHeight;
- }
- }
- if (this.canChangeY && this.fixed) {
- var fixedWidth =
- (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
- if (fixedWidth + this.cropOffsertX > wrapperW) {
- this.cropW = wrapperW - this.cropOffsertX;
- this.cropH =
- (this.cropW / this.fixedNumber[0]) * this.fixedNumber[1];
- } else {
- this.cropW = fixedWidth;
- }
- }
- });
- },
- checkCropLimitSize () {
- let { cropW, cropH, limitMinSize } = this;
- let limitMinNum = new Array;
- if (!Array.isArray[limitMinSize]) {
- limitMinNum = [limitMinSize, limitMinSize]
- } else {
- limitMinNum = limitMinSize
- }
-
- //限制最小宽度和高度
- cropW = parseFloat(limitMinNum[0])
- cropH = parseFloat(limitMinNum[1])
- return [cropW, cropH]
- },
- // 结束改变
- changeCropEnd(e) {
- window.removeEventListener("mousemove", this.changeCropNow);
- window.removeEventListener("mouseup", this.changeCropEnd);
- window.removeEventListener("touchmove", this.changeCropNow);
- window.removeEventListener("touchend", this.changeCropEnd);
- },
- // 创建完成
- endCrop() {
- if (this.cropW === 0 && this.cropH === 0) {
- this.cropping = false;
- }
- window.removeEventListener("mousemove", this.createCrop);
- window.removeEventListener("mouseup", this.endCrop);
- window.removeEventListener("touchmove", this.createCrop);
- window.removeEventListener("touchend", this.endCrop);
- },
- // 开始截图
- startCrop() {
- this.crop = true;
- },
- // 停止截图
- stopCrop() {
- this.crop = false;
- },
- // 清除截图
- clearCrop() {
- this.cropping = false;
- this.cropW = 0;
- this.cropH = 0;
- },
- // 截图移动
- cropMove(e) {
- e.preventDefault();
- if (!this.canMoveBox) {
- this.crop = false;
- this.startMove(e);
- return false;
- }
- if (e.touches && e.touches.length === 2) {
- this.crop = false;
- this.startMove(e);
- this.leaveCrop();
- return false;
- }
- window.addEventListener("mousemove", this.moveCrop);
- window.addEventListener("mouseup", this.leaveCrop);
- window.addEventListener("touchmove", this.moveCrop);
- window.addEventListener("touchend", this.leaveCrop);
- let x = e.clientX ? e.clientX : e.touches[0].clientX;
- let y = e.clientY ? e.clientY : e.touches[0].clientY;
- let newX, newY;
- newX = x - this.cropOffsertX;
- newY = y - this.cropOffsertY;
- this.cropX = newX;
- this.cropY = newY;
- // 触发截图框移动事件
- this.$emit("cropMoving", {
- moving: true,
- axis: this.getCropAxis()
- });
- this.$emit("crop-moving", {
- moving: true,
- axis: this.getCropAxis()
- });
- },
- moveCrop(e, isMove) {
- let nowX = 0;
- let nowY = 0;
- if (e) {
- e.preventDefault();
- nowX = e.clientX ? e.clientX : e.touches[0].clientX;
- nowY = e.clientY ? e.clientY : e.touches[0].clientY;
- }
- this.$nextTick(() => {
- let cx, cy;
- let fw = nowX - this.cropX;
- let fh = nowY - this.cropY;
- if (isMove) {
- fw = this.cropOffsertX;
- fh = this.cropOffsertY;
- }
- // 不能超过外层容器
- if (fw <= 0) {
- cx = 0;
- } else if (fw + this.cropW > this.w) {
- cx = this.w - this.cropW;
- } else {
- cx = fw;
- }
- if (fh <= 0) {
- cy = 0;
- } else if (fh + this.cropH > this.h) {
- cy = this.h - this.cropH;
- } else {
- cy = fh;
- }
- // 不能超过图片
- if (this.centerBox) {
- let axis = this.getImgAxis();
- // 横坐标判断
- if (cx <= axis.x1) {
- cx = axis.x1;
- }
- if (cx + this.cropW > axis.x2) {
- cx = axis.x2 - this.cropW;
- }
- // 纵坐标纵轴
- if (cy <= axis.y1) {
- cy = axis.y1;
- }
- if (cy + this.cropH > axis.y2) {
- cy = axis.y2 - this.cropH;
- }
- }
- this.cropOffsertX = cx;
- this.cropOffsertY = cy;
- // 触发截图框移动事件
- this.$emit("cropMoving", {
- moving: true,
- axis: this.getCropAxis()
- });
- this.$emit("crop-moving", {
- moving: true,
- axis: this.getCropAxis()
- });
- });
- },
- // 算出不同场景下面 图片相对于外层容器的坐标轴
- getImgAxis(x, y, scale) {
- x = x || this.x;
- y = y || this.y;
- scale = scale || this.scale;
- // 如果设置了截图框在图片内, 那么限制截图框不能超过图片的坐标
- // 图片的坐标
- let obj = {
- x1: 0,
- x2: 0,
- y1: 0,
- y2: 0
- };
- let imgW = this.trueWidth * scale;
- let imgH = this.trueHeight * scale;
- switch (this.rotate) {
- case 0:
- obj.x1 = x + (this.trueWidth * (1 - scale)) / 2;
- obj.x2 = obj.x1 + this.trueWidth * scale;
- obj.y1 = y + (this.trueHeight * (1 - scale)) / 2;
- obj.y2 = obj.y1 + this.trueHeight * scale;
- break;
- case 1:
- case -1:
- case 3:
- case -3:
- obj.x1 = x + (this.trueWidth * (1 - scale)) / 2 + (imgW - imgH) / 2;
- obj.x2 = obj.x1 + this.trueHeight * scale;
- obj.y1 = y + (this.trueHeight * (1 - scale)) / 2 + (imgH - imgW) / 2;
- obj.y2 = obj.y1 + this.trueWidth * scale;
- break;
- default:
- obj.x1 = x + (this.trueWidth * (1 - scale)) / 2;
- obj.x2 = obj.x1 + this.trueWidth * scale;
- obj.y1 = y + (this.trueHeight * (1 - scale)) / 2;
- obj.y2 = obj.y1 + this.trueHeight * scale;
- break;
- }
- return obj;
- },
- // 获取截图框的坐标轴
- getCropAxis() {
- let obj = {
- x1: 0,
- x2: 0,
- y1: 0,
- y2: 0
- };
- obj.x1 = this.cropOffsertX;
- obj.x2 = obj.x1 + this.cropW;
- obj.y1 = this.cropOffsertY;
- obj.y2 = obj.y1 + this.cropH;
- return obj;
- },
- leaveCrop(e) {
- window.removeEventListener("mousemove", this.moveCrop);
- window.removeEventListener("mouseup", this.leaveCrop);
- window.removeEventListener("touchmove", this.moveCrop);
- window.removeEventListener("touchend", this.leaveCrop);
- // 触发截图框移动事件
- this.$emit("cropMoving", {
- moving: false,
- axis: this.getCropAxis()
- });
- this.$emit("crop-moving", {
- moving: false,
- axis: this.getCropAxis()
- });
- },
- getCropChecked(cb) {
- let canvas = document.createElement("canvas");
- let img = new Image();
- let rotate = this.rotate;
- let trueWidth = this.trueWidth;
- let trueHeight = this.trueHeight;
- let cropOffsertX = this.cropOffsertX;
- let cropOffsertY = this.cropOffsertY;
- img.onload = () => {
- if (this.cropW !== 0) {
- let ctx = canvas.getContext("2d");
- let dpr = 1;
- if (this.high & !this.full) {
- dpr = window.devicePixelRatio;
- }
- if ((this.enlarge !== 1) & !this.full) {
- dpr = Math.abs(Number(this.enlarge));
- }
- let width = this.cropW * dpr;
- let height = this.cropH * dpr;
- let imgW = trueWidth * this.scale * dpr;
- let imgH = trueHeight * this.scale * dpr;
- // 图片x轴偏移
- let dx =
- (this.x - cropOffsertX + (this.trueWidth * (1 - this.scale)) / 2) *
- dpr;
- // 图片y轴偏移
- let dy =
- (this.y - cropOffsertY + (this.trueHeight * (1 - this.scale)) / 2) *
- dpr;
- //保存状态
- setCanvasSize(width, height);
- ctx.save();
- switch (rotate) {
- case 0:
- if (!this.full) {
- ctx.drawImage(img, dx, dy, imgW, imgH);
- } else {
- // 输出原图比例截图
- setCanvasSize(width / this.scale, height / this.scale);
- ctx.drawImage(
- img,
- dx / this.scale,
- dy / this.scale,
- imgW / this.scale,
- imgH / this.scale
- );
- }
- break;
- case 1:
- case -3:
- if (!this.full) {
- // 换算图片旋转后的坐标弥补
- dx = dx + (imgW - imgH) / 2;
- dy = dy + (imgH - imgW) / 2;
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(img, dy, -dx - imgH, imgW, imgH);
- } else {
- setCanvasSize(width / this.scale, height / this.scale);
- // 换算图片旋转后的坐标弥补
- dx =
- dx / this.scale + (imgW / this.scale - imgH / this.scale) / 2;
- dy =
- dy / this.scale + (imgH / this.scale - imgW / this.scale) / 2;
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(
- img,
- dy,
- -dx - imgH / this.scale,
- imgW / this.scale,
- imgH / this.scale
- );
- }
- break;
- case 2:
- case -2:
- if (!this.full) {
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(img, -dx - imgW, -dy - imgH, imgW, imgH);
- } else {
- setCanvasSize(width / this.scale, height / this.scale);
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- dx = dx / this.scale;
- dy = dy / this.scale;
- ctx.drawImage(
- img,
- -dx - imgW / this.scale,
- -dy - imgH / this.scale,
- imgW / this.scale,
- imgH / this.scale
- );
- }
- break;
- case 3:
- case -1:
- if (!this.full) {
- // 换算图片旋转后的坐标弥补
- dx = dx + (imgW - imgH) / 2;
- dy = dy + (imgH - imgW) / 2;
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(img, -dy - imgW, dx, imgW, imgH);
- } else {
- setCanvasSize(width / this.scale, height / this.scale);
- // 换算图片旋转后的坐标弥补
- dx =
- dx / this.scale + (imgW / this.scale - imgH / this.scale) / 2;
- dy =
- dy / this.scale + (imgH / this.scale - imgW / this.scale) / 2;
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(
- img,
- -dy - imgW / this.scale,
- dx,
- imgW / this.scale,
- imgH / this.scale
- );
- }
- break;
- default:
- if (!this.full) {
- ctx.drawImage(img, dx, dy, imgW, imgH);
- } else {
- // 输出原图比例截图
- setCanvasSize(width / this.scale, height / this.scale);
- ctx.drawImage(
- img,
- dx / this.scale,
- dy / this.scale,
- imgW / this.scale,
- imgH / this.scale
- );
- }
- }
- ctx.restore();
- } else {
- let width = trueWidth * this.scale;
- let height = trueHeight * this.scale;
- let ctx = canvas.getContext("2d");
- ctx.save();
- switch (rotate) {
- case 0:
- setCanvasSize(width, height);
- ctx.drawImage(img, 0, 0, width, height);
- break;
- case 1:
- case -3:
- // 旋转90度 或者-270度 宽度和高度对调
- setCanvasSize(height, width);
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(img, 0, -height, width, height);
- break;
- case 2:
- case -2:
- setCanvasSize(width, height);
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(img, -width, -height, width, height);
- break;
- case 3:
- case -1:
- setCanvasSize(height, width);
- ctx.rotate((rotate * 90 * Math.PI) / 180);
- ctx.drawImage(img, -width, 0, width, height);
- break;
- default:
- setCanvasSize(width, height);
- ctx.drawImage(img, 0, 0, width, height);
- }
- ctx.restore();
- }
- cb(canvas);
- };
- // 判断图片是否是base64
- var s = this.img.substr(0, 4);
- if (s !== "data") {
- img.crossOrigin = "Anonymous";
- }
- img.src = this.imgs;
- function setCanvasSize(width, height) {
- canvas.width = Math.round(width);
- canvas.height = Math.round(height);
- }
- },
- // 获取转换成base64 的图片信息
- getCropData(cb) {
- this.getCropChecked(data => {
- cb(data.toDataURL("image/" + this.outputType, this.outputSize));
- });
- },
- //canvas获取为blob对象
- getCropBlob(cb) {
- this.getCropChecked(data => {
- data.toBlob(
- blob => cb(blob),
- "image/" + this.outputType,
- this.outputSize
- );
- });
- },
- // 自动预览函数
- showPreview() {
- // 优化不要多次触发
- if (this.isCanShow) {
- this.isCanShow = false;
- setTimeout(() => {
- this.isCanShow = true;
- }, 16);
- } else {
- return false;
- }
- let w = this.cropW;
- let h = this.cropH;
- let scale = this.scale;
- var obj = {};
- obj.div = {
- width: `${w}px`,
- height: `${h}px`
- };
- let transformX = (this.x - this.cropOffsertX) / scale;
- let transformY = (this.y - this.cropOffsertY) / scale;
- let transformZ = 0;
- obj.w = w;
- obj.h = h;
- obj.url = this.imgs;
- obj.img = {
- width: `${this.trueWidth}px`,
- height: `${this.trueHeight}px`,
- transform: `scale(${scale})translate3d(${transformX}px, ${transformY}px, ${transformZ}px)rotateZ(${this
- .rotate * 90}deg)`
- };
- obj.html = `
- <div class="show-preview" style="width: ${obj.w}px; height: ${
- obj.h
- }px,; overflow: hidden">
- <div style="width: ${w}px; height: ${h}px">
- <img src=${obj.url} style="width: ${this.trueWidth}px; height: ${
- this.trueHeight
- }px; transform:
- scale(${scale})translate3d(${transformX}px, ${transformY}px, ${transformZ}px)rotateZ(${this
- .rotate * 90}deg)">
- </div>
- </div>`;
- this.$emit("realTime", obj);
- this.$emit("real-time", obj);
- },
- // reload 图片布局函数
- reload() {
- let img = new Image();
- img.onload = () => {
- // 读取图片的信息原始信息, 解析是否需要旋转
- // 读取图片的旋转信息
- // 得到外层容器的宽度高度
- this.w = parseFloat(window.getComputedStyle(this.$refs.cropper).width);
- this.h = parseFloat(window.getComputedStyle(this.$refs.cropper).height);
- // 存入图片真实高度
- this.trueWidth = img.width;
- this.trueHeight = img.height;
- // 判断是否需要压缩大图
- if (!this.original) {
- // 判断布局方式 mode
- this.scale = this.checkedMode();
- } else {
- this.scale = 1;
- }
- this.$nextTick(() => {
- this.x =
- -(this.trueWidth - this.trueWidth * this.scale) / 2 +
- (this.w - this.trueWidth * this.scale) / 2;
- this.y =
- -(this.trueHeight - this.trueHeight * this.scale) / 2 +
- (this.h - this.trueHeight * this.scale) / 2;
- this.loading = false;
- // // 获取是否开启了自动截图
- if (this.autoCrop) {
- this.goAutoCrop();
- }
- // 图片加载成功的回调
- this.$emit("img-load", "success");
- this.$emit("imgLoad", "success");
- setTimeout(() => {
- this.showPreview();
- }, 20);
- });
- };
- img.onerror = () => {
- this.$emit("imgLoad", "error");
- this.$emit("img-load", "error");
- };
- img.src = this.imgs;
- },
- // 背景布局的函数
- checkedMode() {
- let scale = 1;
- // 通过字符串分割
- let imgW = this.trueWidth;
- let imgH = this.trueHeight;
- const arr = this.mode.split(" ");
- switch (arr[0]) {
- case "contain":
- if (this.trueWidth > this.w) {
- // 如果图片宽度大于容器宽度
- scale = this.w / this.trueWidth;
- }
- if (this.trueHeight * scale > this.h) {
- scale = this.h / this.trueHeight;
- }
- break;
- case "cover":
- // 扩展布局 默认填充满整个容器
- // 图片宽度大于容器
- imgW = this.w;
- scale = imgW / this.trueWidth;
- imgH = imgH * scale;
- // 如果扩展之后高度小于容器的外层高度 继续扩展高度
- if (imgH < this.h) {
- imgH = this.h;
- scale = imgH / this.trueHeight;
- }
- break;
- default:
- try {
- let str = arr[0];
- if (str.search("px") !== -1) {
- str = str.replace("px", "");
- imgW = parseFloat(str);
- scale = imgW / this.trueWidth;
- }
- if (str.search("%") !== -1) {
- str = str.replace("%", "");
- imgW = (parseFloat(str) / 100) * this.w;
- scale = imgW / this.trueWidth;
- }
- if (arr.length === 2 && str === "auto") {
- let str2 = arr[1];
- if (str2.search("px") !== -1) {
- str2 = str2.replace("px", "");
- imgH = parseFloat(str2);
- scale = imgH / this.trueHeight;
- }
- if (str2.search("%") !== -1) {
- str2 = str2.replace("%", "");
- imgH = (parseFloat(str2) / 100) * this.h;
- scale = imgH / this.trueHeight;
- }
- }
- } catch (error) {
- scale = 1;
- }
- }
- return scale;
- },
- // 自动截图函数
- goAutoCrop(cw, ch) {
- if (this.imgs === '' || this.imgs === null) return
- this.clearCrop();
- this.cropping = true;
- let maxWidth = this.w;
- let maxHeight = this.h;
- if (this.centerBox) {
- let imgW = this.trueWidth * this.scale;
- let imgH = this.trueHeight * this.scale;
- maxWidth = imgW < maxWidth ? imgW : maxWidth;
- maxHeight = imgH < maxHeight ? imgH : maxHeight;
- }
- // 截图框默认大小
- // 如果为0 那么计算容器大小 默认为80%
- var w = cw ? cw : parseFloat(this.autoCropWidth);
- var h = ch ? ch : parseFloat(this.autoCropHeight);
- if (w === 0 || h === 0) {
- w = maxWidth * 0.8;
- h = maxHeight * 0.8;
- }
- w = w > maxWidth ? maxWidth : w;
- h = h > maxHeight ? maxHeight : h;
- if (this.fixed) {
- h = (w / this.fixedNumber[0]) * this.fixedNumber[1];
- }
- // 如果比例之后 高度大于h
- if (h > this.h) {
- h = this.h;
- w = (h / this.fixedNumber[1]) * this.fixedNumber[0];
- }
- this.changeCrop(w, h);
- },
- // 手动改变截图框大小函数
- changeCrop(w, h) {
- if (this.centerBox) {
- // 修复初始化时候在centerBox=true情况下
- let axis = this.getImgAxis();
- if (w > axis.x2 - axis.x1) {
- // 宽度超标
- w = axis.x2 - axis.x1;
- h = (w / this.fixedNumber[0]) * this.fixedNumber[1];
- }
- if (h > axis.y2 - axis.y1) {
- // 高度超标
- h = axis.y2 - axis.y1;
- w = (h / this.fixedNumber[1]) * this.fixedNumber[0];
- }
- }
- // 判断是否大于容器
- this.cropW = w;
- this.cropH = h;
- this.checkCropLimitSize()
- this.$nextTick(() => {
- // 居中走一走
- this.cropOffsertX = (this.w - this.cropW) / 2;
- this.cropOffsertY = (this.h - this.cropH) / 2;
- if (this.centerBox) {
- this.moveCrop(null, true);
- }
- });
- },
- // 重置函数, 恢复组件置初始状态
- refresh() {
- let img = this.img;
- this.imgs = "";
- this.scale = 1;
- this.crop = false;
- this.rotate = 0;
- this.w = 0;
- this.h = 0;
- this.trueWidth = 0;
- this.trueHeight = 0;
- this.clearCrop();
- this.$nextTick(() => {
- this.checkedImg();
- });
- },
- // 向左边旋转
- rotateLeft() {
- this.rotate = this.rotate <= -3 ? 0 : this.rotate - 1;
- },
- // 向右边旋转
- rotateRight() {
- this.rotate = this.rotate >= 3 ? 0 : this.rotate + 1;
- },
- // 清除旋转
- rotateClear() {
- this.rotate = 0;
- },
- // 图片坐标点校验
- checkoutImgAxis(x, y, scale) {
- x = x || this.x;
- y = y || this.y;
- scale = scale || this.scale;
- let canGo = true;
- // 开始校验 如果说缩放之后的坐标在截图框外 则阻止缩放
- if (this.centerBox) {
- let axis = this.getImgAxis(x, y, scale);
- let cropAxis = this.getCropAxis();
- // 左边的横坐标 图片不能超过截图框
- if (axis.x1 >= cropAxis.x1) {
- canGo = false;
- }
- // 右边横坐标
- if (axis.x2 <= cropAxis.x2) {
- canGo = false;
- }
- // 纵坐标上面
- if (axis.y1 >= cropAxis.y1) {
- canGo = false;
- }
- // 纵坐标下面
- if (axis.y2 <= cropAxis.y2) {
- canGo = false;
- }
- }
- return canGo;
- }
- },
- mounted() {
- this.support =
- "onwheel" in document.createElement("div")
- ? "wheel"
- : document.onmousewheel !== undefined
- ? "mousewheel"
- : "DOMMouseScroll";
- let that = this;
- var u = navigator.userAgent;
- this.isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
- // 兼容blob
- if (!HTMLCanvasElement.prototype.toBlob) {
- Object.defineProperty(HTMLCanvasElement.prototype, "toBlob", {
- value: function(callback, type, quality) {
- var binStr = atob(this.toDataURL(type, quality).split(",")[1]),
- len = binStr.length,
- arr = new Uint8Array(len);
- for (var i = 0; i < len; i++) {
- arr[i] = binStr.charCodeAt(i);
- }
- callback(new Blob([arr], { type: that.type || "image/png" }));
- }
- });
- }
- this.showPreview();
- this.checkedImg();
- },
- destroyed() {
- window.removeEventListener("mousemove", this.moveCrop);
- window.removeEventListener("mouseup", this.leaveCrop);
- window.removeEventListener("touchmove", this.moveCrop);
- window.removeEventListener("touchend", this.leaveCrop);
- }
- };
- </script>
- <style scoped lang="css">
- .vue-cropper {
- position: relative;
- width: 100%;
- height: 100%;
- box-sizing: border-box;
- user-select: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- direction: ltr;
- touch-action: none;
- text-align: left;
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
- }
- .cropper-box,
- .cropper-box-canvas,
- .cropper-drag-box,
- .cropper-crop-box,
- .cropper-face {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- user-select: none;
- }
- .cropper-box-canvas img {
- position: relative;
- text-align: left;
- user-select: none;
- transform: none;
- max-width: none;
- max-height: none;
- }
- .cropper-box {
- overflow: hidden;
- }
- .cropper-move {
- cursor: move;
- }
- .cropper-crop {
- cursor: crosshair;
- }
- .cropper-modal {
- background: rgba(0, 0, 0, 0.5);
- }
- .cropper-crop-box {
- /*border: 2px solid #39f;*/
- }
- .cropper-view-box {
- display: block;
- overflow: hidden;
- width: 100%;
- height: 100%;
- outline: 1px solid #39f;
- outline-color: rgba(51, 153, 255, 0.75);
- user-select: none;
- }
- .cropper-view-box img {
- user-select: none;
- text-align: left;
- max-width: none;
- max-height: none;
- }
- .cropper-face {
- top: 0;
- left: 0;
- background-color: #fff;
- opacity: 0.1;
- }
- .crop-info {
- position: absolute;
- left: 0px;
- min-width: 65px;
- text-align: center;
- color: white;
- line-height: 20px;
- background-color: rgba(0, 0, 0, 0.8);
- font-size: 12px;
- }
- .crop-line {
- position: absolute;
- display: block;
- width: 100%;
- height: 100%;
- opacity: 0.1;
- }
- .line-w {
- top: -3px;
- left: 0;
- height: 5px;
- cursor: n-resize;
- }
- .line-a {
- top: 0;
- left: -3px;
- width: 5px;
- cursor: w-resize;
- }
- .line-s {
- bottom: -3px;
- left: 0;
- height: 5px;
- cursor: s-resize;
- }
- .line-d {
- top: 0;
- right: -3px;
- width: 5px;
- cursor: e-resize;
- }
- .crop-point {
- position: absolute;
- width: 8px;
- height: 8px;
- opacity: 0.75;
- background-color: #39f;
- border-radius: 100%;
- }
- .point1 {
- top: -4px;
- left: -4px;
- cursor: nw-resize;
- }
- .point2 {
- top: -5px;
- left: 50%;
- margin-left: -3px;
- cursor: n-resize;
- }
- .point3 {
- top: -4px;
- right: -4px;
- cursor: ne-resize;
- }
- .point4 {
- top: 50%;
- left: -4px;
- margin-top: -3px;
- cursor: w-resize;
- }
- .point5 {
- top: 50%;
- right: -4px;
- margin-top: -3px;
- cursor: e-resize;
- }
- .point6 {
- bottom: -5px;
- left: -4px;
- cursor: sw-resize;
- }
- .point7 {
- bottom: -5px;
- left: 50%;
- margin-left: -3px;
- cursor: s-resize;
- }
- .point8 {
- bottom: -5px;
- right: -4px;
- cursor: se-resize;
- }
- @media screen and (max-width: 500px) {
- .crop-point {
- position: absolute;
- width: 20px;
- height: 20px;
- opacity: 0.45;
- background-color: #39f;
- border-radius: 100%;
- }
- .point1 {
- top: -10px;
- left: -10px;
- }
- .point2,
- .point4,
- .point5,
- .point7 {
- display: none;
- }
- .point3 {
- top: -10px;
- right: -10px;
- }
- .point4 {
- top: 0;
- left: 0;
- }
- .point6 {
- bottom: -10px;
- left: -10px;
- }
- .point8 {
- bottom: -10px;
- right: -10px;
- }
- }
- </style>
|