| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149 |
- <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}"
- >{{ cropInfo.width }} × {{ 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 { defineComponent } from 'vue'
- import exifmin from "./exif-js-min";
- export default defineComponent({
- 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;
- },
- validator: function (value) {
- if (Array.isArray(value)) {
- return Number(value[0]) >= 0 && Number(value[1]) >= 0;
- } else {
- return Number(value) >= 0;
- }
- },
- },
- },
- 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("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 = Number(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("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 = ('clientX' in e ? e.clientX : e.touches[0].clientX) - this.x;
- this.moveY = ('clientY' in e ? 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("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 = 'clientX' in e ? e.clientX : e.touches[0].clientX;
- this.cropY = 'clientY' in e ? 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 = 'clientX' in e ? e.clientX : e.touches[0].clientX;
- let nowY = 'clientY' in e ? 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("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("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 = 'clientX' in e ? e.clientX : e.touches ? e.touches[0].clientX : 0;
- var nowY = 'clientY' in e ? 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 = 'clientX' in e ? e.clientX : e.touches[0].clientX;
- this.cropY = 'clientY' in e ? 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 = 'clientX' in e ? e.clientX : e.touches ? e.touches[0].clientX : 0;
- var nowY = 'clientY' in e ? 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;
- }
- }
- const [minCropW, minCropH] = this.checkCropLimitSize()
- this.$nextTick(() => {
- var fw = nowX - this.cropX;
- var fh = nowY - this.cropY;
- if (this.canChangeX) {
- if (this.changeCropTypeX === 1) {
- if (this.cropOldW - fw < minCropW) {
- this.cropW = minCropW
- this.cropOffsertX = this.cropOldW + this.cropChangeX - minX - minCropW
- } else 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 < minCropW) {
- this.cropW = minCropW
- } else 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 < minCropH) {
- this.cropH = minCropH
- this.cropOffsertY = this.cropOldH + this.cropChangeY - minY - minCropH
- } else 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 < minCropH) {
- this.cropH = minCropH
- } else 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 < minCropH) {
- this.cropH = minCropH
- this.cropW = this.fixedNumber[0] * minCropH / this.fixedNumber[1]
- // 这里需要去修改 offsetX的值,去调整因为高度变化而导致的宽度变化
- if (this.changeCropTypeX === 1) {
- this.cropOffsertX = this.cropChangeX + (this.cropOldW - this.cropW)
- }
- } else if (fixedHeight + this.cropOffsertY > wrapperH) {
- this.cropH = wrapperH - this.cropOffsertY;
- this.cropW =
- (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
- if (this.changeCropTypeX === 1) {
- this.cropOffsertX = this.cropChangeX + (this.cropOldW - this.cropW)
- }
- } else {
- this.cropH = fixedHeight;
- }
- }
- if (this.canChangeY && this.fixed) {
- var fixedWidth =
- (this.cropH / this.fixedNumber[1]) * this.fixedNumber[0];
- if (fixedWidth < minCropW) {
- this.cropW = minCropW
- this.cropH = this.fixedNumber[1] * minCropW / this.fixedNumber[0];
- this.cropOffsertY = this.cropOldH + this.cropChangeY - this.cropH
- } else 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);
- },
- // 根据比例x/y,最小宽度,最小高度,现有宽度,现有高度,得到应该有的宽度和高度
- calculateSize(x, y, minX, minY, w, h) {
- const ratio = x / y;
- let width = w;
- let height = h;
- // 先根据最小宽度来计算高度
- if (width < minX) {
- width = minX;
- height = Math.ceil(width / ratio);
- }
- // 如果计算出来的高度小于最小高度,则根据最小高度来重新计算宽度和高度
- if (height < minY) {
- height = minY;
- width = Math.ceil(height * ratio);
- // 如果重新计算的宽度仍然小于最小宽度,则使用最小宽度,并重新计算高度
- if (width < minX) {
- width = minX;
- height = Math.ceil(width / ratio);
- }
- }
- // 如果计算出来的宽度或高度小于输入的宽度或高度,则分别使用输入的宽度或高度
- if (width < w) {
- width = w;
- height = Math.ceil(width / ratio);
- }
- if (height < h) {
- height = h;
- width = Math.ceil(height * ratio);
- }
- return { width, height };
- },
- // 创建完成
- endCrop() {
- if (this.cropW === 0 && this.cropH === 0) {
- this.cropping = false;
- }
- let [minCropW, minCropH] = this.checkCropLimitSize();
- const { width, height } = this.fixed ? this.calculateSize(
- this.fixedNumber[0],
- this.fixedNumber[1],
- minCropW,
- minCropH,
- this.cropW,
- this.cropH
- ) : { width: minCropW, height: minCropH }
- if (width > this.cropW) {
- this.cropW = width;
- if (this.cropOffsertX + width > this.w) {
- this.cropOffsertX = this.w - width;
- }
- }
- if (height > this.cropH) {
- this.cropH = height;
- if (this.cropOffsertY + height > this.h) {
- this.cropOffsertY = this.h - height;
- }
- }
- 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 = 'clientX' in e ? e.clientX : e.touches[0].clientX;
- let y = 'clientY' in e ? 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("crop-moving", {
- moving: true,
- axis: this.getCropAxis()
- });
- },
- moveCrop(e, isMove) {
- let nowX = 0;
- let nowY = 0;
- if (e) {
- e.preventDefault();
- nowX = 'clientX' in e ? e.clientX : e.touches[0].clientX;
- nowY = 'clientY' in e ? 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("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("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("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");
- setTimeout(() => {
- this.showPreview();
- }, 20);
- });
- };
- img.onerror = () => {
- 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);
- const scaleX = imgW / this.trueWidth;
- let scaleY = 1;
- let strH = arr[1];
- if (strH.search("px") !== -1) {
- strH = strH.replace("px", "");
- imgH = parseFloat(strH);
- scaleY = imgH / this.trueHeight;
- }
- scale = Math.min(scaleX,scaleY)
- }
- 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) {
- const switchWH = Math.abs(this.rotate) % 2 > 0
- let imgW = (switchWH ? this.trueHeight : this.trueWidth) * this.scale;
- let imgH = (switchWH ? this.trueWidth : 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();
- },
- unmounted() {
- window.removeEventListener("mousemove", this.moveCrop);
- window.removeEventListener("mouseup", this.leaveCrop);
- window.removeEventListener("touchmove", this.moveCrop);
- window.removeEventListener("touchend", this.leaveCrop);
- this.cancelScale()
- }
- });
- </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>
|