<template> 
 | 
    <view class="bt-container" :style="[containerStyle]"> 
 | 
        <!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
        <view class="mainContent" data-type="image" @touchstart="wxsModule.touchStart" @touchmove="wxsModule.touchMove" @touchend="wxsModule.touchEnd"> 
 | 
        <!-- #endif --> 
 | 
        <!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
        <view class="mainContent" data-type="image" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd"> 
 | 
        <!-- #endif --> 
 | 
            <template v-if="imageRect && cropperRect"> 
 | 
                <!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
                <image mode="aspectFit" :src="imageSrc" class="image" :rotateAngle="rotateAngle" :change:rotateAngle="wxsModule.changeRotateAngle" :change:imageRect="wxsModule.changeImageRect" :imageRect="imageRect" :class="{ anim }"> 
 | 
                <!-- #endif --> 
 | 
                <!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
                <image mode="aspectFit" :src="imageSrc" class="image" :style="[imageStyle]" :class="{ anim }"> 
 | 
                <!-- #endif --> 
 | 
                </image> 
 | 
                <!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
                <view class="cropper" :class="{ anim }" :change:cropperRect="wxsModule.changeCropper" :cropperRect="cropperRect" :change:ratio="wxsModule.changeRatio" :ratio="ratio" > 
 | 
                <!-- #endif --> 
 | 
                <!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
                <view class="cropper" :class="{ anim }"  :style="[cropperStyle]"> 
 | 
                <!-- #endif --> 
 | 
                    <image class="mask" :src="mask"></image> 
 | 
                    <template v-if="showGrid"> 
 | 
                        <view class="line row row1"></view> 
 | 
                        <view class="line row row2"></view> 
 | 
                        <view class="line col col1"></view> 
 | 
                        <view class="line col col2"></view> 
 | 
                    </template> 
 | 
                    <!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5  --> 
 | 
                    <view class="controller vertical left" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller vertical right" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller horizon top" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller horizon bottom" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller left top" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller left bottom" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller right top" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <view class="controller right bottom" @touchstart="wxsModule.touchStart" data-type="controller" /> 
 | 
                    <!-- #endif --> 
 | 
                    <!-- #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5  --> 
 | 
                    <view class="controller vertical left" @touchstart.stop="touchStart(-1, 0, $event)" /> 
 | 
                    <view class="controller vertical right" @touchstart.stop="touchStart(1, 0, $event)" /> 
 | 
                    <view class="controller horizon top" @touchstart.stop="touchStart(0, -1, $event)" /> 
 | 
                    <view class="controller horizon bottom" @touchstart.stop="touchStart(0, 1, $event)" /> 
 | 
                    <view class="controller left top" @touchstart.stop="touchStart(-1, -1, $event)" /> 
 | 
                    <view class="controller left bottom" @touchstart.stop="touchStart(-1, 1, $event)" /> 
 | 
                    <view class="controller right top" @touchstart.stop="touchStart(1, -1, $event)" /> 
 | 
                    <view class="controller right bottom" @touchstart.stop="touchStart(1, 1, $event)" /> 
 | 
                    <!-- #endif --> 
 | 
                </view> 
 | 
            </template> 
 | 
        </view> 
 | 
            <canvas v-if="type2d" type="2d" class="bt-canvas" :width="target.width" :height="target.height"></canvas> 
 | 
            <canvas 
 | 
                v-else 
 | 
                :canvas-id="canvasId" 
 | 
                class="bt-canvas" 
 | 
                :style="{ 
 | 
                    width: target.width + 'px', 
 | 
                    height: target.height + 'px' 
 | 
                }" 
 | 
                :width="target.width * pixel" 
 | 
                :height="target.height * pixel" 
 | 
            ></canvas> 
 | 
    </view> 
 | 
</template> 
 | 
  
 | 
<script> 
 | 
// #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 
 | 
import touches from './js/touchs.js'; 
 | 
// #endif 
 | 
import { parseUnit,sleep } from './utils/tools.js'; 
 | 
/** 
 | 
 * better-cropper 图片裁切插件 
 | 
 */ 
 | 
