<template>
<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" 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"
							@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-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 class="vicp-rotate" v-if="!noRotate">
                        <i @click="rotateImg">↻</i>
                    </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>
'use strict';
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'
        },
		// 图片背景 jpg情况下生效
        imgBgc: {
            type: String,
            'default': '#fff'
        },
		// 是否支持跨域
        withCredentials: {
            type: Boolean,
            'default': false
        }
    },
    data() {
        let that = this,
            {
				imgFormat,
				langType,
				langExt,
				width,
				height
			} = that,
            isSupported = true,
            allowImgFormat = [
                'jpg',
                'png'
            ],
            tempImgFormat = allowImgFormat.indexOf(imgFormat) === -1 ? 'jpg' : imgFormat,
            lang = language[langType] ? language[langType] : language['en'],
            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

                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,
                top = scale.y + sourceImgMasking.y + 'px',
                left = scale.x + sourceImgMasking.x + 'px';
            return {
                top,
                left,
                width: scale.width + 'px',
                height: scale.height + 'px',// 兼容 Opera
            };
        },
		// 原图蒙版属性
        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 {
					sourceImgMasking,
					sourceImgContainer
				} = this,
                sic = sourceImgContainer,
                sim = 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'
            };
        }
    },
    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) {
                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;
            }

			// 超出大小
            if (file.size / 1024 > maxSize) {
                that.hasError = true;
                that.errorMsg = lang.error.outOfSize + maxSize + 'kb';
                return false;
            }
            return true;
        },
		// 重置控件
        reset() {
            let 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.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;
        },
		// 顺时针旋转图片
        rotateImg(e) {
            let {
					sourceImg,
                    scale: {
						naturalWidth,
						naturalHeight,
                    }
                } = this,
                width = naturalHeight,
                height = naturalWidth,
                canvas = this.$refs.canvas,
                ctx = canvas.getContext('2d');
            canvas.width = width;
            canvas.height = height;
            ctx.clearRect(0, 0, width, height);

            ctx.fillStyle = 'rgba(0,0,0,0)';
            ctx.fillRect(0, 0, width, height);

            ctx.translate(width, 0);
            ctx.rotate(Math.PI * 90 / 180);

            ctx.drawImage(sourceImg, 0, 0, naturalWidth, naturalHeight);
            let imgUrl = canvas.toDataURL(mimes['png']);

            this.sourceImgUrl = imgUrl;
            this.startCrop();
        },

		// 按钮按下开始放大
        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,
                {
					imgFormat,
					imgBgc,
                    mime,
                    sourceImg,
                    scale: {
                        x,
                        y,
                        width,
                        height,
                    },
                    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);

            if(imgFormat == 'png'){
                ctx.fillStyle = 'rgba(0,0,0,0)';
            } else{
				// 如果jpg 为透明区域设置背景，默认白色
                ctx.fillStyle = imgBgc;
            }
            ctx.fillRect(0, 0, that.width, that.height);

            ctx.drawImage(sourceImg, x / scale, y / scale, width / scale, height / scale);
            that.createImgUrl = canvas.toDataURL(mime);
        },
        prepareUpload(){
            let {
				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);
            new Promise(function(resolve, reject) {
                let client = new XMLHttpRequest();
                client.open('POST', url, true);
                client.withCredentials = withCredentials;
                client.onreadystatechange = function() {
                    if (this.readyState !== 4) {
                        return;
                    }
                    if (this.status === 200 || this.status === 201) {
                        resolve(JSON.parse(this.responseText));
                    } else {
                        reject(this.status);
                    }
                };
                client.upload.addEventListener('progress', uploadProgress, false); //监听进度
				// 设置header
                if (typeof headers == 'object' && headers) {
                    Object.keys(headers).forEach((k) => {
                        client.setRequestHeader(k, headers[k]);
                    });
                }
                client.send(fmData);
            }).then(
				// 上传成功
				function(resData) {
    if (that.value) {
        that.loading = 2;
        that.$emit('crop-upload-success', resData, field, ki);
    }
},
				// 上传失败
				function(sts) {
    if (that.value) {
        that.loading = 3;
        that.hasError = true;
        that.errorMsg = lang.fail;
        that.$emit('crop-upload-fail', sts, field, ki);
    }
}
			);
        }
    },
    created(){
		// 绑定按键esc隐藏此插件事件
        document.addEventListener('keyup', (e)=>{
            if(this.value && (e.key == 'Escape' || e.keyCode == 27)){
                this.off();
            }
        });
    }
};

</script>

<!--
<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>
