diff --git a/src/components/ImageCropper/index.vue b/src/components/ImageCropper/index.vue index c33f6ec9..2bd22c59 100644 --- a/src/components/ImageCropper/index.vue +++ b/src/components/ImageCropper/index.vue @@ -1,697 +1,1415 @@ <template> - <div class="vue-image-crop-upload" v-show="show"> - <div class="vicp-wrap"> - <div class="vicp-close" @click="off"> - <i class="vicp-icon4"></i> - </div> +<div class="vue-image-crop-upload" v-show="value"> + <div class="vicp-wrap"> + <div class="vicp-close" @click="off"> + <i class="vicp-icon4"></i> + </div> - <div class="vicp-step1" v-show="step == 1"> - <div class="vicp-drop-area" - @dragleave="preventDefault" - @dragover="preventDefault" - @dragenter="preventDefault" - @click="handleClick" - @drop="handleChange"> - <i class="vicp-icon1" v-show="loading != 1"> - <i class="vicp-icon1-arrow"></i> - <i class="vicp-icon1-body"></i> - <i class="vicp-icon1-bottom"></i> - </i> - <span class="vicp-hint" v-show="loading !== 1">{{ lang.hint }}</span> - <span class="vicp-no-supported-hint" v-show="!isSupported">{{ lang.noSupported }}</span> - <input type="file" v-show="false" @change="handleChange" ref="fileinput"> - </div> - <div class="vicp-error" v-show="hasError"> - <i class="vicp-icon2"></i> {{ errorMsg }} - </div> - <div class="vicp-operate"> - <a @click="off" @mousedown="ripple">{{ lang.btn.off }}</a> - </div> - </div> + <div class="vicp-step1" v-show="step == 1"> + <div class="vicp-drop-area" @dragleave="preventDefault" @dragover="preventDefault" @dragenter="preventDefault" @click="handleClick" @drop="handleChange"> + <i class="vicp-icon1" v-show="loading != 1"> + <i class="vicp-icon1-arrow"></i> + <i class="vicp-icon1-body"></i> + <i class="vicp-icon1-bottom"></i> + </i> + <span class="vicp-hint" v-show="loading !== 1">{{ lang.hint }}</span> + <span class="vicp-no-supported-hint" v-show="!isSupported">{{ lang.noSupported }}</span> + <input type="file" v-show="false" v-if="step == 1" @change="handleChange" ref="fileinput"> + </div> + <div class="vicp-error" v-show="hasError"> + <i class="vicp-icon2"></i> {{ errorMsg }} + </div> + <div class="vicp-operate"> + <a @click="off" @mousedown="ripple">{{ lang.btn.off }}</a> + </div> + </div> - <div class="vicp-step2" v-if="step == 2"> - <div class="vicp-crop"> - <div class="vicp-crop-left" v-show="true"> - <div class="vicp-img-container"> - <img :src="sourceImgUrl" - :style="sourceImgStyle" - class="vicp-img" - draggable="false" - @drag="preventDefault" - @dragstart="preventDefault" - @dragend="preventDefault" - @dragleave="preventDefault" - @dragover="preventDefault" - @dragenter="preventDefault" - @drop="preventDefault" - @mousedown="imgStartMove" - @mousemove="imgMove" - @mouseup="createImg" - @mouseout="createImg" - ref="img"> - <div class="vicp-img-shade vicp-img-shade-1" :style="sourceImgShadeStyle"></div> - <div class="vicp-img-shade vicp-img-shade-2" :style="sourceImgShadeStyle"></div> - </div> - <div class="vicp-range"> - <input type="range" :value="scale.range" step="1" min="0" max="100" @change="zoomChange"> - <i @mousedown="startZoomSub" @mouseout="endZoomSub" @mouseup="endZoomSub" - class="vicp-icon5"></i> - <i @mousedown="startZoomAdd" @mouseout="endZoomAdd" @mouseup="endZoomAdd" - class="vicp-icon6"></i> - </div> - </div> - <div class="vicp-crop-right" v-show="true"> - <div class="vicp-preview"> - <div class="vicp-preview-item"> - <img :src="createImgUrl" :style="previewStyle"> - <span>{{ lang.preview }}</span> - </div> - <div class="vicp-preview-item"> - <img :src="createImgUrl" :style="previewStyle" v-if="!noCircle"> - <span>{{ lang.preview }}</span> - </div> - </div> - </div> - </div> - <div class="vicp-operate"> - <a @click="setStep(1)" @mousedown="ripple">{{ lang.btn.back }}</a> - <a class="vicp-operate-btn" @click="upload" @mousedown="ripple">{{ lang.btn.save }}</a> - </div> - </div> + <div class="vicp-step2" v-if="step == 2"> + <div class="vicp-crop"> + <div class="vicp-crop-left" v-show="true"> + <div class="vicp-img-container"> + <img :src="sourceImgUrl" :style="sourceImgStyle" class="vicp-img" draggable="false" + @drag="preventDefault" + @dragstart="preventDefault" + @dragend="preventDefault" + @dragleave="preventDefault" + @dragover="preventDefault" + @dragenter="preventDefault" + @drop="preventDefault" + @touchstart="imgStartMove" + @touchmove="imgMove" + @touchend="createImg" + @touchcancel="createImg" + @mousedown="imgStartMove" + @mousemove="imgMove" + @mouseup="createImg" + @mouseout="createImg" + ref="img"> + <div class="vicp-img-shade vicp-img-shade-1" :style="sourceImgShadeStyle"></div> + <div class="vicp-img-shade vicp-img-shade-2" :style="sourceImgShadeStyle"></div> + </div> - <div class="vicp-step3" v-if="step == 3"> - <div class="vicp-upload"> - <span class="vicp-loading" v-show="loading === 1">{{ lang.loading }}</span> - <div class="vicp-progress-wrap"> - <span class="vicp-progress" v-show="loading === 1" :style="progressStyle"></span> + <div class="vicp-range"> + <input type="range" :value="scale.range" step="1" min="0" max="100" @input="zoomChange"> + <i @mousedown="startZoomSub" @mouseout="endZoomSub" @mouseup="endZoomSub" class="vicp-icon5"></i> + <i @mousedown="startZoomAdd" @mouseout="endZoomAdd" @mouseup="endZoomAdd" class="vicp-icon6"></i> + </div> + + <div class="vicp-rotate" v-if="!noRotate"> + <i @mousedown="startRotateLeft" @mouseout="endRotate" @mouseup="endRotate">↺</i> + <i @mousedown="startRotateRight" @mouseout="endRotate" @mouseup="endRotate">↻</i> </div> - <div class="vicp-error" v-show="hasError"> - <i class="vicp-icon2"></i> {{ errorMsg }} - </div> - <div class="vicp-success" v-show="loading === 2"> - <i class="vicp-icon3"></i> {{ lang.success }} - </div> - </div> - <div class="vicp-operate"> - <a @click="setStep(2)" @mousedown="ripple">{{ lang.btn.back }}</a> - <a @click="off" @mousedown="ripple">{{ lang.btn.close }}</a> - </div> - </div> - <canvas v-show="false" :width="width" :height="height" ref="canvas"></canvas> - </div> - </div> + </div> + <div class="vicp-crop-right" v-show="true"> + <div class="vicp-preview"> + <div class="vicp-preview-item" v-if="!noSquare"> + <img :src="createImgUrl" :style="previewStyle"> + <span>{{ lang.preview }}</span> + </div> + <div class="vicp-preview-item vicp-preview-item-circle" v-if="!noCircle"> + <img :src="createImgUrl" :style="previewStyle"> + <span>{{ lang.preview }}</span> + </div> + </div> + </div> + </div> + <div class="vicp-operate"> + <a @click="setStep(1)" @mousedown="ripple">{{ lang.btn.back }}</a> + <a class="vicp-operate-btn" @click="prepareUpload" @mousedown="ripple">{{ lang.btn.save }}</a> + </div> + </div> + + <div class="vicp-step3" v-if="step == 3"> + <div class="vicp-upload"> + <span class="vicp-loading" v-show="loading === 1">{{ lang.loading }}</span> + <div class="vicp-progress-wrap"> + <span class="vicp-progress" v-show="loading === 1" :style="progressStyle"></span> + </div> + <div class="vicp-error" v-show="hasError"> + <i class="vicp-icon2"></i> {{ errorMsg }} + </div> + <div class="vicp-success" v-show="loading === 2"> + <i class="vicp-icon3"></i> {{ lang.success }} + </div> + </div> + <div class="vicp-operate"> + <a @click="setStep(2)" @mousedown="ripple">{{ lang.btn.back }}</a> + <a @click="off" @mousedown="ripple">{{ lang.btn.close }}</a> + </div> + </div> + <canvas v-show="false" :width="width" :height="height" ref="canvas"></canvas> + </div> +</div> </template> <script> - /* eslint-disable */ - import {effectRipple, data2blob} from './utils'; - import request from '@/utils/request'; - import langBag from './lang'; - const mimes = { - 'jpg': 'image/jpeg', - 'png': 'image/png', - 'gif': 'image/gif', - 'svg': 'image/svg+xml', - 'psd': 'image/photoshop' - }; - - export default { - props: { - // 域,上传文件name,触发事件会带上(如果一个页面多个图片上传控件,可以做区分 - field: { - type: String, - default: 'avatar' - }, - // 上传地址 - url: { - type: String, - default: '' - }, - // 其他要上传文件附带的数据,对象格式 - params: { - type: Object, - default: null - }, - // 剪裁图片的宽 - width: { - type: Number, - default: 200 - }, - // 剪裁图片的高 - height: { - type: Number, - default: 200 - }, - // 不预览圆形图片 - noCircle: { - type: Boolean, - default: false - }, - // 单文件大小限制 - maxSize: { - type: Number, - default: 10240 - }, - // 语言类型 - langType: { - type: String, - 'default': 'zh' - }, - - }, - data() { - let that = this, - { - langType, - width, - height - } = that, - isSupported = true, - lang = langBag[langType] ? langBag[langType] : lang['zh']; - - if (typeof FormData != 'function') { - isSupported = false; - } - return { - show: true, - // 图片的mime - mime:mimes['jpg'], - // 语言包 - lang, - // 浏览器是否支持该控件 - isSupported, - // 步骤 - step: 1, //1选择文件 2剪裁 3上传 - // 上传状态及进度 - loading: 0, //0未开始 1正在 2成功 3错误 - progress: 0, - // 是否有错误及错误信息 - hasError: false, - errorMsg: '', - // 需求图宽高比 - ratio: width / height, - // 原图地址、生成图片地址 - sourceImg: null, - sourceImgUrl: '', - createImgUrl: '', - // 原图片拖动事件初始值 - sourceImgMouseDown: { - on: false, - mX: 0, //鼠标按下的坐标 - mY: 0, - x: 0, //scale原图坐标 - y: 0 - }, - // 生成图片预览的容器大小 - previewContainer: { - width: 100, - height: 100 - }, - // 原图容器宽高 - sourceImgContainer: { // sic - width: 240, - height: 180 - }, - // 原图展示属性 - scale: { - zoomAddOn: false, //按钮缩放事件开启 - zoomSubOn: false, //按钮缩放事件开启 - range: 1, //最大100 - x: 0, - y: 0, - width: 0, - height: 0, - maxWidth: 0, - maxHeight: 0, - minWidth: 0, //最宽 - minHeight: 0, - naturalWidth: 0, //原宽 - naturalHeight: 0 - } - } - }, - computed: { - // 进度条样式 - progressStyle() { - let { - progress - } = this; - return { - width: progress + '%' - } - }, - // 原图样式 - sourceImgStyle() { - let { - scale, - sourceImgMasking - } = this; - return { - top: scale.y + sourceImgMasking.y + 'px', - left: scale.x + sourceImgMasking.x + 'px', - width: scale.width + 'px', - height: scale.height + 'px' - } - }, - // 原图蒙版属性 - sourceImgMasking() { - let { - width, - height, - ratio, - sourceImgContainer - } = this, - sic = sourceImgContainer, - sicRatio = sic.width / sic.height, // 原图容器宽高比 - x = 0, - y = 0, - w = sic.width, - h = sic.height, - scale = 1; - if (ratio < sicRatio) { - scale = sic.height / height; - w = sic.height * ratio; - x = (sic.width - w) / 2; - } - if (ratio > sicRatio) { - scale = sic.width / width; - h = sic.width / ratio; - y = (sic.height - h) / 2; - } - return { - scale, // 蒙版相对需求宽高的缩放 - x, - y, - width: w, - height: h - }; - }, - // 原图遮罩样式 - sourceImgShadeStyle() { - let sic = this.sourceImgContainer, - sim = this.sourceImgMasking, - w = sim.width == sic.width ? sim.width : (sic.width - sim.width) / 2, - h = sim.height == sic.height ? sim.height : (sic.height - sim.height) / 2; - return { - width: w + 'px', - height: h + 'px' - }; - }, - previewStyle() { - let { - width, - height, - ratio, - previewContainer - } = this, - pc = previewContainer, - w = pc.width, - h = pc.height, - pcRatio = w / h; - if (ratio < pcRatio) { - w = pc.height * ratio; - } - if (ratio > pcRatio) { - h = pc.width / ratio; - } - return { - width: w + 'px', - height: h + 'px' - }; - } - }, - methods: { - // 点击波纹效果 - ripple(e) { - effectRipple(e); - }, - // 关闭控件 - off() { - this.show = false; - this.$emit('close'); - }, - // 设置步骤 - setStep(step) { - let that = this; - setTimeout(function () { - that.step = step; - }, 200); - }, - /* 图片选择区域函数绑定 - ---------------------------------------------------------------*/ - preventDefault(e) { - e.preventDefault(); - return false; - }, - handleClick(e) { - if (this.loading !== 1) { - if (e.target !== this.$refs.fileinput) { - e.preventDefault(); - if (document.activeElement !== this.$refs) { - this.$refs.fileinput.click(); - } - } - } - }, - handleChange(e) { - e.preventDefault(); - if (this.loading !== 1) { - let files = e.target.files || e.dataTransfer.files; - this.reset(); - if (this.checkFile(files[0])) { - this.setSourceImg(files[0]); - } - } - }, - /* ---------------------------------------------------------------*/ - // 检测选择的文件是否合适 - checkFile(file) { - let that = this, - { - lang, - maxSize - } = that; - // 仅限图片 - if (file.type.indexOf('image') === -1) { - that.hasError = true; - that.errorMsg = lang.error.onlyImg; - return false; - } - this.mime=file.type; - // 超出大小 - if (file.size / 1024 > maxSize) { - that.hasError = true; - that.errorMsg = lang.error.outOfSize + maxSize + 'kb'; - return false; - } - return true; - }, - // 重置控件 - reset() { - let that = this; - that.step = 1; - that.loading = 0; - that.hasError = false; - that.errorMsg = ''; - that.progress = 0; - }, - // 设置图片源 - setSourceImg(file) { - let that = this, - fr = new FileReader(); - fr.onload = function (e) { - that.sourceImgUrl = fr.result; - that.startCrop(); - }; - fr.readAsDataURL(file); - }, - // 剪裁前准备工作 - startCrop() { - let that = this, - { - width, - height, - ratio, - scale, - sourceImgUrl, - sourceImgMasking, - lang - } = that, - sim = sourceImgMasking, - img = new Image(); - img.src = sourceImgUrl; - img.onload = function () { - let nWidth = img.naturalWidth, - nHeight = img.naturalHeight, - nRatio = nWidth / nHeight, - w = sim.width, - h = sim.height, - x = 0, - y = 0; - // 图片像素不达标 -// if (nWidth < width || nHeight < height) { -// that.hasError = true; -// that.errorMsg = lang.error.lowestPx + width + '*' + height; -// return false; -// } - if (ratio > nRatio) { - h = w / nRatio; - y = (sim.height - h) / 2; - } - if (ratio < nRatio) { - w = h * nRatio; - x = (sim.width - w) / 2; - } - scale.range = 0; - scale.x = x; - scale.y = y; - scale.width = w; - scale.height = h; - scale.minWidth = w; - scale.minHeight = h; - scale.maxWidth = nWidth * sim.scale; - scale.maxHeight = nHeight * sim.scale; - scale.naturalWidth = nWidth; - scale.naturalHeight = nHeight; - that.sourceImg = img; - that.createImg(); - that.setStep(2); - }; - }, - // 鼠标按下图片准备移动 - imgStartMove(e) { - let { - sourceImgMouseDown, - scale - } = this, - simd = sourceImgMouseDown; - simd.mX = e.screenX; - simd.mY = e.screenY; - simd.x = scale.x; - simd.y = scale.y; - simd.on = true; - }, - // 鼠标按下状态下移动,图片移动 - imgMove(e) { - let { - sourceImgMouseDown: { - on, - mX, - mY, - x, - y - }, - scale, - sourceImgMasking - } = this, - sim = sourceImgMasking, - nX = e.screenX, - nY = e.screenY, - dX = nX - mX, - dY = nY - mY, - rX = x + dX, - rY = y + dY; - if (!on) return; - if (rX > 0) { - rX = 0; - } - if (rY > 0) { - rY = 0; - } - if (rX < sim.width - scale.width) { - rX = sim.width - scale.width; - } - if (rY < sim.height - scale.height) { - rY = sim.height - scale.height; - } - scale.x = rX; - scale.y = rY; - }, - // 按钮按下开始放大 - startZoomAdd(e) { - let that = this, - { - scale - } = that; - scale.zoomAddOn = true; - function zoom() { - if (scale.zoomAddOn) { - let range = scale.range >= 100 ? 100 : ++scale.range; - that.zoomImg(range); - setTimeout(function () { - zoom(); - }, 60); - } - } - - zoom(); - }, - // 按钮松开或移开取消放大 - endZoomAdd(e) { - this.scale.zoomAddOn = false; - }, - // 按钮按下开始缩小 - startZoomSub(e) { - let that = this, - { - scale - } = that; - scale.zoomSubOn = true; - function zoom() { - if (scale.zoomSubOn) { - let range = scale.range <= 0 ? 0 : --scale.range; - that.zoomImg(range); - setTimeout(function () { - zoom(); - }, 60); - } - } - - zoom(); - }, - // 按钮松开或移开取消缩小 - endZoomSub(e) { - let { - scale - } = this; - scale.zoomSubOn = false; - }, - zoomChange(e) { - this.zoomImg(e.target.value); - }, - // 缩放原图 - zoomImg(newRange) { - let that = this, - { - sourceImgMasking, - sourceImgMouseDown, - scale - } = this, - { - maxWidth, - maxHeight, - minWidth, - minHeight, - width, - height, - x, - y, - range - } = scale, - sim = sourceImgMasking, - // 蒙版宽高 - sWidth = sim.width, - sHeight = sim.height, - // 新宽高 - nWidth = minWidth + (maxWidth - minWidth) * newRange / 100, - nHeight = minHeight + (maxHeight - minHeight) * newRange / 100, - // 新坐标(根据蒙版中心点缩放) - nX = sWidth / 2 - (nWidth / width) * (sWidth / 2 - x), - nY = sHeight / 2 - (nHeight / height) * (sHeight / 2 - y); - // 判断新坐标是否超过蒙版限制 - if (nX > 0) { - nX = 0; - } - if (nY > 0) { - nY = 0; - } - if (nX < sWidth - nWidth) { - nX = sWidth - nWidth; - } - if (nY < sHeight - nHeight) { - nY = sHeight - nHeight; - } - // 赋值处理 - scale.x = nX; - scale.y = nY; - scale.width = nWidth; - scale.height = nHeight; - scale.range = newRange; - setTimeout(function () { - if (scale.range == newRange) { - that.createImg(); - } - }, 300); - }, - // 生成需求图片 - createImg(e) { - let that = this, - { - mime, - sourceImg, - scale: { - x, - y, - width, - height - }, - sourceImgMasking: { - scale - } - } = that, - canvas = that.$refs.canvas, - ctx = canvas.getContext('2d'); - if (e) { - // 取消鼠标按下移动状态 - that.sourceImgMouseDown.on = false; - } - ctx.drawImage(sourceImg, x / scale, y / scale, width / scale, height / scale); - that.createImgUrl = canvas.toDataURL(mime); - }, - // 上传图片 - upload() { - let that = this, - { - lang, - imgFormat, - mime, - url, - params, - headers, - field, - ki, - createImgUrl - } = this, - fmData = new FormData(); - fmData.append(field, data2blob(createImgUrl, mime), field + '.' + imgFormat); - // 添加其他参数 - if (typeof params == 'object' && params) { - Object.keys(params).forEach((k) => { - fmData.append(k, params[k]); - }) - } - // 监听进度回调 - function uploadProgress (event) { - console.log(event) - if (event.lengthComputable) { - that.progress = 100 * Math.round(event.loaded) / event.total; - } - }; - // 上传文件 - that.reset(); - that.loading = 1; - that.setStep(3); - that.$emit('crop-success', createImgUrl, field, ki); - request({ - url, - method: 'post', - data: fmData - }).then(resData=>{ - that.loading = 2; - that.$emit('crop-upload-success', resData.data); - }).catch(err=>{ - if (that.value) { - that.loading = 3; - that.hasError = true; - that.errorMsg = lang.fail; - that.$emit('crop-upload-fail', err, field, ki); - } - }); - } - } + /* eslint-disable */ +'use strict' +import request from '@/utils/request' +import language from './utils/language.js' +import mimes from './utils/mimes.js' +import data2blob from './utils/data2blob.js' +import effectRipple from './utils/effectRipple.js' +export default { + props: { + // 域,上传文件name,触发事件会带上(如果一个页面多个图片上传控件,可以做区分 + field: { + type: String, + 'default': 'avatar' + }, + // 原名key,类似于id,触发事件会带上(如果一个页面多个图片上传控件,可以做区分 + ki: { + 'default': 0 + }, + // 显示该控件与否 + value: { + 'default': true + }, + // 上传地址 + url: { + type: String, + 'default': '' + }, + // 其他要上传文件附带的数据,对象格式 + params: { + type: Object, + 'default': null + }, + // Add custom headers + headers: { + type: Object, + 'default': null + }, + // 剪裁图片的宽 + width: { + type: Number, + default: 200 + }, + // 剪裁图片的高 + height: { + type: Number, + default: 200 + }, + // 不显示旋转功能 + noRotate: { + type: Boolean, + default: true + }, + // 不预览圆形图片 + noCircle: { + type: Boolean, + default: false + }, + // 不预览方形图片 + noSquare: { + type: Boolean, + default: false + }, + // 单文件大小限制 + maxSize: { + type: Number, + 'default': 10240 + }, + // 语言类型 + langType: { + type: String, + 'default': 'zh' + }, + // 语言包 + langExt: { + type: Object, + 'default': null + }, + // 图片上传格式 + imgFormat: { + type: String, + 'default': 'png' + }, + // 是否支持跨域 + withCredentials: { + type: Boolean, + 'default': false } + }, + data() { + const that = this + const { + imgFormat, + langType, + langExt, + width, + height + } = that + let isSupported = true + const allowImgFormat = [ + 'jpg', + 'png' + ] + const tempImgFormat = allowImgFormat.indexOf(imgFormat) === -1 ? 'jpg' : imgFormat + const lang = language[langType] ? language[langType] : language['en'] + const mime = mimes[tempImgFormat] + // 规范图片格式 + that.imgFormat = tempImgFormat + if (langExt) { + Object.assign(lang, langExt) + } + if (typeof FormData !== 'function') { + isSupported = false + } + return { + // 图片的mime + mime, + // 语言包 + lang, + // 浏览器是否支持该控件 + isSupported, + // 浏览器是否支持触屏事件 + isSupportTouch: document.hasOwnProperty('ontouchstart'), + // 步骤 + step: 1, // 1选择文件 2剪裁 3上传 + // 上传状态及进度 + loading: 0, // 0未开始 1正在 2成功 3错误 + progress: 0, + // 是否有错误及错误信息 + hasError: false, + errorMsg: '', + // 需求图宽高比 + ratio: width / height, + // 原图地址、生成图片地址 + sourceImg: null, + sourceImgUrl: '', + createImgUrl: '', + // 原图片拖动事件初始值 + sourceImgMouseDown: { + on: false, + mX: 0, // 鼠标按下的坐标 + mY: 0, + x: 0, // scale原图坐标 + y: 0 + }, + // 生成图片预览的容器大小 + previewContainer: { + width: 100, + height: 100 + }, + // 原图容器宽高 + sourceImgContainer: { // sic + width: 240, + height: 184 // 如果生成图比例与此一致会出现bug,先改成特殊的格式吧,哈哈哈 + }, + // 原图展示属性 + scale: { + zoomAddOn: false, // 按钮缩放事件开启 + zoomSubOn: false, // 按钮缩放事件开启 + range: 1, // 最大100 + rotateLeft: false, // 按钮向左旋转事件开启 + rotateRight: false, // 按钮向右旋转事件开启 + degree: 0, // 旋转度数 + x: 0, + y: 0, + width: 0, + height: 0, + maxWidth: 0, + maxHeight: 0, + minWidth: 0, // 最宽 + minHeight: 0, + naturalWidth: 0, // 原宽 + naturalHeight: 0 + } + } + }, + computed: { + // 进度条样式 + progressStyle() { + const { + progress + } = this + return { + width: progress + '%' + } + }, + // 原图样式 + sourceImgStyle() { + const { + scale, + sourceImgMasking + } = this + const top = scale.y + sourceImgMasking.y + 'px' + const left = scale.x + sourceImgMasking.x + 'px' + return { + top, + left, + width: scale.width + 'px', + height: scale.height + 'px', + transform: 'rotate(' + scale.degree + 'deg)', // 旋转时 左侧原始图旋转样式 + '-ms-transform': 'rotate(' + scale.degree + 'deg)', // 兼容IE9 + '-moz-transform': 'rotate(' + scale.degree + 'deg)', // 兼容FireFox + '-webkit-transform': 'rotate(' + scale.degree + 'deg)', // 兼容Safari 和 chrome + '-o-transform': 'rotate(' + scale.degree + 'deg)' // 兼容 Opera + } + }, + // 原图蒙版属性 + sourceImgMasking() { + const { + width, + height, + ratio, + sourceImgContainer + } = this + const sic = sourceImgContainer + const sicRatio = sic.width / sic.height // 原图容器宽高比 + let x = 0 + let y = 0 + let w = sic.width + let h = sic.height + let scale = 1 + if (ratio < sicRatio) { + scale = sic.height / height + w = sic.height * ratio + x = (sic.width - w) / 2 + } + if (ratio > sicRatio) { + scale = sic.width / width + h = sic.width / ratio + y = (sic.height - h) / 2 + } + return { + scale, // 蒙版相对需求宽高的缩放 + x, + y, + width: w, + height: h + } + }, + // 原图遮罩样式 + sourceImgShadeStyle() { + const { + sourceImgMasking, + sourceImgContainer + } = this + const sic = sourceImgContainer + const sim = sourceImgMasking + const w = sim.width == sic.width ? sim.width : (sic.width - sim.width) / 2 + const h = sim.height == sic.height ? sim.height : (sic.height - sim.height) / 2 + return { + width: w + 'px', + height: h + 'px' + } + }, + previewStyle() { + const { + width, + height, + ratio, + previewContainer + } = this + const pc = previewContainer + let w = pc.width + let h = pc.height + const pcRatio = w / h + if (ratio < pcRatio) { + w = pc.height * ratio + } + if (ratio > pcRatio) { + h = pc.width / ratio + } + return { + width: w + 'px', + height: h + 'px' + } + } + }, + watch: { + value(newValue) { + if (newValue && this.loading != 1) { + this.reset() + } + } + }, + methods: { + // 点击波纹效果 + ripple(e) { + effectRipple(e) + }, + // 关闭控件 + off() { + setTimeout(() => { + this.$emit('input', false) + if (this.step == 3 && this.loading == 2) { + this.setStep(1) + } + }, 200) + }, + // 设置步骤 + setStep(no) { + // 延时是为了显示动画效果呢,哈哈哈 + setTimeout(() => { + this.step = no + }, 200) + }, + /* 图片选择区域函数绑定 + ---------------------------------------------------------------*/ + preventDefault(e) { + e.preventDefault() + return false + }, + handleClick(e) { + if (this.loading !== 1) { + if (e.target !== this.$refs.fileinput) { + e.preventDefault() + if (document.activeElement !== this.$refs) { + this.$refs.fileinput.click() + } + } + } + }, + handleChange(e) { + e.preventDefault() + if (this.loading !== 1) { + const files = e.target.files || e.dataTransfer.files + this.reset() + if (this.checkFile(files[0])) { + this.setSourceImg(files[0]) + } + } + }, + /* ---------------------------------------------------------------*/ + // 检测选择的文件是否合适 + checkFile(file) { + let that = this, + { + lang, + maxSize + } = that + // 仅限图片 + if (file.type.indexOf('image') === -1) { + that.hasError = true + that.errorMsg = lang.error.onlyImg + return false + } + // 超出大小 + if (file.size / 1024 > maxSize) { + that.hasError = true + that.errorMsg = lang.error.outOfSize + maxSize + 'kb' + return false + } + return true + }, + // 重置控件 + reset() { + const that = this + that.loading = 0 + that.hasError = false + that.errorMsg = '' + that.progress = 0 + }, + // 设置图片源 + setSourceImg(file) { + let that = this, + fr = new FileReader() + fr.onload = function(e) { + that.sourceImgUrl = fr.result + that.startCrop() + } + fr.readAsDataURL(file) + }, + // 剪裁前准备工作 + startCrop() { + let that = this, + { + width, + height, + ratio, + scale, + sourceImgUrl, + sourceImgMasking, + lang + } = that, + sim = sourceImgMasking, + img = new Image() + img.src = sourceImgUrl + img.onload = function() { + let nWidth = img.naturalWidth, + nHeight = img.naturalHeight, + nRatio = nWidth / nHeight, + w = sim.width, + h = sim.height, + x = 0, + y = 0 + // 图片像素不达标 + if (nWidth < width || nHeight < height) { + that.hasError = true + that.errorMsg = lang.error.lowestPx + width + '*' + height + return false + } + if (ratio > nRatio) { + h = w / nRatio + y = (sim.height - h) / 2 + } + if (ratio < nRatio) { + w = h * nRatio + x = (sim.width - w) / 2 + } + scale.range = 0 + scale.x = x + scale.y = y + scale.width = w + scale.height = h + scale.degree = 0 + scale.minWidth = w + scale.minHeight = h + scale.maxWidth = nWidth * sim.scale + scale.maxHeight = nHeight * sim.scale + scale.naturalWidth = nWidth + scale.naturalHeight = nHeight + that.sourceImg = img + that.createImg() + that.setStep(2) + } + }, + // 鼠标按下图片准备移动 + imgStartMove(e) { + e.preventDefault() + // 支持触摸事件,则鼠标事件无效 + if (this.isSupportTouch && !e.targetTouches) { + return false + } + let et = e.targetTouches ? e.targetTouches[0] : e, + { + sourceImgMouseDown, + scale + } = this, + simd = sourceImgMouseDown + simd.mX = et.screenX + simd.mY = et.screenY + simd.x = scale.x + simd.y = scale.y + simd.on = true + }, + // 鼠标按下状态下移动,图片移动 + imgMove(e) { + e.preventDefault() + // 支持触摸事件,则鼠标事件无效 + if (this.isSupportTouch && !e.targetTouches) { + return false + } + let et = e.targetTouches ? e.targetTouches[0] : e, + { + sourceImgMouseDown: { + on, + mX, + mY, + x, + y + }, + scale, + sourceImgMasking + } = this, + sim = sourceImgMasking, + nX = et.screenX, + nY = et.screenY, + dX = nX - mX, + dY = nY - mY, + rX = x + dX, + rY = y + dY + if (!on) return + if (rX > 0) { + rX = 0 + } + if (rY > 0) { + rY = 0 + } + if (rX < sim.width - scale.width) { + rX = sim.width - scale.width + } + if (rY < sim.height - scale.height) { + rY = sim.height - scale.height + } + scale.x = rX + scale.y = rY + }, + // 按钮按下开始向右旋转 + startRotateRight(e) { + let that = this, + { + scale + } = that + scale.rotateRight = true + function rotate() { + if (scale.rotateRight) { + const degree = ++scale.degree + that.createImg(degree) + setTimeout(function() { + rotate() + }, 60) + } + } + rotate() + }, + // 按钮按下开始向右旋转 + startRotateLeft(e) { + let that = this, + { + scale + } = that + scale.rotateLeft = true + function rotate() { + if (scale.rotateLeft) { + const degree = --scale.degree + that.createImg(degree) + setTimeout(function() { + rotate() + }, 60) + } + } + rotate() + }, + // 停止旋转 + endRotate() { + const { + scale + } = this + scale.rotateLeft = false + scale.rotateRight = false + }, + // 按钮按下开始放大 + startZoomAdd(e) { + let that = this, + { + scale + } = that + scale.zoomAddOn = true + function zoom() { + if (scale.zoomAddOn) { + const range = scale.range >= 100 ? 100 : ++scale.range + that.zoomImg(range) + setTimeout(function() { + zoom() + }, 60) + } + } + zoom() + }, + // 按钮松开或移开取消放大 + endZoomAdd(e) { + this.scale.zoomAddOn = false + }, + // 按钮按下开始缩小 + startZoomSub(e) { + let that = this, + { + scale + } = that + scale.zoomSubOn = true + function zoom() { + if (scale.zoomSubOn) { + const range = scale.range <= 0 ? 0 : --scale.range + that.zoomImg(range) + setTimeout(function() { + zoom() + }, 60) + } + } + zoom() + }, + // 按钮松开或移开取消缩小 + endZoomSub(e) { + const { + scale + } = this + scale.zoomSubOn = false + }, + zoomChange(e) { + this.zoomImg(e.target.value) + }, + // 缩放原图 + zoomImg(newRange) { + const that = this + const { + sourceImgMasking, + sourceImgMouseDown, + scale + } = this + const { + maxWidth, + maxHeight, + minWidth, + minHeight, + width, + height, + x, + y, + range + } = scale + const sim = sourceImgMasking + // 蒙版宽高 + const sWidth = sim.width + const sHeight = sim.height + // 新宽高 + const nWidth = minWidth + (maxWidth - minWidth) * newRange / 100 + const nHeight = minHeight + (maxHeight - minHeight) * newRange / 100 + // 新坐标(根据蒙版中心点缩放) + let nX = sWidth / 2 - (nWidth / width) * (sWidth / 2 - x) + let nY = sHeight / 2 - (nHeight / height) * (sHeight / 2 - y) + // 判断新坐标是否超过蒙版限制 + if (nX > 0) { + nX = 0 + } + if (nY > 0) { + nY = 0 + } + if (nX < sWidth - nWidth) { + nX = sWidth - nWidth + } + if (nY < sHeight - nHeight) { + nY = sHeight - nHeight + } + // 赋值处理 + scale.x = nX + scale.y = nY + scale.width = nWidth + scale.height = nHeight + scale.range = newRange + setTimeout(function() { + if (scale.range == newRange) { + that.createImg() + } + }, 300) + }, + // 生成需求图片 + createImg(e) { + let that = this, + { + mime, + sourceImg, + scale: { + x, + y, + width, + height, + degree + }, + sourceImgMasking: { + scale + } + } = that, + canvas = that.$refs.canvas, + ctx = canvas.getContext('2d') + if (e) { + // 取消鼠标按下移动状态 + that.sourceImgMouseDown.on = false + } + canvas.width = that.width + canvas.height = that.height + ctx.clearRect(0, 0, that.width, that.height) + // 将透明区域设置为白色底边 + ctx.fillStyle = '#fff' + ctx.fillRect(0, 0, that.width, that.height) + ctx.translate(that.width * 0.5, that.height * 0.5) + ctx.rotate(Math.PI * degree / 180) + ctx.translate(-that.width * 0.5, -that.height * 0.5) + ctx.drawImage(sourceImg, x / scale, y / scale, width / scale, height / scale) + that.createImgUrl = canvas.toDataURL(mime) + }, + prepareUpload() { + const { + url, + createImgUrl, + field, + ki + } = this + this.$emit('crop-success', createImgUrl, field, ki) + if (typeof url === 'string' && url) { + this.upload() + } else { + this.off() + } + }, + // 上传图片 + upload() { + let that = this, + { + lang, + imgFormat, + mime, + url, + params, + headers, + field, + ki, + createImgUrl, + withCredentials + } = this, + fmData = new FormData() + fmData.append(field, data2blob(createImgUrl, mime), field + '.' + imgFormat) + // 添加其他参数 + if (typeof params === 'object' && params) { + Object.keys(params).forEach((k) => { + fmData.append(k, params[k]) + }) + } + // 监听进度回调 + const uploadProgress = function(event) { + if (event.lengthComputable) { + that.progress = 100 * Math.round(event.loaded) / event.total + } + } + // 上传文件 + that.reset() + that.loading = 1 + that.setStep(3) + request({ + url, + method: 'post', + data: fmData + }).then(resData => { + that.loading = 2 + that.$emit('crop-upload-success', resData.data) + }).catch(err => { + if (that.value) { + that.loading = 3 + that.hasError = true + that.errorMsg = lang.fail + that.$emit('crop-upload-fail', err, field, ki) + } + }) + } + }, + created() { + // 绑定按键esc隐藏此插件事件 + document.addEventListener('keyup', (e) => { + if (this.value && (e.key == 'Escape' || e.keyCode == 27)) { + this.off() + } + }) + } +} </script> -<style scoped> - @import "./upload.css"; +<!-- +<style lang='sass' src="./scss/upload.scss"> +</style> --> + +<style> +@charset "UTF-8"; +@-webkit-keyframes vicp_progress { + 0% { + background-position-y: 0; } + 100% { + background-position-y: 40px; } } +@keyframes vicp_progress { + 0% { + background-position-y: 0; } + 100% { + background-position-y: 40px; } } +@-webkit-keyframes vicp { + 0% { + opacity: 0; + -webkit-transform: scale(0) translatey(-60px); + transform: scale(0) translatey(-60px); } + 100% { + opacity: 1; + -webkit-transform: scale(1) translatey(0); + transform: scale(1) translatey(0); } } +@keyframes vicp { + 0% { + opacity: 0; + -webkit-transform: scale(0) translatey(-60px); + transform: scale(0) translatey(-60px); } + 100% { + opacity: 1; + -webkit-transform: scale(1) translatey(0); + transform: scale(1) translatey(0); } } +.vue-image-crop-upload { + position: fixed; + display: block; + -webkit-box-sizing: border-box; + box-sizing: border-box; + z-index: 10000; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.65); + -webkit-tap-highlight-color: transparent; + -moz-tap-highlight-color: transparent; } + .vue-image-crop-upload .vicp-wrap { + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + position: fixed; + display: block; + -webkit-box-sizing: border-box; + box-sizing: border-box; + z-index: 10000; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: 600px; + height: 330px; + padding: 25px; + background-color: #fff; + border-radius: 2px; + -webkit-animation: vicp 0.12s ease-in; + animation: vicp 0.12s ease-in; } + .vue-image-crop-upload .vicp-wrap .vicp-close { + position: absolute; + right: -30px; + top: -30px; } + .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4 { + position: relative; + display: block; + width: 30px; + height: 30px; + cursor: pointer; + -webkit-transition: -webkit-transform 0.18s; + transition: -webkit-transform 0.18s; + transition: transform 0.18s; + transition: transform 0.18s, -webkit-transform 0.18s; + -webkit-transform: rotate(0); + -ms-transform: rotate(0); + transform: rotate(0); } + .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after, .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::before { + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + content: ''; + position: absolute; + top: 12px; + left: 4px; + width: 20px; + height: 3px; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + background-color: #fff; } + .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after { + -webkit-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); } + .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4:hover { + -webkit-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area { + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 35px; + height: 170px; + background-color: rgba(0, 0, 0, 0.03); + text-align: center; + border: 1px dashed rgba(0, 0, 0, 0.08); + overflow: hidden; } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 { + display: block; + margin: 0 auto 6px; + width: 42px; + height: 42px; + overflow: hidden; } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-arrow { + display: block; + margin: 0 auto; + width: 0; + height: 0; + border-bottom: 14.7px solid rgba(0, 0, 0, 0.3); + border-left: 14.7px solid transparent; + border-right: 14.7px solid transparent; } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-body { + display: block; + width: 12.6px; + height: 14.7px; + margin: 0 auto; + background-color: rgba(0, 0, 0, 0.3); } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-bottom { + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: block; + height: 12.6px; + border: 6px solid rgba(0, 0, 0, 0.3); + border-top: none; } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-hint { + display: block; + padding: 15px; + font-size: 14px; + color: #666; + line-height: 30px; } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-no-supported-hint { + display: block; + position: absolute; + top: 0; + left: 0; + padding: 30px; + width: 100%; + height: 60px; + line-height: 30px; + background-color: #eee; + text-align: center; + color: #666; + font-size: 14px; } + .vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area:hover { + cursor: pointer; + border-color: rgba(0, 0, 0, 0.1); + background-color: rgba(0, 0, 0, 0.05); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop { + overflow: hidden; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left { + float: left; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container { + position: relative; + display: block; + width: 240px; + height: 180px; + background-color: #e5e5e0; + overflow: hidden; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img { + position: absolute; + display: block; + cursor: move; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade { + -webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); + position: absolute; + background-color: rgba(241, 242, 243, 0.8); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-1 { + top: 0; + left: 0; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-2 { + bottom: 0; + right: 0; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate { + position: relative; + width: 240px; + height: 18px; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i { + display: block; + width: 18px; + height: 18px; + border-radius: 100%; + line-height: 18px; + text-align: center; + font-size: 12px; + font-weight: bold; + background-color: rgba(0, 0, 0, 0.08); + color: #fff; + overflow: hidden; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i:hover { + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + cursor: pointer; + background-color: rgba(0, 0, 0, 0.14); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i:first-child { + float: left; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-rotate i:last-child { + float: right; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range { + position: relative; + margin: 30px 0 10px 0; + width: 240px; + height: 18px; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5, + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 { + position: absolute; + top: 0; + width: 18px; + height: 18px; + border-radius: 100%; + background-color: rgba(0, 0, 0, 0.08); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5:hover, + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6:hover { + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + cursor: pointer; + background-color: rgba(0, 0, 0, 0.14); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5 { + left: 0; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5::before { + position: absolute; + content: ''; + display: block; + left: 3px; + top: 8px; + width: 12px; + height: 2px; + background-color: #fff; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 { + right: 0; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::before { + position: absolute; + content: ''; + display: block; + left: 3px; + top: 8px; + width: 12px; + height: 2px; + background-color: #fff; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::after { + position: absolute; + content: ''; + display: block; + top: 3px; + left: 8px; + width: 2px; + height: 12px; + background-color: #fff; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range] { + display: block; + padding-top: 5px; + margin: 0 auto; + width: 180px; + height: 8px; + vertical-align: top; + background: transparent; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + cursor: pointer; + /* 滑块 + ---------------------------------------------------------------*/ + /* 轨道 + ---------------------------------------------------------------*/ } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus { + outline: none; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-thumb { + -webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); + -webkit-appearance: none; + appearance: none; + margin-top: -3px; + width: 12px; + height: 12px; + background-color: #61c091; + border-radius: 100%; + border: none; + -webkit-transition: 0.2s; + transition: 0.2s; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-thumb { + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); + -moz-appearance: none; + appearance: none; + width: 12px; + height: 12px; + background-color: #61c091; + border-radius: 100%; + border: none; + -webkit-transition: 0.2s; + transition: 0.2s; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-thumb { + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); + appearance: none; + width: 12px; + height: 12px; + background-color: #61c091; + border: none; + border-radius: 100%; + -webkit-transition: 0.2s; + transition: 0.2s; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-moz-range-thumb { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + width: 14px; + height: 14px; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-ms-thumb { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + width: 14px; + height: 14px; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-webkit-slider-thumb { + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); + margin-top: -4px; + width: 14px; + height: 14px; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-runnable-track { + -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + width: 100%; + height: 6px; + cursor: pointer; + border-radius: 2px; + border: none; + background-color: rgba(68, 170, 119, 0.3); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-track { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + width: 100%; + height: 6px; + cursor: pointer; + border-radius: 2px; + border: none; + background-color: rgba(68, 170, 119, 0.3); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-track { + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); + width: 100%; + cursor: pointer; + background: transparent; + border-color: transparent; + color: transparent; + height: 6px; + border-radius: 2px; + border: none; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-lower { + background-color: rgba(68, 170, 119, 0.3); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-upper { + background-color: rgba(68, 170, 119, 0.15); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-webkit-slider-runnable-track { + background-color: rgba(68, 170, 119, 0.5); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-moz-range-track { + background-color: rgba(68, 170, 119, 0.5); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-lower { + background-color: rgba(68, 170, 119, 0.45); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-upper { + background-color: rgba(68, 170, 119, 0.25); } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right { + float: right; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview { + height: 150px; + overflow: hidden; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item { + position: relative; + padding: 5px; + width: 100px; + height: 100px; + float: left; + margin-right: 16px; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item span { + position: absolute; + bottom: -30px; + width: 100%; + font-size: 14px; + color: #bbb; + display: block; + text-align: center; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item img { + position: absolute; + display: block; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + padding: 3px; + background-color: #fff; + border: 1px solid rgba(0, 0, 0, 0.15); + overflow: hidden; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item.vicp-preview-item-circle { + margin-right: 0; } + .vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item.vicp-preview-item-circle img { + border-radius: 100%; } + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload { + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; + padding: 35px; + height: 170px; + background-color: rgba(0, 0, 0, 0.03); + text-align: center; + border: 1px dashed #ddd; } + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-loading { + display: block; + padding: 15px; + font-size: 16px; + color: #999; + line-height: 30px; } + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap { + margin-top: 12px; + background-color: rgba(0, 0, 0, 0.08); + border-radius: 3px; } + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress { + position: relative; + display: block; + height: 5px; + border-radius: 3px; + background-color: #4a7; + -webkit-box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3); + box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3); + -webkit-transition: width 0.15s linear; + transition: width 0.15s linear; + background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); + background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); + background-size: 40px 40px; + -webkit-animation: vicp_progress 0.5s linear infinite; + animation: vicp_progress 0.5s linear infinite; } + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress::after { + content: ''; + position: absolute; + display: block; + top: -3px; + right: -3px; + width: 9px; + height: 9px; + border: 1px solid rgba(245, 246, 247, 0.7); + -webkit-box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7); + box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7); + border-radius: 100%; + background-color: #4a7; } + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-error, + .vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-success { + height: 100px; + line-height: 100px; } + .vue-image-crop-upload .vicp-wrap .vicp-operate { + position: absolute; + right: 20px; + bottom: 20px; } + .vue-image-crop-upload .vicp-wrap .vicp-operate a { + position: relative; + float: left; + display: block; + margin-left: 10px; + width: 100px; + height: 36px; + line-height: 36px; + text-align: center; + cursor: pointer; + font-size: 14px; + color: #4a7; + border-radius: 2px; + overflow: hidden; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + .vue-image-crop-upload .vicp-wrap .vicp-operate a:hover { + background-color: rgba(0, 0, 0, 0.03); } + .vue-image-crop-upload .vicp-wrap .vicp-error, + .vue-image-crop-upload .vicp-wrap .vicp-success { + display: block; + font-size: 14px; + line-height: 24px; + height: 24px; + color: #d10; + text-align: center; + vertical-align: top; } + .vue-image-crop-upload .vicp-wrap .vicp-success { + color: #4a7; } + .vue-image-crop-upload .vicp-wrap .vicp-icon3 { + position: relative; + display: inline-block; + width: 20px; + height: 20px; + top: 4px; } + .vue-image-crop-upload .vicp-wrap .vicp-icon3::after { + position: absolute; + top: 3px; + left: 6px; + width: 6px; + height: 10px; + border-width: 0 2px 2px 0; + border-color: #4a7; + border-style: solid; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); + content: ''; } + .vue-image-crop-upload .vicp-wrap .vicp-icon2 { + position: relative; + display: inline-block; + width: 20px; + height: 20px; + top: 4px; } + .vue-image-crop-upload .vicp-wrap .vicp-icon2::after, .vue-image-crop-upload .vicp-wrap .vicp-icon2::before { + content: ''; + position: absolute; + top: 9px; + left: 4px; + width: 13px; + height: 2px; + background-color: #d10; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); } + .vue-image-crop-upload .vicp-wrap .vicp-icon2::after { + -webkit-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); } +.e-ripple { + position: absolute; + border-radius: 100%; + background-color: rgba(0, 0, 0, 0.15); + background-clip: padding-box; + pointer-events: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-transform: scale(0); + -ms-transform: scale(0); + transform: scale(0); + opacity: 1; } + .e-ripple.z-active { + opacity: 0; + -webkit-transform: scale(2); + -ms-transform: scale(2); + transform: scale(2); + -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; + transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; + transition: opacity 1.2s ease-out, transform 0.6s ease-out; + transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; } </style> diff --git a/src/components/ImageCropper/lang.js b/src/components/ImageCropper/lang.js deleted file mode 100644 index 264f914c..00000000 --- a/src/components/ImageCropper/lang.js +++ /dev/null @@ -1,41 +0,0 @@ -const langBag = { - zh: { - hint: '点击,或拖动图片至此处', - loading: '正在上传……', - noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!', - success: '上传成功', - fail: '图片上传失败', - preview: '头像预览', - btn: { - off: '取消', - close: '关闭', - back: '上一步', - save: '保存' - }, - error: { - onlyImg: '仅限图片格式', - outOfSize: '单文件大小不能超过 ', - lowestPx: '图片最低像素为(宽*高):' - } - }, - en: { - hint: 'Click, or drag the file here', - loading: 'Uploading……', - noSupported: 'Browser does not support, please use IE10+ or other browsers', - success: 'Upload success', - fail: 'Upload failed', - preview: 'Preview', - btn: { - off: 'Cancel', - close: 'Close', - back: 'Back', - save: 'Save' - }, - error: { - onlyImg: 'Image only', - outOfSize: 'Image exceeds size limit: ', - lowestPx: 'The lowest pixel in the image: ' - } - } -} -export default langBag diff --git a/src/components/ImageCropper/upload.css b/src/components/ImageCropper/upload.css deleted file mode 100644 index d01dc40c..00000000 --- a/src/components/ImageCropper/upload.css +++ /dev/null @@ -1,691 +0,0 @@ -@charset "UTF-8"; -@-webkit-keyframes vicp_progress { - 0% { - background-position-y: 0; - } - 100% { - background-position-y: 40px; - } -} - -@keyframes vicp_progress { - 0% { - background-position-y: 0; - } - 100% { - background-position-y: 40px; - } -} - -@-webkit-keyframes vicp { - 0% { - opacity: 0; - -webkit-transform: scale(0) translatey(-60px); - transform: scale(0) translatey(-60px); - } - 100% { - opacity: 1; - -webkit-transform: scale(1) translatey(0); - transform: scale(1) translatey(0); - } -} - -@keyframes vicp { - 0% { - opacity: 0; - -webkit-transform: scale(0) translatey(-60px); - transform: scale(0) translatey(-60px); - } - 100% { - opacity: 1; - -webkit-transform: scale(1) translatey(0); - transform: scale(1) translatey(0); - } -} - -.vue-image-crop-upload { - position: fixed; - display: block; - -webkit-box-sizing: border-box; - box-sizing: border-box; - z-index: 10000; - top: 0; - bottom: 0; - left: 0; - right: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.65); - -webkit-tap-highlight-color: transparent; - -moz-tap-highlight-color: transparent; -} - -.vue-image-crop-upload .vicp-wrap { - -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - position: fixed; - display: block; - -webkit-box-sizing: border-box; - box-sizing: border-box; - z-index: 10000; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; - width: 600px; - height: 330px; - padding: 25px; - background-color: #fff; - border-radius: 2px; - -webkit-animation: vicp 0.12s ease-in; - animation: vicp 0.12s ease-in; -} - -.vue-image-crop-upload .vicp-wrap .vicp-close { - position: absolute; - right: -30px; - top: -30px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4 { - position: relative; - display: block; - width: 30px; - height: 30px; - cursor: pointer; - -webkit-transition: -webkit-transform 0.18s; - transition: -webkit-transform 0.18s; - transition: transform 0.18s; - transition: transform 0.18s, -webkit-transform 0.18s; - -webkit-transform: rotate(0); - -ms-transform: rotate(0); - transform: rotate(0); -} - -.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after, .vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::before { - -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - content: ''; - position: absolute; - top: 12px; - left: 4px; - width: 20px; - height: 3px; - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); - background-color: #fff; -} - -.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4::after { - -webkit-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - transform: rotate(-45deg); -} - -.vue-image-crop-upload .vicp-wrap .vicp-close .vicp-icon4:hover { - -webkit-transform: rotate(90deg); - -ms-transform: rotate(90deg); - transform: rotate(90deg); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area { - position: relative; - padding: 35px; - height: 200px; - background-color: rgba(0, 0, 0, 0.03); - text-align: center; - border: 1px dashed rgba(0, 0, 0, 0.08); - overflow: hidden; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 { - display: block; - margin: 0 auto 6px; - width: 42px; - height: 42px; - overflow: hidden; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-arrow { - display: block; - margin: 0 auto; - width: 0; - height: 0; - border-bottom: 14.7px solid rgba(0, 0, 0, 0.3); - border-left: 14.7px solid transparent; - border-right: 14.7px solid transparent; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-body { - display: block; - width: 12.6px; - height: 14.7px; - margin: 0 auto; - background-color: rgba(0, 0, 0, 0.3); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-icon1 .vicp-icon1-bottom { - -webkit-box-sizing: border-box; - box-sizing: border-box; - display: block; - height: 12.6px; - border: 6px solid rgba(0, 0, 0, 0.3); - border-top: none; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-hint { - display: block; - padding: 15px; - font-size: 14px; - color: #666; - line-height: 30px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area .vicp-no-supported-hint { - display: block; - position: absolute; - top: 0; - left: 0; - padding: 30px; - width: 100%; - height: 60px; - line-height: 30px; - background-color: #eee; - text-align: center; - color: #666; - font-size: 14px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step1 .vicp-drop-area:hover { - cursor: pointer; - border-color: rgba(0, 0, 0, 0.1); - background-color: rgba(0, 0, 0, 0.05); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop { - overflow: hidden; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left { - float: left; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container { - position: relative; - display: block; - width: 240px; - height: 180px; - background-color: #e5e5e0; - overflow: hidden; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img { - position: absolute; - display: block; - cursor: move; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade { - -webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); - position: absolute; - background-color: rgba(241, 242, 243, 0.8); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-1 { - top: 0; - left: 0; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-img-container .vicp-img-shade.vicp-img-shade-2 { - bottom: 0; - right: 0; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range { - position: relative; - margin: 30px 0; - width: 240px; - height: 18px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5, -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 { - position: absolute; - top: 0; - width: 18px; - height: 18px; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.08); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5:hover, -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6:hover { - -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); - cursor: pointer; - background-color: rgba(0, 0, 0, 0.14); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5 { - left: 0; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon5::before { - position: absolute; - content: ''; - display: block; - left: 3px; - top: 8px; - width: 12px; - height: 2px; - background-color: #fff; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6 { - right: 0; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::before { - position: absolute; - content: ''; - display: block; - left: 3px; - top: 8px; - width: 12px; - height: 2px; - background-color: #fff; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range .vicp-icon6::after { - position: absolute; - content: ''; - display: block; - top: 3px; - left: 8px; - width: 2px; - height: 12px; - background-color: #fff; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range] { - display: block; - padding-top: 5px; - margin: 0 auto; - width: 180px; - height: 8px; - vertical-align: top; - background: transparent; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - cursor: pointer; - /* 滑块 - ---------------------------------------------------------------*/ - /* 轨道 - ---------------------------------------------------------------*/ -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus { - outline: none; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-thumb { - -webkit-box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); - -webkit-appearance: none; - appearance: none; - margin-top: -3px; - width: 12px; - height: 12px; - background-color: #61c091; - border-radius: 100%; - border: none; - -webkit-transition: 0.2s; - transition: 0.2s; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-thumb { - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); - -moz-appearance: none; - appearance: none; - width: 12px; - height: 12px; - background-color: #61c091; - border-radius: 100%; - border: none; - -webkit-transition: 0.2s; - transition: 0.2s; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-thumb { - box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.18); - appearance: none; - width: 12px; - height: 12px; - background-color: #61c091; - border: none; - border-radius: 100%; - -webkit-transition: 0.2s; - transition: 0.2s; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-moz-range-thumb { - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - width: 14px; - height: 14px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-ms-thumb { - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - width: 14px; - height: 14px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:active::-webkit-slider-thumb { - -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.23); - margin-top: -4px; - width: 14px; - height: 14px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-webkit-slider-runnable-track { - -webkit-box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); - width: 100%; - height: 6px; - cursor: pointer; - border-radius: 2px; - border: none; - background-color: rgba(68, 170, 119, 0.3); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-moz-range-track { - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); - width: 100%; - height: 6px; - cursor: pointer; - border-radius: 2px; - border: none; - background-color: rgba(68, 170, 119, 0.3); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-track { - box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12); - width: 100%; - cursor: pointer; - background: transparent; - border-color: transparent; - color: transparent; - height: 6px; - border-radius: 2px; - border: none; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-lower { - background-color: rgba(68, 170, 119, 0.3); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]::-ms-fill-upper { - background-color: rgba(68, 170, 119, 0.15); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-webkit-slider-runnable-track { - background-color: rgba(68, 170, 119, 0.5); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-moz-range-track { - background-color: rgba(68, 170, 119, 0.5); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-lower { - background-color: rgba(68, 170, 119, 0.45); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-left .vicp-range input[type=range]:focus::-ms-fill-upper { - background-color: rgba(68, 170, 119, 0.25); -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right { - float: right; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview { - height: 150px; - overflow: hidden; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item { - position: relative; - padding: 5px; - width: 100px; - height: 100px; - float: left; - margin-right: 16px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item span { - position: absolute; - bottom: -30px; - width: 100%; - font-size: 14px; - color: #bbb; - display: block; - text-align: center; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item img { - position: absolute; - display: block; - top: 0; - bottom: 0; - left: 0; - right: 0; - margin: auto; - padding: 3px; - background-color: #fff; - border: 1px solid rgba(0, 0, 0, 0.15); - overflow: hidden; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item:last-child { - margin-right: 0; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step2 .vicp-crop .vicp-crop-right .vicp-preview .vicp-preview-item:last-child img { - border-radius: 100%; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload { - position: relative; - padding: 35px; - height: 200px; - background-color: rgba(0, 0, 0, 0.03); - text-align: center; - border: 1px dashed #ddd; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-loading { - display: block; - padding: 15px; - font-size: 16px; - color: #999; - line-height: 30px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap { - margin-top: 12px; - background-color: rgba(0, 0, 0, 0.08); - border-radius: 3px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress { - position: relative; - display: block; - height: 5px; - border-radius: 3px; - background-color: #4a7; - -webkit-box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3); - box-shadow: 0 2px 6px 0 rgba(68, 170, 119, 0.3); - -webkit-transition: width 0.15s linear; - transition: width 0.15s linear; - background-image: -webkit-linear-gradient(135deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); - background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent); - background-size: 40px 40px; - -webkit-animation: vicp_progress 0.5s linear infinite; - animation: vicp_progress 0.5s linear infinite; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-progress-wrap .vicp-progress::after { - content: ''; - position: absolute; - display: block; - top: -3px; - right: -3px; - width: 9px; - height: 9px; - border: 1px solid rgba(245, 246, 247, 0.7); - -webkit-box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7); - box-shadow: 0 1px 4px 0 rgba(68, 170, 119, 0.7); - border-radius: 100%; - background-color: #4a7; -} - -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-error, -.vue-image-crop-upload .vicp-wrap .vicp-step3 .vicp-upload .vicp-success { - height: 100px; - line-height: 100px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-operate { - position: absolute; - right: 20px; - bottom: 20px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-operate a { - position: relative; - float: left; - display: block; - margin-left: 10px; - width: 100px; - height: 36px; - line-height: 36px; - text-align: center; - cursor: pointer; - font-size: 14px; - color: #4a7; - border-radius: 2px; - overflow: hidden; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.vue-image-crop-upload .vicp-wrap .vicp-operate a:hover { - background-color: rgba(0, 0, 0, 0.03); -} - -.vue-image-crop-upload .vicp-wrap .vicp-error, -.vue-image-crop-upload .vicp-wrap .vicp-success { - display: block; - font-size: 14px; - line-height: 24px; - height: 24px; - color: #d10; - text-align: center; - vertical-align: top; -} - -.vue-image-crop-upload .vicp-wrap .vicp-success { - color: #4a7; -} - -.vue-image-crop-upload .vicp-wrap .vicp-icon3 { - position: relative; - display: inline-block; - width: 20px; - height: 20px; - top: 4px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-icon3::after { - position: absolute; - top: 3px; - left: 6px; - width: 6px; - height: 10px; - border-width: 0 2px 2px 0; - border-color: #4a7; - border-style: solid; - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); - content: ''; -} - -.vue-image-crop-upload .vicp-wrap .vicp-icon2 { - position: relative; - display: inline-block; - width: 20px; - height: 20px; - top: 4px; -} - -.vue-image-crop-upload .vicp-wrap .vicp-icon2::after, .vue-image-crop-upload .vicp-wrap .vicp-icon2::before { - content: ''; - position: absolute; - top: 9px; - left: 4px; - width: 13px; - height: 2px; - background-color: #d10; - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); -} - -.vue-image-crop-upload .vicp-wrap .vicp-icon2::after { - -webkit-transform: rotate(-45deg); - -ms-transform: rotate(-45deg); - transform: rotate(-45deg); -} - -.e-ripple { - position: absolute; - border-radius: 100%; - background-color: rgba(0, 0, 0, 0.15); - background-clip: padding-box; - pointer-events: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-transform: scale(0); - -ms-transform: scale(0); - transform: scale(0); - opacity: 1; -} - -.e-ripple.z-active { - opacity: 0; - -webkit-transform: scale(2); - -ms-transform: scale(2); - transform: scale(2); - -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; - transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out; - transition: opacity 1.2s ease-out, transform 0.6s ease-out; - transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out; -} \ No newline at end of file diff --git a/src/components/ImageCropper/utils.js b/src/components/ImageCropper/utils.js deleted file mode 100644 index 0ccd3af0..00000000 --- a/src/components/ImageCropper/utils.js +++ /dev/null @@ -1,58 +0,0 @@ -/* eslint-disable */ - -/** - * - * @param e - * @param arg_opts - * @returns {boolean} - */ -export function effectRipple(e, arg_opts) { - let opts = Object.assign({ - ele: e.target, // 波纹作用元素 - type: 'hit', // hit点击位置扩散 center中心点扩展 - bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 - }, arg_opts), - target = opts.ele; - if (target) { - let rect = target.getBoundingClientRect(), - ripple = target.querySelector('.e-ripple'); - if (!ripple) { - ripple = document.createElement('span'); - ripple.className = 'e-ripple'; - ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'; - target.appendChild(ripple); - } else { - ripple.className = 'e-ripple'; - } - switch (opts.type) { - case 'center': - ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'; - ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'; - break; - default: - ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'; - ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'; - } - ripple.style.backgroundColor = opts.bgc; - ripple.className = 'e-ripple z-active'; - return false; - } -} -// database64文件格式转换为2进制 -/** - * - * @param data - * @param mime - * @returns {*} - */ -export function data2blob(data, mime) { - // dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 - data = data.split(',')[1]; - data = window.atob(data); - var ia = new Uint8Array(data.length); - for (var i = 0; i < data.length; i++) { - ia[i] = data.charCodeAt(i); - } - // canvas.toDataURL 返回的默认格式就是 image/png - return new Blob([ia], {type: mime}); -}; diff --git a/src/components/ImageCropper/utils/data2blob.js b/src/components/ImageCropper/utils/data2blob.js new file mode 100755 index 00000000..9c47f8af --- /dev/null +++ b/src/components/ImageCropper/utils/data2blob.js @@ -0,0 +1,19 @@ +/** + * database64文件格式转换为2进制 + * + * @param {[String]} data dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了 + * @param {[String]} mime [description] + * @return {[blob]} [description] + */ +export default function(data, mime) { + data = data.split(',')[1] + data = window.atob(data) + var ia = new Uint8Array(data.length) + for (var i = 0; i < data.length; i++) { + ia[i] = data.charCodeAt(i) + } + // canvas.toDataURL 返回的默认格式就是 image/png + return new Blob([ia], { + type: mime + }) +} diff --git a/src/components/ImageCropper/utils/effectRipple.js b/src/components/ImageCropper/utils/effectRipple.js new file mode 100755 index 00000000..46a01640 --- /dev/null +++ b/src/components/ImageCropper/utils/effectRipple.js @@ -0,0 +1,39 @@ +/** + * 点击波纹效果 + * + * @param {[event]} e [description] + * @param {[Object]} arg_opts [description] + * @return {[bollean]} [description] + */ +export default function(e, arg_opts) { + var opts = Object.assign({ + ele: e.target, // 波纹作用元素 + type: 'hit', // hit点击位置扩散center中心点扩展 + bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色 + }, arg_opts) + var target = opts.ele + if (target) { + var rect = target.getBoundingClientRect() + var ripple = target.querySelector('.e-ripple') + if (!ripple) { + ripple = document.createElement('span') + ripple.className = 'e-ripple' + ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px' + target.appendChild(ripple) + } else { + ripple.className = 'e-ripple' + } + switch (opts.type) { + case 'center': + ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px' + ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px' + break + default: + ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px' + ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px' + } + ripple.style.backgroundColor = opts.bgc + ripple.className = 'e-ripple z-active' + return false + } +} diff --git a/src/components/ImageCropper/utils/language.js b/src/components/ImageCropper/utils/language.js new file mode 100755 index 00000000..727872d9 --- /dev/null +++ b/src/components/ImageCropper/utils/language.js @@ -0,0 +1,232 @@ +export default { + zh: { + hint: '点击,或拖动图片至此处', + loading: '正在上传……', + noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!', + success: '上传成功', + fail: '图片上传失败', + preview: '头像预览', + btn: { + off: '取消', + close: '关闭', + back: '上一步', + save: '保存' + }, + error: { + onlyImg: '仅限图片格式', + outOfSize: '单文件大小不能超过 ', + lowestPx: '图片最低像素为(宽*高):' + } + }, + 'zh-tw': { + hint: '點擊,或拖動圖片至此處', + loading: '正在上傳……', + noSupported: '瀏覽器不支持該功能,請使用IE10以上或其他現代瀏覽器!', + success: '上傳成功', + fail: '圖片上傳失敗', + preview: '頭像預覽', + btn: { + off: '取消', + close: '關閉', + back: '上一步', + save: '保存' + }, + error: { + onlyImg: '僅限圖片格式', + outOfSize: '單文件大小不能超過 ', + lowestPx: '圖片最低像素為(寬*高):' + } + }, + en: { + hint: 'Click or drag the file here to upload', + loading: 'Uploading…', + noSupported: 'Browser is not supported, please use IE10+ or other browsers', + success: 'Upload success', + fail: 'Upload failed', + preview: 'Preview', + btn: { + off: 'Cancel', + close: 'Close', + back: 'Back', + save: 'Save' + }, + error: { + onlyImg: 'Image only', + outOfSize: 'Image exceeds size limit: ', + lowestPx: 'Image\'s size is too low. Expected at least: ' + } + }, + ro: { + hint: 'Atinge sau trage fișierul aici', + loading: 'Se încarcă', + noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.', + success: 'S-a încărcat cu succes', + fail: 'A apărut o problemă la încărcare', + preview: 'Previzualizează', + + btn: { + off: 'Anulează', + close: 'Închide', + back: 'Înapoi', + save: 'Salvează' + }, + + error: { + onlyImg: 'Doar imagini', + outOfSize: 'Imaginea depășește limita de: ', + loewstPx: 'Imaginea este prea mică; Minim: ' + } + }, + ru: { + hint: 'Нажмите, или перетащите файл в это окно', + loading: 'Загружаю……', + noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры', + success: 'Загрузка выполнена успешно', + fail: 'Ошибка загрузки', + preview: 'Предпросмотр', + btn: { + off: 'Отменить', + close: 'Закрыть', + back: 'Назад', + save: 'Сохранить' + }, + error: { + onlyImg: 'Только изображения', + outOfSize: 'Изображение превышает предельный размер: ', + lowestPx: 'Минимальный размер изображения: ' + } + }, + 'pt-br': { + hint: 'Clique ou arraste o arquivo aqui para carregar', + loading: 'Carregando…', + noSupported: 'Browser não suportado, use o IE10+ ou outro browser', + success: 'Sucesso ao carregar imagem', + fail: 'Falha ao carregar imagem', + preview: 'Pré-visualizar', + btn: { + off: 'Cancelar', + close: 'Fechar', + back: 'Voltar', + save: 'Salvar' + }, + error: { + onlyImg: 'Apenas imagens', + outOfSize: 'A imagem excede o limite de tamanho: ', + lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: ' + } + }, + fr: { + hint: 'Cliquez ou glissez le fichier ici.', + loading: 'Téléchargement…', + noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.', + success: 'Téléchargement réussit', + fail: 'Téléchargement echoué', + preview: 'Aperçu', + btn: { + off: 'Annuler', + close: 'Fermer', + back: 'Retour', + save: 'Enregistrer' + }, + error: { + onlyImg: 'Image uniquement', + outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ', + lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: ' + } + }, + nl: { + hint: 'Klik hier of sleep een afbeelding in dit vlak', + loading: 'Uploaden…', + noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.', + success: 'Upload succesvol', + fail: 'Upload mislukt', + preview: 'Voorbeeld', + btn: { + off: 'Annuleren', + close: 'Sluiten', + back: 'Terug', + save: 'Opslaan' + }, + error: { + onlyImg: 'Alleen afbeeldingen', + outOfSize: 'De afbeelding is groter dan: ', + lowestPx: 'De afbeelding is te klein! Minimale afmetingen: ' + } + }, + tr: { + hint: 'Tıkla veya yüklemek istediğini buraya sürükle', + loading: 'Yükleniyor…', + noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın', + success: 'Yükleme başarılı', + fail: 'Yüklemede hata oluştu', + preview: 'Önizle', + btn: { + off: 'İptal', + close: 'Kapat', + back: 'Geri', + save: 'Kaydet' + }, + error: { + onlyImg: 'Sadece resim', + outOfSize: 'Resim yükleme limitini aşıyor: ', + lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: ' + } + }, + 'es-MX': { + hint: 'Selecciona o arrastra una imagen', + loading: 'Subiendo...', + noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes', + success: 'Subido exitosamente', + fail: 'Sucedió un error', + preview: 'Vista previa', + btn: { + off: 'Cancelar', + close: 'Cerrar', + back: 'Atras', + save: 'Guardar' + }, + error: { + onlyImg: 'Unicamente imagenes', + outOfSize: 'La imagen excede el tamaño maximo:', + lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:' + } + }, + de: { + hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen', + loading: 'Hochladen…', + noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser', + success: 'Upload erfolgreich', + fail: 'Upload fehlgeschlagen', + preview: 'Vorschau', + btn: { + off: 'Abbrechen', + close: 'Schließen', + back: 'Zurück', + save: 'Speichern' + }, + error: { + onlyImg: 'Nur Bilder', + outOfSize: 'Das Bild ist zu groß: ', + lowestPx: 'Das Bild ist zu klein. Mindestens: ' + } + }, + ja: { + hint: 'クリック・ドラッグしてファイルをアップロード', + loading: 'アップロード中...', + noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。', + success: 'アップロード成功', + fail: 'アップロード失敗', + preview: 'プレビュー', + btn: { + off: 'キャンセル', + close: '閉じる', + back: '戻る', + save: '保存' + }, + error: { + onlyImg: '画像のみ', + outOfSize: '画像サイズが上限を超えています。上限: ', + lowestPx: '画像が小さすぎます。最小サイズ: ' + } + } +} diff --git a/src/components/ImageCropper/utils/mimes.js b/src/components/ImageCropper/utils/mimes.js new file mode 100755 index 00000000..e20c085c --- /dev/null +++ b/src/components/ImageCropper/utils/mimes.js @@ -0,0 +1,7 @@ +export default { + 'jpg': 'image/jpeg', + 'png': 'image/png', + 'gif': 'image/gif', + 'svg': 'image/svg+xml', + 'psd': 'image/photoshop' +}