export default { 
 | 
    name: 'bt-cropper', 
 | 
    props: { 
 | 
        // 图片路径,支持网络路径和本地路径 
 | 
        imageSrc: { 
 | 
            type: String, 
 | 
            default: '', 
 | 
            required: true 
 | 
        }, 
 | 
        mask: { 
 | 
            type: String, 
 | 
            default: '' 
 | 
        }, 
 | 
        // 手动指定容器的大小 
 | 
        containerSize: { 
 | 
            type: Object, 
 | 
            default: null 
 | 
        }, 
 | 
        // 输出图片的格式,默认jpg 
 | 
        fileType: { 
 | 
            type: String, 
 | 
            default: 'png' 
 | 
        }, 
 | 
        // 生成的图片的宽度,不传或者传0表示按照原始分辨率裁切 
 | 
        dWidth: Number, 
 | 
        maxWidth: { 
 | 
            type: Number, 
 | 
            default: 2000 
 | 
        }, 
 | 
        // 裁切比例,0表示自由 
 | 
        ratio: { 
 | 
            type: Number, 
 | 
            default: 0, 
 | 
            validator(value) { 
 | 
                if (typeof value === 'number') { 
 | 
                    if (value < 0) { 
 | 
                        return false; 
 | 
                    } 
 | 
                } else { 
 | 
                    return false; 
 | 
                } 
 | 
                return true; 
 | 
            } 
 | 
        }, 
 | 
        // 旋转角度 
 | 
        rotate: Number, 
 | 
        // 是否展示网格 
 | 
        showGrid: { 
 | 
            type: Boolean, 
 | 
            default: false 
 | 
        }, 
 | 
        // 图片质量,0-1 越大质量越好 
 | 
        quality: { 
 | 
            type: Number, 
 | 
            default: 1 
 | 
        }, 
 | 
        canvas2d: { 
 | 
            type: Boolean, 
 | 
            default: false 
 | 
        }, 
 | 
        // 初始的图片位置 
 | 
        initPosition: { 
 | 
            type: Object, 
 | 
            default() { 
 | 
                return null; 
 | 
            } 
 | 
        }, 
 | 
        // 是否开启操作结束后自动放大 
 | 
        autoZoom: { 
 | 
            type: Boolean, 
 | 
            default: true 
 | 
        } 
 | 
    }, 
 | 
    // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5 
 | 
    mixins: [touches], 
 | 
    // #endif 
 | 
    data() { 
 | 
        return { 
 | 
            canvasId: 'bt-cropper', 
 | 
            containerRect: null, 
 | 
            imageInfo: null, 
 | 
            operationHistory: [], 
 | 
            operationIndex: 0, 
 | 
            anim: false, 
 | 
            timer: null, 
 | 
            // 是否使用2D canvas 
 | 
            type2d: false, 
 | 
            pixel: 1, 
 | 
            imageRect: null, 
 | 
            cropperRect: null, 
 | 
            target:{ 
 | 
                width:0, 
 | 
                height:0 
 | 
            } 
 | 
        }; 
 | 
    }, 
 | 
    watch: { 
 | 
        imageSrc: { 
 | 
            handler(src) { 
 | 
                if (typeof src === 'string' && src !== '') { 
 | 
                    this.imageInit(src); 
 | 
                } else { 
 | 
                    this.imageInfo = null; 
 | 
                } 
 | 
            }, 
 | 
            immediate: true 
 | 
        }, 
 | 
        ratio() { 
 | 
            if (this.ratio != 0) { 
 | 
                this.startAnim(); 
 | 
                this.init(); 
 | 
            } 
 | 
        } 
 | 
    }, 
 | 
    computed: { 
 | 
        containerStyle() { 
 | 
            if (this.containerSize && this.containerRect) { 
 | 
                return { 
 | 
                    width: this.containerRect.width + 'px', 
 | 
                    height: this.containerRect.height + 'px' 
 | 
                }; 
 | 
            } 
 | 
            return {}; 
 | 
        }, 
 | 
        rotateAngle(){ 
 | 
            const angel = Number(this.rotate) 
 | 
            if(isNaN(angel)){ 
 | 
                return 0 
 | 
            }else{ 
 | 
                return angel % 360 
 | 
            } 
 | 
        } 
 | 
    }, 
 | 
    methods: { 
 | 
        startAnim() { 
 | 
            this.stopAnim(); 
 | 
            this.anim = true; 
 | 
            this.timer = setTimeout(() => { 
 | 
                this.anim = false; 
 | 
            }, 200); 
 | 
        }, 
 | 
        stopAnim() { 
 | 
            this.anim = false; 
 | 
            clearTimeout(this.timer); 
 | 
        }, 
 | 
        imageInit(src) { 
 | 
            uni.showLoading({ 
 | 
                title: '载入中...' 
 | 
            }); 
 | 
            uni.getImageInfo({ 
 | 
                src, 
 | 
                success: res => { 
 | 
                    this.imageInfo = res; 
 | 
                    this.$nextTick(() => { 
 | 
                        this.getContainer().then(rect => { 
 | 
                            this.containerRect = rect; 
 | 
                            this.init(); 
 | 
                        }); 
 | 
                    }); 
 | 
                }, 
 | 
                fail: (err) => { 
 | 
                    this.$emit('loadFail',err); 
 | 
                    uni.showToast({ 
 | 
                        title: '图片下载失败!', 
 | 
                        icon: 'none' 
 | 
                    }); 
 | 
                }, 
 | 
                complete(res) { 
 | 
                    uni.hideLoading(); 
 | 
                } 
 | 
            }); 
 | 
        }, 
 | 
        initCropper() { 
 | 
            const imageRate = this.imageInfo.width / this.imageInfo.height; 
 | 
            const containerRate = this.containerRect.width / this.containerRect.height; 
 | 
            const imageRect = {}; 
 | 
            let cropperRate = this.ratio; 
 | 
            if (cropperRate == 0) { 
 | 
                if (this.cropperRect) { 
 | 
                    cropperRate = this.cropperRect.width / this.cropperRect.height; 
 | 
                } else { 
 | 
                    cropperRate = 1; 
 | 
                } 
 | 
            } 
 | 
            const cropperRect = {}; 
 | 
            if (containerRate > cropperRate) { 
 | 
                cropperRect.height = this.containerRect.height * 0.85; 
 | 
                cropperRect.width = cropperRect.height * cropperRate; 
 | 
            } else { 
 | 
                cropperRect.width = this.containerRect.width * 0.85; 
 | 
                cropperRect.height = cropperRect.width / cropperRate; 
 | 
            } 
 | 
            if (cropperRate > imageRate) { 
 | 
                imageRect.width = cropperRect.width; 
 | 
                imageRect.height = imageRect.width / imageRate; 
 | 
            } else { 
 | 
                imageRect.height = cropperRect.height; 
 | 
                imageRect.width = imageRect.height * imageRate; 
 | 
            } 
 | 
            imageRect.left = (this.containerRect.width - imageRect.width) / 2; 
 | 
            imageRect.top = (this.containerRect.height - imageRect.height) / 2; 
 | 
            cropperRect.left = imageRect.left + (imageRect.width - cropperRect.width) / 2; 
 | 
            cropperRect.top = imageRect.top + (imageRect.height - cropperRect.height) / 2; 
 | 
            return { 
 | 
                imageRect, 
 | 
                cropperRect 
 | 
            }; 
 | 
        }, 
 | 
        init() { 
 | 
            const {imageRect,cropperRect} = this.initCropper() 
 | 
            if(this.initPosition){ 
 | 
                const scale = this.imageInfo.width/imageRect.width; 
 | 
                const {left,top,width,height} = this.initPosition; 
 | 
                if(left!==undefined&&top!==undefined&&width!==undefined&&height!==undefined){ 
 | 
                    cropperRect.width = width/scale; 
 | 
                    cropperRect.height = height/scale; 
 | 
                    cropperRect.left = left/scale; 
 | 
                    cropperRect.top = top/scale; 
 | 
                    this.$nextTick(this.zoomToFill); 
 | 
                } 
 | 
            } 
 | 
            this.imageRect = imageRect 
 | 
            this.cropperRect = cropperRect 
 | 
            this.operationHistory = [{imageRect,cropperRect}]; 
 | 
            this.operationIndex = 0; 
 | 
            this.setTarget(); 
 | 
            // #ifdef MP-WEIXIN 
 | 
            const systemInfo = uni.getSystemInfoSync(); 
 | 
            if (this.canvas2d === false || systemInfo.platform === 'windows' || systemInfo.platform === 'mac') { 
 | 
                this.type2d = false; 
 | 
            } else { 
 | 
                this.type2d = true; 
 | 
                this.pixel = systemInfo.pixelRatio; 
 | 
            } 
 | 
            // #endif 
 | 
            //非微信小程序端强制关闭canvas2d模式 
 | 
            // #ifndef MP-WEIXIN 
 | 
            this.type2d = false; 
 | 
            // #endif 
 | 
            // #ifdef  MP-TOUTIAO || MP-LARK || MP-ALIPAY 
 | 
            this.type2d = this.canvas2d; 
 | 
            // #endif 
 | 
        }, 
 | 
        // 设置目标图像的大小 
 | 
        setTarget(){ 
 | 
            const ratio = this.cropperRect.width / this.cropperRect.height; 
 | 
            if (!!this.dWidth) { 
 | 
                this.target = { 
 | 
                    width: this.dWidth, 
 | 
                    height: this.dWidth / (ratio || 1) 
 | 
                } 
 | 
            } else { 
 | 
                const width = Math.min(this.maxWidth, this.cropperRect.width * (this.imageInfo.width / this.imageRect.width)); 
 | 
                this.target = { 
 | 
                    width, 
 | 
                    height: width / (ratio || 1) 
 | 
                } 
 | 
            } 
 | 
        }, 
 | 
        addHistory({imageRect,cropperRect}){ 
 | 
            if(this.operationIndex!==this.operationHistory.length-1){ 
 | 
                this.operationHistory = this.operationHistory.slice(0,this.operationIndex) 
 | 
            } 
 | 
            this.operationHistory.push({ 
 | 
                imageRect, 
 | 
                cropperRect 
 | 
            }); 
 | 
            if (this.operationHistory.length > 10) { 
 | 
                this.operationHistory.shift(); 
 | 
            } 
 | 
            this.operationIndex = this.operationHistory.length - 1; 
 | 
        }, 
 | 
        updateData(data) { 
 | 
            this.imageRect = data.imageRect 
 | 
            this.cropperRect = data.cropperRect 
 | 
            this.addHistory(data); 
 | 
            this.setTarget(); 
 | 
            if (this.autoZoom) { 
 | 
                this.timer = setTimeout(() => { 
 | 
                    this.zoomToFill(); 
 | 
                }, 600); 
 | 
            } 
 | 
            const {imageRect,cropperRect} = data 
 | 
            const scale = imageRect.width/this.imageInfo.width 
 | 
            this.$emit('change', { 
 | 
                left: (cropperRect.left - imageRect.left) /scale, 
 | 
                top: (cropperRect.top - imageRect.top) / scale, 
 | 
                width: cropperRect.width / scale, 
 | 
                height: cropperRect.height / scale 
 | 
            }); 
 | 
        }, 
 | 
        getContainer() { 
 | 
            if (this.containerSize !== null && typeof this.containerSize == 'object') { 
 | 
                const { width, height } = this.containerSize; 
 | 
                return Promise.resolve({ 
 | 
                    width: parseUnit(width), 
 | 
                    height: parseUnit(height) 
 | 
                }); 
 | 
            } else { 
 | 
                return new Promise(resolve => { 
 | 
                    const query = uni.createSelectorQuery().in(this); 
 | 
                    query 
 | 
                        .select('.mainContent') 
 | 
                        .boundingClientRect(rect => { 
 | 
                            resolve(rect); 
 | 
                        }) 
 | 
                        .exec(); 
 | 
                }); 
 | 
            } 
 | 
        }, 
 | 
        zoomToFill() { 
 | 
            this.startAnim(); 
 | 
            const beforeCropper = { 
 | 
                ...this.cropperRect 
 | 
            }; 
 | 
            const operation = { 
 | 
                imageRect:this.imageRect, 
 | 
                cropperRect:this.cropperRect 
 | 
            }; 
 | 
            this.cropperRect = this.initCropper().cropperRect; 
 | 
            const scale = this.cropperRect.width / beforeCropper.width; 
 | 
            const ox = beforeCropper.left - this.imageRect.left; 
 | 
            const oy = beforeCropper.top - this.imageRect.top; 
 | 
            this.imageRect = { 
 | 
                width: this.imageRect.width * scale, 
 | 
                height: this.imageRect.height * scale, 
 | 
                left: this.imageRect.left + (this.cropperRect.left - beforeCropper.left) - (scale - 1) * ox, 
 | 
                top: this.imageRect.top + (this.cropperRect.top - beforeCropper.top) - (scale - 1) * oy 
 | 
            }; 
 | 
        }, 
 | 
        onTouchStart() { 
 | 
            this.stopAnim(); 
 | 
        }, 
 | 
        // 撤销 
 | 
        undo() { 
 | 
            if (this.operationIndex > 0) { 
 | 
                this.operationIndex--; 
 | 
                this.imageRect = this.operationHistory[this.operationIndex].imageRect; 
 | 
                this.cropperRect = this.operationHistory[this.operationIndex].cropperRect; 
 | 
                return true; 
 | 
            } 
 | 
            return false; 
 | 
        }, 
 | 
        // 重做 
 | 
        resume() { 
 | 
            if (this.operationIndex < this.operationHistory.length - 1) { 
 | 
                this.operationIndex++; 
 | 
                this.imageRect = this.operationHistory[this.operationIndex].imageRect; 
 | 
                this.cropperRect = this.operationHistory[this.operationIndex].cropperRect; 
 | 
                return true; 
 | 
            } 
 | 
            return false; 
 | 
        }, 
 | 
        async drawImage(ctx,image,x,y,w,h){ 
 | 
            if (this.type2d) { 
 | 
                await new Promise(resolve => (image.onload = resolve)); 
 | 
                ctx.drawImage(image, x * this.pixel, y * this.pixel, w * this.pixel, h * this.pixel); 
 | 
            } else { 
 | 
                const path = await new Promise((resolve)=>{ 
 | 
                    uni.getImageInfo({ 
 | 
                        src:image, 
 | 
                        success({path}){ 
 | 
                            resolve(path) 
 | 
                        } 
 | 
                    }) 
 | 
                }) 
 | 
                ctx.drawImage(path, x * this.pixel, y * this.pixel, w * this.pixel, h * this.pixel); 
 | 
                await new Promise((resolve) => ctx.draw(false,resolve)); 
 | 
            } 
 | 
        }, 
 | 
        async crop() { 
 | 
            let ctx; 
 | 
            let canvas; 
 | 
            this.$emit('cropStart') 
 | 
            this.setTarget() 
 | 
            if (this.type2d) { 
 | 
                const query = uni.createSelectorQuery().in(this); 
 | 
                canvas = await new Promise(resolve => 
 | 
                    query 
 | 
                        .select('.bt-canvas') 
 | 
                        .node(({ node }) => resolve(node)) 
 | 
                        .exec() 
 | 
                ); 
 | 
                canvas.width = this.target.width * this.pixel; 
 | 
                canvas.height = this.target.height * this.pixel; 
 | 
                ctx = canvas.getContext('2d'); 
 | 
                // #ifdef MP-TOUTIAO 
 | 
                if(this.type2d){ 
 | 
                    console.warn("请注意:目前头条系小程序暂时无法使用2d canvas保存图片,建议换成V1版本") 
 | 
                } 
 | 
                // #endif 
 | 
            } else { 
 | 
                ctx = uni.createCanvasContext(this.canvasId,this); 
 | 
            } 
 | 
            const scale = this.cropperRect.width / this.target.width; 
 | 
            const dx = (this.cropperRect.left - this.imageRect.left) / scale; 
 | 
            const dy = (this.cropperRect.top - this.imageRect.top) / scale; 
 | 
            let image; 
 | 
            if(this.type2d){ 
 | 
                image = canvas.createImage() 
 | 
                image.src = this.imageSrc; 
 | 
            }else{ 
 | 
                image = this.imageSrc; 
 | 
            } 
 | 
            const x = -dx, 
 | 
                y = -dy, 
 | 
                w = this.imageRect.width / scale, 
 | 
                h = this.imageRect.height / scale; 
 | 
            ctx.save(); 
 | 
            ctx.translate(x + w / 2, y + h / 2); 
 | 
            ctx.rotate((this.rotateAngle * Math.PI) / 180); 
 | 
            ctx.translate(-(x + w / 2), -(y + h / 2)); 
 | 
            await this.drawImage(ctx,image, x, y, w, h); 
 | 
            ctx.restore(); 
 | 
            if (this.mask !== '') { 
 | 
                let imageData; 
 | 
                if (this.type2d) { 
 | 
                    imageData = ctx.getImageData(0, 0, this.target.width, this.target.height); 
 | 
                } else { 
 | 
                    imageData = await new Promise(resolve => { 
 | 
                        uni.canvasGetImageData({ 
 | 
                            canvasId: this.canvasId, 
 | 
                            x: 0, 
 | 
                            y: 0, 
 | 
                            width: this.target.width, 
 | 
                            height: this.target.height, 
 | 
                            success(res) { 
 | 
                                resolve(res); 
 | 
                            } 
 | 
                        },this); 
 | 
                    }); 
 | 
                } 
 | 
                ctx.clearRect(0, 0, this.target.width, this.target.height); 
 | 
                if(this.type2d){ 
 | 
                    image.src = this.mask; 
 | 
                }else{ 
 | 
                    image = this.mask; 
 | 
                } 
 | 
                await this.drawImage(ctx,image, 0, 0, this.target.width, this.target.height); 
 | 
                let maskData; 
 | 
                if (this.type2d) { 
 | 
                    maskData = ctx.getImageData(0, 0, this.target.width, this.target.height); 
 | 
                } else { 
 | 
                    maskData = await new Promise((resolve,reject) => { 
 | 
                        uni.canvasGetImageData({ 
 | 
                            canvasId: this.canvasId, 
 | 
                            x: 0, 
 | 
                            y: 0, 
 | 
                            width: this.target.width, 
 | 
                            height: this.target.height, 
 | 
                            success(res) { 
 | 
                                resolve(res); 
 | 
                            } 
 | 
                        },this); 
 | 
                    }); 
 | 
                } 
 | 
                ctx.clearRect(0, 0, this.target.width, this.target.height); 
 | 
                for (let index = 3; index < maskData.data.length; index += 4) { 
 | 
                    const alpha = maskData.data[index]; 
 | 
                    if (alpha !== 0) { 
 | 
                        imageData.data[index] = 0; 
 | 
                    } 
 | 
                } 
 | 
                if (this.type2d) { 
 | 
                    ctx.putImageData(imageData, 0, 0); 
 | 
                } else { 
 | 
                    await new Promise(resolve => { 
 | 
                        uni.canvasPutImageData({ 
 | 
                            canvasId: this.canvasId, 
 | 
                            x: 0, 
 | 
                            y: 0, 
 | 
                            width: imageData.width, 
 | 
                            height: imageData.height, 
 | 
                            data: imageData.data, 
 | 
                            complete: res => { 
 | 
                                resolve(res); 
 | 
                            } 
 | 
                        },this); 
 | 
                    }); 
 | 
                } 
 | 
            } 
 | 
            return new Promise((resolve,reject) => { 
 | 
                const params = {}; 
 | 
                if (this.type2d) { 
 | 
                    params.canvas = canvas; 
 | 
                } else { 
 | 
                    params.canvasId = this.canvasId; 
 | 
                } 
 | 
                 
 | 
                uni.canvasToTempFilePath({ 
 | 
                    ...params, 
 | 
                    destWidth: this.target.width, 
 | 
                    destHeight: this.target.height, 
 | 
                    quality: Number(this.quality) || 1, 
 | 
                    fileType: this.fileType, 
 | 
                    success: ({ tempFilePath }) => { 
 | 
                        // #ifdef H5 
 | 
                        var arr = tempFilePath.split(','); 
 | 
                        var mime = arr[0].match(/:(.*?);/)[1]; 
 | 
                        var bstr = atob(arr[1]); 
 | 
                        var n = bstr.length; 
 | 
                        var u8arr = new Uint8Array(n); 
 | 
                        for (var i = 0; i < n; i++) { 
 | 
                            u8arr[i] = bstr.charCodeAt(i); 
 | 
                        } 
 | 
                        var url = URL || webkitURL; 
 | 
                        resolve( 
 | 
                            url.createObjectURL( 
 | 
                                new Blob([u8arr], { 
 | 
                                    type: mime 
 | 
                                }) 
 | 
                            ) 
 | 
                        ); 
 | 
                        // #endif 
 | 
                        resolve(tempFilePath); 
 | 
                    }, 
 | 
                    fail(err) { 
 | 
                        console.log('保存失败,错误信息:',err); 
 | 
                        reject(err); 
 | 
                    } 
 | 
                },this); 
 | 
            }); 
 | 
        } 
 | 
    } 
 | 
}; 
 | 
