yuanwyj / Mini-Program-cropper

小程序图片剪裁
https://www.cnblogs.com/yuanzp/p/9268861.html
67 stars 17 forks source link

已加入wepy 版本的裁剪 #3

Open w19871029 opened 5 years ago

w19871029 commented 5 years ago

image image 帮忙看看是不是写的有问题

w19871029 commented 5 years ago

希望增加一个wepy 版本的

w19871029 commented 5 years ago

问题已解决 wepy 版本 裁剪 子组件

<style lang="less" scoped>
    .wpyContainer {
        position: relative;
        width: 100%;
        height: 100%;
        background-color:#000;
    }
    .img {
        position: absolute;
        top:5%;
        left: 50%;
        transform: translateX(-50%);
        overflow: hidden;
        background:white;
    }
    .img image {
        height:400px;
    }
    .imgcrop {
        position: absolute;
        left: -50000rpx;
        top: -500000rpx;
    }
    .footer {
        position: absolute;
        width: 100%;
        height: 110rpx;
        color: #fff;
        background: #000;
        bottom: 0;
        display: flex;
        align-items: center;
        justify-content: space-around;
    }
    .footer view {
        width: 30%;
        text-align: center;
    }
    .background {
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        z-index: -1;
    }
</style>
<template>
    <view class="wpyContainer">
        <view class="img" style="width:{{ width }}px; height:{{height}}px" catchtouchstart="touchstartCallback"  catchtouchmove="touchmoveCallback" catchtouchend="touchendCallback"  >
            <image style="transform: translate({{stv.offsetX}}px, {{stv.offsetY}}px) scale({{stv.scale}}) rotate({{ stv.rotate }}deg);width:{{originImg.width}}px; height: {{originImg.height}}px" mode="aspectFit" src="{{ originImg.url }}"></image>
        </view>
        <view class='footer'>
            <view bindtap='uploadTap'>选择图片</view>
            <view bindtap='rotate'>旋转</view>
            <view bindtap='cropperImg'>裁剪</view>
        </view>
        <!--  canvas长宽设为初始图片设置的长款的两倍,使剪裁得到的图片更清晰,也不至于过大  -->
        <canvas class='imgcrop' style="width:{{ width * 2 }}px;height:{{ height * 2}}px;" canvas-id='imgcrop'></canvas>
    </view>