</script> 
 | 
<!-- #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5 --> 
 | 
<script module="wxsModule" lang="wxs"> 
 | 
var startTouchs = []; 
 | 
var startDistance = 0; 
 | 
var touchCenter = []; 
 | 
var ratio = 0; 
 | 
var imageInstance = null; 
 | 
var cropperInstance = null; 
 | 
var touchType = ""; 
 | 
var touchInstance = null; 
 | 
var cropperRect = null; 
 | 
var imageRect = null; 
 | 
// 旋转角度 
 | 
var rotateAngle = 0; 
 | 
// 操作时改变的对象 
 | 
var changes = { 
 | 
    imageRect: null, 
 | 
    cropperRect: null 
 | 
} 
 | 
  
 | 
function updateImageStyle() { 
 | 
    var imageRect = changes.imageRect 
 | 
    imageInstance.setStyle({ 
 | 
        left: imageRect.left + 'px', 
 | 
        top: imageRect.top + 'px', 
 | 
        width: imageRect.width + 'px', 
 | 
        height: imageRect.height + 'px', 
 | 
        transform: 'rotate(' + rotateAngle + 'deg)' 
 | 
    }) 
 | 
} 
 | 
  
 | 
function updateCopperStyle() { 
 | 
    var cropperRect = changes.cropperRect 
 | 
    cropperInstance.setStyle({ 
 | 
        left: cropperRect.left + "px", 
 | 
        top: cropperRect.top + "px", 
 | 
        width: cropperRect.width + "px", 
 | 
        height: cropperRect.height + "px" 
 | 
    }) 
 | 
} 
 | 
  
 | 
function imageScale(scaleRate) { 
 | 
    var cw = imageRect.width * (scaleRate - 1) 
 | 
    var ch = imageRect.height * (scaleRate - 1) 
 | 
    changes.imageRect = { 
 | 
        width: imageRect.width + cw, 
 | 
        height: imageRect.height + ch, 
 | 
        left: imageRect.left - cw * (touchCenter[0]), 
 | 
        top: imageRect.top - ch * (touchCenter[1]) 
 | 
    } 
 | 
} 
 | 
function getImageRotateSizeChange(w,h){ 
 | 
    var cw = h*Math.sin(rotateAngle/180 * Math.PI) 
 | 
    var ch = w*Math.sin(rotateAngle/180 * Math.PI) 
 | 
    return {cw,ch} 
 | 
} 
 | 
// 角度转弧度 
 | 
function ang2deg(ang){ 
 | 
    return ang/180*Math.PI 
 | 
} 
 | 
// 计算旋转后真实的图片大小 
 | 
function getRealSize(){ 
 | 
    var w = changes.imageRect.width 
 | 
    var h = changes.imageRect.height 
 | 
    var l =  changes.imageRect.left 
 | 
    var t =  changes.imageRect.top 
 | 
    // 内斜边 
 | 
    var R = Math.sqrt(w*w+h*h) 
 | 
    var angle = Math.atan(h/w) / Math.PI * 180 
 | 
    var rorate = rotateAngle%90 
 | 
    var direct = Math.floor(rotateAngle/90) 
 | 
    var width = R*Math.cos(ang2deg(angle-rorate)) 
 | 
    var height = R*Math.sin(ang2deg(angle+rorate)) 
 | 
    if(direct % 2 === 1){ 
 | 
        var temp = width 
 | 
        width = height 
 | 
        height = temp 
 | 
    } 
 | 
    return { 
 | 
        width: width, 
 | 
        height: height, 
 | 
        left: l - (width - w)/2, 
 | 
        top: t - (height - h)/2, 
 | 
        dw: width - w, 
 | 
        dh: height - h 
 | 
    } 
 | 
} 
 | 