</template>
<script>
    import wepy from 'wepy';
    const device = wx.getSystemInfoSync();
    var twoPoint = {
        x1: 0,
        y1: 0,
        x2: 0,
        y2: 0
    }
    export default class Cropper extends wepy.component {
        data={
            width: device.windowWidth,                //剪裁框的宽度
            height: device.windowWidth, //剪裁框的长度
            originImg:null,                                //存放原图信息
            stv: {
                offsetX: 0,                                   //剪裁图片左上角坐标x
                offsetY: 0,                                   //剪裁图片左上角坐标y
                zoom: false,                                  //是否缩放状态
                distance: 0,                                  //两指距离
                scale: 1,                                     //缩放倍数
                rotate: 0                                     //旋转角度
            }
        };
        initImg(url) {
            let _this = this;
            wx.getImageInfo({
                src: url,
                success(resopne) {
                    console.log(resopne,"参数值");
                    let innerAspectRadio = resopne.width / resopne.height;
                    console.log(innerAspectRadio,_this.data.width,_this.data.height)
                    if (innerAspectRadio < _this.data.width / _this.data.height) {
                        _this.originImg= {
                            url: url,
                            width: _this.width,
                            height: _this.width / innerAspectRadio
                        };
                        _this.stv= {
                            offsetX: 0,
                            offsetY: 0 - Math.abs((_this.height - _this.width / innerAspectRadio) / 2),
                            zoom: false, //是否缩放状态
                            distance: 0,  //两指距离
                            scale: 1,  //缩放倍数
                            rotate: 0
                        };
                    } else {
                        _this.originImg={
                            url: url,
                            height: _this.height,
                            width: _this.height * innerAspectRadio
                        };
                        _this.stv={
                            offsetX: 0 - Math.abs((_this.width - _this.height * innerAspectRadio) / 2),
                            offsetY: 0,
                            zoom: false, //是否缩放状态
                            distance: 0,  //两指距离
                            scale: 1,  //缩放倍数
                            rotate: 0
                        };
                    }
                    _this.$apply();
                }
            })
        };
        methods = {
            //事件处理函数
            touchstartCallback(e) {
                if (e.touches.length === 1) {
                    let { clientX, clientY } = e.touches[0];
                    this.startX = clientX;
                    this.startY = clientY;
                    this.touchStartEvent = e.touches;
                } else {
                    let xMove = e.touches[1].clientX - e.touches[0].clientX;
                    let yMove = e.touches[1].clientY - e.touches[0].clientY;
                    let distance = Math.sqrt(xMove * xMove + yMove * yMove);
                    twoPoint.x1 = e.touches[0].pageX * 2
                    twoPoint.y1 = e.touches[0].pageY * 2
                    twoPoint.x2 = e.touches[1].pageX * 2
                    twoPoint.y2 = e.touches[1].pageY * 2
                    this.setData({
                        'stv.distance': distance,
                        'stv.zoom': true, //缩放状态
                    })
                }
            },
        //图片手势动态缩放
        touchmoveCallback(e) {
            let _this = this
            fn(_this, e)
        },
        touchendCallback(e) {
            //触摸结束
            if (e.touches.length === 0) {
                this.setData({
                    'stv.zoom': false, //重置缩放状态
                })
            }
        },
        uploadTap() {
                let _this = this
                wx.chooseImage({
                    count: 1, // 默认9
                    sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
                    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
                    success(res) {
                        _this.initImg( res.tempFilePaths[0]);
                    }
                })
            },
            rotate() {
                let _this = this;
                _this.setData({
                    'stv.rotate': _this.data.stv.rotate % 90 == 0 ? _this.data.stv.rotate = _this.data.stv.rotate + 90 : _this.data.stv.rotate = 0
                })
            },
            // canvas剪裁图片
            cropperImg() {
                wx.showLoading({
                    title: 'loading',
                    mask: true
                })
                let _this = this;
                let ctx = wx.createCanvasContext('imgcrop',this);
                let cropData = _this.data.stv;
                ctx.save();
                // 缩放偏移值
                let x = (_this.data.originImg.width - _this.data.originImg.width * cropData.scale) / 2;
                let y = (_this.data.originImg.height - _this.data.originImg.height * cropData.scale) / 2;

                //画布中点坐标转移到图片中心
                let movex = (cropData.offsetX + x) * 2 + _this.data.originImg.width * cropData.scale;
                let movey = (cropData.offsetY + y) * 2 + _this.data.originImg.height * cropData.scale;
                ctx.translate(movex, movey);
                ctx.rotate(cropData.rotate * Math.PI / 180);
                ctx.translate(-movex, -movey);

                ctx.drawImage(_this.data.originImg.url, (cropData.offsetX + x) * 2, (cropData.offsetY + y) * 2, _this.data.originImg.width * 2 * cropData.scale, _this.data.originImg.height * 2 * cropData.scale);
                ctx.restore();
                ctx.draw(false, ()=> {
                    wx.canvasToTempFilePath({
                        canvasId: 'imgcrop',
                        success(response) {
                            console.log(response.tempFilePath);
                            _this.$emit("childFn", { url: response.tempFilePath })
                            wx.hideLoading();
                        },
                        fail( e ) {
                            console.log( e );
                            wx.hideLoading();
                            wx.showToast({
                                title: '生成图片失败',
                                icon: 'none'
                            })
                        }
                    }, this)
                });
            }
        };
    };
    /**
     * fn:延时调用函数
     * delay:延迟多长时间
     * mustRun:至少多长时间触发一次
     */
    var throttle = function (fn, delay, mustRun) {
        var timer = null,
            previous = null;

        return function () {
            var now = +new Date(),
                context = this,
                args = arguments;
            if (!previous) previous = now;
            var remaining = now - previous;
            if (mustRun && remaining >= mustRun) {
                fn.apply(context, args);
                previous = now;
            } else {
                clearTimeout(timer);
                timer = setTimeout(function () {
                    fn.apply(context, args);
                }, delay);

            }
        }
    }

    var touchMove = function (_this, e) {
        //触摸移动中
        if (e.touches.length === 1) {
            //单指移动
            if (_this.data.stv.zoom) {
                //缩放状态,不处理单指
                return;
            }
            let { clientX, clientY } = e.touches[0];
            let offsetX = clientX - _this.startX;
            let offsetY = clientY - _this.startY;
            _this.startX = clientX;
            _this.startY = clientY;
            let { stv } = _this.data;
            stv.offsetX += offsetX;
            stv.offsetY += offsetY;
            stv.offsetLeftX = -stv.offsetX;
            stv.offsetLeftY = -stv.offsetLeftY;
            _this.stv=stv;

        } else if (e.touches.length === 2) {
            //计算旋转
            let preTwoPoint = JSON.parse(JSON.stringify(twoPoint))
            twoPoint.x1 = e.touches[0].pageX * 2
            twoPoint.y1 = e.touches[0].pageY * 2
            twoPoint.x2 = e.touches[1].pageX * 2

            function vector(x1, y1, x2, y2) {
                this.x = x2 - x1;
                this.y = y2 - y1;
            };

            //计算点乘
            function calculateVM(vector1, vector2) {
                return (vector1.x * vector2.x + vector1.y * vector2.y) / (Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y) * Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y));

            }
            //计算叉乘
            function calculateVC(vector1, vector2) {
                return (vector1.x * vector2.y - vector2.x * vector1.y) > 0 ? 1 : -1;
            }

            let vector1 = new vector(preTwoPoint.x1, preTwoPoint.y1, preTwoPoint.x2, preTwoPoint.y2);
            let vector2 = new vector(twoPoint.x1, twoPoint.y1, twoPoint.x2, twoPoint.y2);
            let cos = calculateVM(vector1, vector2);
            let angle = Math.acos(cos) * 180 / Math.PI;

            let direction = calculateVC(vector1, vector2);
            let _allDeg = direction * angle;

            // 双指缩放
            let xMove = e.touches[1].clientX - e.touches[0].clientX;
            let yMove = e.touches[1].clientY - e.touches[0].clientY;
            let distance = Math.sqrt(xMove * xMove + yMove * yMove);

            let distanceDiff = distance - _this.data.stv.distance;
            let newScale = _this.data.stv.scale + 0.005 * distanceDiff;
            if (Math.abs(_allDeg) > 1) {
                //去掉缩放中做翻转
                // _this.setData({
                //   'stv.rotate': _this.data.stv.rotate + _allDeg
                // })
            } else {
                //双指缩放
                let xMove = e.touches[1].clientX - e.touches[0].clientX;
                let yMove = e.touches[1].clientY - e.touches[0].clientY;
                let distance = Math.sqrt(xMove * xMove + yMove * yMove);

                let distanceDiff = distance - _this.data.stv.distance;
                let newScale = _this.data.stv.scale + 0.005 * distanceDiff;
                if (newScale < 0.2 || newScale > 2.5) {
                    return;
                }
                _this.stv.distance=distance;
                _this.stv.scale=newScale;
            }
        } else {
            return;
        }
    }
    //为touchMove函数节流
    const fn = throttle(touchMove, 10, 10);