module.exports = { 
 | 
    touchStart: function (ev, oi) { 
 | 
        // #ifdef APP-PLUS || H5 
 | 
        ev.preventDefault(); 
 | 
        ev.stopPropagation(); 
 | 
        // #endif 
 | 
        touchInstance = ev.instance; 
 | 
        var dataSet = ev.instance.getDataset() 
 | 
        touchType = dataSet.type; 
 | 
        startTouchs = ev.touches; 
 | 
        oi.callMethod('onTouchStart') 
 | 
        if (startTouchs.length == 2) { 
 | 
            touchType = "image" 
 | 
            var x1 = startTouchs[0].clientX 
 | 
            var y1 = startTouchs[0].clientY 
 | 
            var x2 = startTouchs[1].clientX 
 | 
            var y2 = startTouchs[1].clientY 
 | 
            var distance = Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2) 
 | 
            startDistance = Math.sqrt(distance) 
 | 
            var leftPercent = ((x1 + x2) / 2 - imageRect.left) / imageRect.width 
 | 
            var topPercent = ((y1 + y2) / 2 - imageRect.top) / imageRect.height 
 | 
            touchCenter = [leftPercent, topPercent] 
 | 
        } 
 | 
        return false; 
 | 
    }, 
 | 
    touchMove: function (ev, io) { 
 | 
        if (touchType == "") return false 
 | 
        // #ifdef H5 
 | 
        ev.preventDefault(); 
 | 
        ev.stopPropagation(); 
 | 
        // #endif 
 | 
        var touches = ev.touches; 
 | 
        var changeX1 = touches[0].clientX - startTouchs[0].clientX; 
 | 
        var changeY1 = touches[0].clientY - startTouchs[0].clientY; 
 | 
        if (startTouchs.length == 1) { 
 | 
            if (touchType === 'image') { 
 | 
                changes.imageRect.left = imageRect.left + changeX1; 
 | 
                changes.imageRect.top = imageRect.top + changeY1; 
 | 
                updateImageStyle() 
 | 
            } else if (touchType === 'controller') { 
 | 
                var directionX = 0; 
 | 
                if (touchInstance.hasClass('left')) { 
 | 
                    directionX = -1; 
 | 
                } 
 | 
                if (touchInstance.hasClass('right')) { 
 | 
                    directionX = 1; 
 | 
                } 
 | 
                var directionY = 0; 
 | 
                if (touchInstance.hasClass('top')) { 
 | 
                    directionY = -1 
 | 
                } 
 | 
                if (touchInstance.hasClass('bottom')) { 
 | 
                    directionY = 1 
 | 
                } 
 | 
                var changeX = changeX1 * directionX; 
 | 
                var changeY = changeY1 * directionY; 
 | 
                // 比例缩放控制 
 | 
                if (ratio !== 0) { 
 | 
                    if (directionX * directionY !== 0) { 
 | 
                        if (changeX / ratio > changeY) { 
 | 
                            changeY = changeX / ratio 
 | 
                            changeX = changeY * ratio 
 | 
                        } else { 
 | 
                            changeX = changeY * ratio 
 | 
                            changeY = changeX / ratio 
 | 
                        } 
 | 
                    } else { 
 | 
                        if (directionX == 0) { 
 | 
                            changeX = changeY * ratio 
 | 
                        } else { 
 | 
                            changeY = changeX / ratio 
 | 
                        } 
 | 
                    } 
 | 
                } 
 | 
                var realSize = getRealSize() 
 | 
                var width = cropperRect.width + changeX 
 | 
                var height = cropperRect.height + changeY 
 | 
                // var imageRight = imageRect.left + imageRect.width 
 | 
                // var imageBottom = imageRect.top + imageRect.height 
 | 
                var imageRight = realSize.left+realSize.width 
 | 
                var imageBottom = realSize.top+realSize.height 
 | 
                if (directionX != -1) { 
 | 
                    if (cropperRect.left + width > imageRight) { 
 | 
                        width = imageRight - cropperRect.left 
 | 
                        if (ratio !== 0) { 
 | 
                            height = width / ratio 
 | 
                        } 
 | 
                    } 
 | 
                } else { 
 | 
                    var cLeft = cropperRect.left - changeX 
 | 
                    if (cLeft < realSize.left) { 
 | 
                        width = cropperRect.left + cropperRect.width - realSize.left 
 | 
                        if (ratio !== 0) { 
 | 
                            height = width / ratio 
 | 
                        } 
 | 
                    } 
 | 
                } 
 | 
                // 判断是否触底 
 | 
                if (directionY != -1) { 
 | 
                    if (cropperRect.top + height > imageBottom) { 
 | 
                        height = imageBottom - cropperRect.top 
 | 
                        if (ratio !== 0) { 
 | 
                            width = height * ratio 
 | 
                        } 
 | 
                    } 
 | 
                } else { 
 | 
                    var cTop = cropperRect.top - changeY 
 | 
                    if (cTop < realSize.top) { 
 | 
                        height = cropperRect.top + cropperRect.height - realSize.top 
 | 
                        if (ratio !== 0) { 
 | 
                            width = height * ratio 
 | 
                        } 
 | 
                    } 
 | 
                } 
 | 
                if (directionX == -1) { 
 | 
                    changes.cropperRect.left = cropperRect.left + cropperRect.width - width 
 | 
                } 
 | 
                if (directionY == -1) { 
 | 
                    changes.cropperRect.top = cropperRect.top + cropperRect.height - height 
 | 
                } 
 | 
                // 边界控制 
 | 
                changes.cropperRect.width = width 
 | 
                changes.cropperRect.height = height 
 | 
                updateCopperStyle() 
 | 
            } 
 | 
        } else if (touches.length == 2 && startTouchs.length == 2) { 
 | 
            var changeX2 = touches[0].clientX - touches[1].clientX; 
 | 
            var changeY2 = touches[0].clientY - touches[1].clientY; 
 | 
            var distance = Math.pow(changeX2, 2) + Math.pow(changeY2, 2) 
 | 
            distance = Math.sqrt(distance) 
 | 
            // 放大比例 
 | 
            var scaleRate = distance / startDistance 
 | 
            imageScale(scaleRate) 
 | 
            updateImageStyle() 
 | 
        } 
 | 
        return false; 
 | 
    }, 
 | 
    touchEnd: function (ev, oi) { 
 | 
        if (touchType === "image") { 
 | 
            var cropperLeft = cropperRect.left 
 | 
            var cropperRight = cropperRect.left + cropperRect.width 
 | 
            var cropperTop = cropperRect.top 
 | 
            var cropperBottom = cropperTop + cropperRect.height 
 | 
            var cropperRate = cropperRect.width / cropperRect.height 
 | 
            var realSize = getRealSize() 
 | 
            var rate = realSize.width / realSize.height 
 | 
            if (realSize.width < cropperRect.width || realSize.height < cropperRect.height) { 
 | 
                var scale = 1 
 | 
                if (rate < cropperRate) { 
 | 
                    scale = cropperRect.width / realSize.width 
 | 
                } else { 
 | 
                    scale = cropperRect.height / realSize.height 
 | 
                } 
 | 
                imageRect.width = changes.imageRect.width 
 | 
                imageRect.height = changes.imageRect.height 
 | 
                imageScale(scale) 
 | 
            } 
 | 
            // 边界控制start 
 | 
            if (cropperLeft < realSize.left) { 
 | 
                changes.imageRect.left = cropperLeft + realSize.dw/2 
 | 
            } 
 | 
            if (cropperRight > realSize.left + realSize.width) { 
 | 
                changes.imageRect.left = cropperRight - realSize.width + realSize.dw/2 
 | 
            } 
 | 
            if (cropperTop < realSize.top) { 
 | 
                changes.imageRect.top = cropperTop + realSize.dh/2 
 | 
            } 
 | 
            if (cropperBottom > realSize.top + realSize.height) { 
 | 
                changes.imageRect.top = cropperBottom - realSize.height + realSize.dh/2 
 | 
            } 
 | 
            // 边界控制end 
 | 
            updateImageStyle() 
 | 
        } 
 | 
        oi.callMethod('updateData', { 
 | 
            cropperRect: changes.cropperRect, 
 | 
            imageRect: changes.imageRect, 
 | 
        }) 
 | 
        touchType = "" 
 | 
        startTouchs = [] 
 | 
        return false; 
 | 
    }, 
 | 
    // 将逻辑层的图像变换同步过来 
 | 
    // 裁剪比例变化 
 | 
    changeRatio: function (value) { 
 | 
        ratio = value 
 | 
    }, 
 | 
    changeRotateAngle:function (value){ 
 | 
        rotateAngle = value; 
 | 
        if(imageInstance){ 
 | 
            updateImageStyle() 
 | 
        } 
 | 
        var realSize = getRealSize() 
 | 
    }, 
 | 
    changeImageRect: function (value, oldValue, oi) { 
 | 
        if (value) { 
 | 
            imageRect = value; 
 | 
            changes.imageRect = { 
 | 
                left: value.left, 
 | 
                top: value.top, 
 | 
                width: value.width, 
 | 
                height: value.height 
 | 
            }; 
 | 
            // #ifndef MP-WEIXIN || MP-QQ 
 | 
            setTimeout(function() { 
 | 
                imageInstance = oi.selectComponent('.mainContent > .image') 
 | 
                updateImageStyle(); 
 | 
            }); 
 | 
            // #endif 
 | 
            // #ifdef MP-WEIXIN || MP-QQ 
 | 
            imageInstance = oi.selectComponent('.mainContent > .image') 
 | 
            updateImageStyle(); 
 | 
            // #endif 
 | 
        } 
 | 
    }, 
 | 
    changeCropper: function (value, oldValue, oi) { 
 | 
        if (value) { 
 | 
            cropperRect = value 
 | 
            changes.cropperRect = { 
 | 
                left: value.left, 
 | 
                top: value.top, 
 | 
                width: value.width, 
 | 
                height: value.height 
 | 
            } 
 | 
            // #ifdef H5 || APP-VUE 
 | 
            setTimeout(function() { 
 | 
            // #endif 
 | 
                cropperInstance = oi.selectComponent('.mainContent > .cropper') 
 | 
                updateCopperStyle() 
 | 
            // #ifdef H5 || APP-VUE 
 | 
            }); 
 | 
            // #endif 
 | 
        } 
 | 
    } 
 | 
} 
 | 
</script> 
 | 
<!-- #endif --> 
 | 
<style lang="scss" scoped> 
 | 
.bt-container { 
 | 
    // display: flex; 
 | 
    // flex-direction: column; 
 | 
    // justify-content: space-between; 
 | 
    height: 100%; 
 | 
    box-sizing: border-box; 
 | 
    // background-color: #0e1319; 
 | 
    position: relative; 
 | 
    overflow: hidden; 
 | 
  
 | 
    .bt-canvas { 
 | 
        position: fixed; 
 | 
        left: 100%; 
 | 
        top: 0; 
 | 
    } 
 | 
  
 | 
    .mainContent { 
 | 
        // flex: 1; 
 | 
        // flex-shrink:0; 
 | 
        // margin: 60rpx; 
 | 
        width: 100%; 
 | 
        height: 100%; 
 | 
        position: relative; 
 | 
        display: flex; 
 | 
        justify-content: center; 
 | 
        align-items: center; 
 | 
        // touch-action: none; 
 | 
  
 | 
        .image { 
 | 
            position: absolute; 
 | 
            // will-change: transform; 
 | 
            // transform-origin: center center; 
 | 
            width: 85%; 
 | 
            height: 85%; 
 | 
            will-change: left, top, width, height; 
 | 
        } 
 | 
  
 | 
        .controller { 
 | 
            position: absolute; 
 | 
            z-index: 99; 
 | 
            padding: 10rpx; 
 | 
            $offset: -20rpx; 
 | 
  
 | 
            &::after { 
 | 
                display: block; 
 | 
                content: ''; 
 | 
                filter: drop-shadow(0 0px 10rpx rgba(0, 0, 0, 0.3)); 
 | 
            } 
 | 
  
 | 
            &.vertical { 
 | 
                top: calc(50% - 30rpx); 
 | 
            } 
 | 
  
 | 
            &.horizon { 
 | 
                left: calc(50% - 30rpx); 
 | 
            } 
 | 
  
 | 
            &.left { 
 | 
                &::after { 
 | 
                    height: 40rpx; 
 | 
                    border-left: 10rpx solid #fff; 
 | 
                } 
 | 
  
 | 
                left: $offset; 
 | 
            } 
 | 
  
 | 
            &.right { 
 | 
                &::after { 
 | 
                    height: 40rpx; 
 | 
                    border-right: 10rpx solid #fff; 
 | 
                } 
 | 
  
 | 
                right: $offset; 
 | 
            } 
 | 
  
 | 
            &.top { 
 | 
                top: $offset; 
 | 
  
 | 
                &::after { 
 | 
                    width: 40rpx; 
 | 
                    border-top: 10rpx solid #fff; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            &.bottom { 
 | 
                bottom: $offset; 
 | 
  
 | 
                &::after { 
 | 
                    width: 40rpx; 
 | 
                    border-bottom: 10rpx solid #fff; 
 | 
                } 
 | 
            } 
 | 
  
 | 
            &.left.bottom, 
 | 
            &.right.bottom, 
 | 
            &.left.top, 
 | 
            &.left.bottom { 
 | 
                &::after { 
 | 
                    width: 30rpx; 
 | 
                    height: 30rpx; 
 | 
                    background-color: transparent; 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        .cropper { 
 | 
            position: absolute; 
 | 
            border: 1px solid #eee; 
 | 
            box-sizing: content-box; 
 | 
            // transform-origin: center center; 
 | 
            outline: 999px solid rgba(0, 0, 0, 0.5); 
 | 
            will-change: left, top, width, height; 
 | 
  
 | 
            // display: contain; 
 | 
            // pointer-events: none; 
 | 
            .mask { 
 | 
                position: absolute; 
 | 
                left: 0; 
 | 
                top: 0; 
 | 
                width: 100%; 
 | 
                height: 100%; 
 | 
                opacity: 0.5; 
 | 
            } 
 | 
  
 | 
            .line { 
 | 
                position: absolute; 
 | 
                // background-color: #eee; 
 | 
            } 
 | 
  
 | 
            .row { 
 | 
                width: 100%; 
 | 
                height: 0px; 
 | 
                left: 0; 
 | 
                border-top: 1px dashed #007aff; 
 | 
            } 
 | 
  
 | 
            .col { 
 | 
                height: 100%; 
 | 
                width: 0px; 
 | 
                border-left: 1px dashed #007aff; 
 | 
            } 
 | 
  
 | 
            .row1 { 
 | 
                top: 33%; 
 | 
            } 
 | 
  
 | 
            .row2 { 
 | 
                top: 66%; 
 | 
            } 
 | 
  
 | 
            .col1 { 
 | 
                left: 33%; 
 | 
            } 
 | 
  
 | 
            .col2 { 
 | 
                left: 66%; 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    // .slot { 
 | 
    //     position: relative; 
 | 
    //     padding-top: 20rpx; 
 | 
    // } 
 | 
} 
 | 
  
 | 
.anim { 
 | 
    transition: 0.2s; 
 | 
} 
 | 
</style> 
 |