</script>

父组件

<style lang="less" scoped>
    /**index.wxss**/
    page {
        width: 100%;
        height: 100%;
    }
    .container {
        width: 100%;
        height: 100%;
        background: #eee;
        overflow: hidden;
    }
    .cropper {
        width: 100%;
        height: 100%;
    }
    .img {
        margin:20rpx auto;
        display: block;
        width:100%;
        background: #fff;
    }
    .choose-img {
        width: 40%;
        text-align: center;
        padding: 30rpx;
        border: 1px solid #fff;
        margin: 20rpx auto;
        background: #000;
        color: #fff;
    }
</style>
<template>
    <view class='container'>
        <image class='img' mode='widthFix' src="{{ cropperResult }}" wx:if="{{ cropperResult }}"></image>
        <view class='cropper' wx:if="{{originUrl}}">
            <cropper  @childFn.user="getCropperImg"></cropper>
        </view>
        <view class='choose-img' wx:else bindtap='uploadTap'>选择图片</view>
    </view>
</template>
<script>
    import wepy from 'wepy';
    import Cropper from '@/components/wpy_cropper';
    export default class Cropper extends wepy.page {
        config = {
            "navigationBarBackgroundColor": "#f7f7f7",
            "navigationBarTextStyle": "black",
            "navigationBarTitleText":"裁剪",
            "backgroundTextStyle": "light"
        };
        data={
            ratio: 102/152,
            originUrl: '',
            cropperResult: ''
        };
        components = {
            cropper:Cropper
        };
        methods={
            uploadTap() {
                let _this = this;
                wx.chooseImage({
                    count: 1, // 默认9
                    sizeType: ['original'], // 可以指定是原图还是压缩图,默认二者都有
                    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
                    success(res) {
                        _this.originUrl=res.tempFilePaths[0];
                        _this.cropperResult="";
                        _this.$apply();
                        _this.$invoke("cropper","initImg",_this.originUrl)
                    }
                })
            },
            getCropperImg(e) {
                this.originUrl="";
                this.cropperResult="";
                this.cropperResult = e.url;
            }
        }
    }
</script>