一、项目预览。

效果:

一个手指拖动,可以移动图片,两个手指拖动可以缩放。

下载该项目:

git clone https://github.com/Q956164483/vue-image-cropper.git

运行:

npm i

cd vue-image-cropper

npm run serve

二、应用在自己的项目。

1.复制“vue-image-cropper”项目中的 ./src/components/imageCropper.vue文件 (imageCropper.vue需要引入exif-small.js)

2.在需要使用的页面直接引入imageCropper组件,并绑定cropperConfig配置参数和裁剪之后的回调函数callback。

3.安装lrz。

npm i lrz -S

<image-cropper ref="imageCropper" :cropperConfig="cropperConfig" :callback="loadImage"></image-cropper>
cropperConfig: {
  width: 1, // 裁剪宽度(比例)
  height: 1, // 裁剪高度(比例)
  quality: 0.7, // 图片质量(0~1之间)
  maxWidth: 750 // 导出的图片的最大宽度
}

loadImage (data) {
  console.log(data);//data为图片base64字符串,可以直接放入img的src
}

三、本人修改了部分代码,可以直接复制源码。

可以选择支持一下博主,花个几十积分,我的代码:

uni-app的图片裁剪nice-cropper.rar_uniapp图片裁剪,uniapp图片裁剪-互联网文档类资源-CSDN下载

如果饭都吃不起,就选择如下:

安装lrz:

npm i lrz -S

1.效果。

2.imageCropper.vue的代码:

<template>
    <div ref="cropperPage" class="cropper-page" v-show="isShow">
        <div @click="cancel()" class="icon icon-back"></div>
        <input ref="file" type="file" accept="image/*" @change="readImage">
        <img alt="" class="cropper-img" :style="imageStyle" ref="img">
        <div class="cover box box-ac box-jc" :style="{height: coverHeight + 'px'}">
            <!-- 请调整图片 -->
        </div>
        <div ref="cropBox" class="cropper-box" @touchstart.prevent="touchStart" @touchmove.prevent="touchMove"></div>
        <div class="cover cover box box-ac box-jc" :style="{height: coverHeight + 'px'}">
            <div class="box box-f1 box-jc box-fh">
                <div class="btn" @click="cancel">取消</div>
                <!--重选checkPhoto-->
            </div>
            <div class="box box-f1 box-jc box-fh">
                <div class="btn sure" @click="confirm">确定</div>
            </div>
        </div>
    </div>
</template>

<script>
    import lrz from 'lrz'
    const getDinstance = function(point0, point1) {
        return Math.sqrt(Math.pow(point0.pageY - point1.pageY, 2) + Math.pow(point0.pageX - point1.pageX, 2))
    }
    export default {
        name: 'imageCropper',
        props: {
            //裁剪确认后的回调,结果是图片数据。
            callback: {
                type: Function,
                default () {}
            },
            cropperConfig: {
                type: Object,
                default () {
                    return {
                        width: 1,//裁剪宽度(比例)
                        height: 1,//裁剪高度(比例)
                        quality: 0.7,//图片质量(0~1之间)
                        maxWidth: 640//导出的图片最大宽度
                    }
                }
            }
        },
        data() {
            return {
                coverHeight: 0,
                cropperHeight: 0,
                imgInitTop: 0,
                amplitude: 0,
                imageState: {
                    left: 0,
                    top: 0,
                    scale: 1,
                    width: 0,
                    height: 0,
                    originX: 0,
                    originY: 0
                },
                distance: 0,
                imageStyle: {
                    top: '0',
                    transform: 'translate3d(0px, 0px, 0px) scale(1)',
                    transformOrigin: 'left top'
                },
                cropBoxRect: {},
                touchPos: {
                    x: 0,
                    y: 0
                },
                isShow: false,
                minScale: 0,
                info: '',
                orientation: ''
            }
        },
        watch: {
            'imageState': {
                handler(val) {
                    this.imageStyle.transform = 'translate3d(-' + val.left + 'px, -' + val.top + 'px, 0px) scale(' + val.scale + ')'
                },
                deep: true
            }
        },
        methods: {
            cancel() {
                this.file = null
                this.isShow = false
            },
            checkPhoto() {
                this.$refs.file.click()
            },
            readImage($event) {
                var self = this
                var file = $event.target.files[0]
                lrz(file)
                    .then(rst => {
                        self.orientation = 1
                        self.$refs.img.onload = () => {
                            self.initCropper()
                        }
                        self.$refs.img.src = rst.base64
                        $event.target.value = null
                    })
            },
            initCropper() {
                this.isShow = true // 显示裁剪界面
                this.$nextTick(() => {
                    let cropperPage = this.$refs.cropperPage
                    let pageWidth = cropperPage.clientWidth
                    let pageHeight = cropperPage.clientHeight
                    let cropBox = this.$refs.cropBox
                    let cropBoxWidth = cropBox.clientWidth
                    let cropBoxHeight = Math.floor(cropBoxWidth * (+this.cropperConfig.height) / (+this.cropperConfig.width))
                    this.$refs.cropBox.style.height = cropBoxHeight + 'px'
                    this.coverHeight = (pageHeight - cropBoxHeight) / 2
                    let cropBoxTop = this.coverHeight
                    this.imageState.left = 0
                    this.imageState.top = 0
                    this.imageStyle.top = cropBoxTop + 'px'
                    this.cropBoxRect = {
                        left: 0,
                        top: cropBoxTop,
                        width: pageWidth,
                        height: cropBoxHeight
                    }
                    let img = this.$refs.img
                    var width = this.imageState.width = img.naturalWidth
                    var height = this.imageState.height = img.naturalHeight
                    // 计算imageState
                    if (width > height) {
                        this.minScale = this.imageState.scale = this.cropBoxRect.height / height
                        this.imageState.left = (width * this.imageState.scale - this.cropBoxRect.width) / 2
                    } else {
                        this.minScale = this.imageState.scale = this.cropBoxRect.width / width
                        this.imageState.top = (height * this.imageState.scale - this.cropBoxRect.height) / 2
                    }
                })
            },
            confirm() {
                let self = this
                let imageState = this.imageState
                let cropBoxRect = this.cropBoxRect
                // 导出图片的最大宽度
                let maxWidth = this.cropperConfig.maxWidth
                let scale2 = maxWidth / cropBoxRect.width
                let scale = imageState.scale * scale2
                let width = cropBoxRect.width * scale2
                let height = cropBoxRect.height * scale2
                let left = imageState.left * scale2
                let top = imageState.top * scale2
                let image = this.$refs.img
                let canvas = document.createElement('canvas')
                let ctx = canvas.getContext('2d')
                // ios 的照片有拍摄的角度信息 参考 http://www.bcty365.com/content-142-3055-1.html
                let orientation = this.orientation
                switch (orientation) {
                    case 1:
                        canvas.width = width
                        canvas.height = height
                        ctx.drawImage(image, left / scale, top / scale, width / scale, height / scale, 0, 0, width, height)
                        break
                    case 6:
                        canvas.width = height
                        canvas.height = width
                        ctx.rotate(90 * Math.PI / 180)
                        ctx.drawImage(image, left / scale, top / scale, width / scale, height / scale, 0, -height, width, height)
                        break
                    case 8:
                        canvas.width = height
                        canvas.height = width
                        ctx.rotate(-90 * Math.PI / 180)
                        ctx.drawImage(image, left / scale, top / scale, width / scale, height / scale, -width, 0, width, height)
                        break
                    case 3:
                        canvas.width = width
                        canvas.height = height
                        ctx.rotate(180 * Math.PI / 180)
                        ctx.drawImage(image, left / scale, top / scale, width / scale, height / scale, -width, -height, width, height)
                        break
                }
                let dataUrl = canvas.toDataURL('image/jpeg', this.cropperConfig.quality)
                self.callback(dataUrl)
                self.isShow = false
            },
            getFocalPoint(point0, point1) {
                return {
                    x: (point0.pageX + point1.pageX) / 2,
                    y: (point0.pageY + point1.pageY) / 2
                }
            },
            touchStart(event) {
                var fingerCount = event.touches.length
                if (fingerCount) {
                    // 记录触摸初始位置
                    let touchEvent = event.touches[0]
                    this.touchPos = {
                        x: touchEvent.clientX,
                        y: touchEvent.clientY
                    }
                }
                if (fingerCount >= 2) {
                    // 获取两点距离、中点位置;两点距离old/new=放大倍数;中点位置,缩放中心;
                    let point0 = event.touches[0]
                    let point1 = event.touches[1]
                    this.distance = getDinstance(point0, point1)
                    this.touchPos = this.getFocalPoint(point0, point1)
                    // 设置缩放倍数,
                }
            },
            touchMove(event) {
                // 根据触摸点位移,移动图片,重置触摸点位置
                var fingerCount = event.touches.length
                var touchEvent = event.touches[0]
                if (fingerCount === 1) {
                    let distX = touchEvent.pageX - this.touchPos.x
                    let distY = touchEvent.pageY - this.touchPos.y
                    let newX = this.imageState.left - distX
                    let newY = this.imageState.top - distY
                    let scale = this.imageState.scale
                    // alert(scale)
                    let maxX = this.imageState.width * scale - this.cropBoxRect.width
                    let maxY = this.imageState.height * scale - this.cropBoxRect.height
                    this.imageState.left = newX < 0 ? 0 : (newX > maxX ? maxX : newX)
                    this.imageState.top = newY < 0 ? 0 : (newY > maxY ? maxY : newY)
                    this.touchPos.x = touchEvent.pageX
                    this.touchPos.y = touchEvent.pageY
                } else if (fingerCount > 1) {
                    let point0 = event.touches[0]
                    let point1 = event.touches[1]
                    let distance = getDinstance(point0, point1)
                    let zoom = distance / this.distance
                    let scale = zoom * this.imageState.scale
                    let maxX = this.imageState.width * scale - this.cropBoxRect.width
                    let maxY = this.imageState.height * scale - this.cropBoxRect.height
                    let touchPos = this.getFocalPoint(point0, point1)
                    let newX = zoom * (this.imageState.left + touchPos.x) - touchPos.x
                    let newY = zoom * ((this.imageState.top - this.imgInitTop) + touchPos.y) - touchPos.y + this.imgInitTop
                    // 限制缩放
                    // 图片新位置:由中点位置确认;(新位置到中点)/(旧位置到中点)=(new scale)/(old scale)
                    // newLeft - touchPos.x = (distance / this.distance) * (oldLetf - touchPos.x)
                    // oldLeft = 0 - this.imageState.left
                    // oldTop = imgInitTop - this.imageState.top
                    this.distance = distance
                    if (scale < this.minScale) {
                        this.imageState.scale = this.minScale
                    } else {
                        this.imageState.scale = scale
                        this.imageState.left = newX < 0 ? 0 : (newX > maxX ? maxX : newX)
                        this.imageState.top = newY < 0 ? 0 : (newY > maxY ? maxY : newY)
                    }
                    this.touchPos = touchPos
                }
            }
        }
    }
</script>

<style lang="scss" scoped>
$themeColor:#409EFF;
.box {
    display: flex;
    position: relative;
}
.box-f1 {
    flex: 1;
}
.box-ac {
    align-items: center
}
.box-jc {
    justify-content: center
}
.box-ver {
    flex-direction: column
}
.cropper-page {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 10;
    background-color: #fff;
    overflow: hidden;
}
.cover {
    color: #FFF;
    font-size: .4rem;
    background-color: rgba(0, 0, 0, 0.2);
}
.cropper-box {
    // border: 1px dashed #FFF;
    border: 2px dashed #FFF;
}
.cropper-img {
    position: absolute;
    z-index: -1;
}
input[type="file"] {
    opacity: 0;
    position: fixed;
    top: -1000px;
    left: -1000px;
}
.btn {
    font-size: .6rem;
    padding: .1rem .3rem;
    border: .02rem solid;
    color: #FFF;
    border-radius: .4rem;
    width:80px;
    height:35px;
    display: flex;
    align-items: center;
    justify-content: center;
    background:#909399;
    &.sure {
        background: $themeColor;
    }
}
</style>

3.简单使用。

<template>
    <div>
        <image-cropper ref="imageCropper" :cropperConfig="cropperConfig" :callback="loadImage"></image-cropper>
        <button @click="selectFile()">打开</button>
        <div>
            <img v-if="img" :src="img" style="width:200px;height:200px;"/>
        </div>
    </div>
</template>

<script>
import imageCropper from '@/components/imageCropper'
export default {
    name: 'Home',
    components: {imageCropper},
    data() {
        return {
            img:'',
            cropperConfig: {
                width: 1,
                height: 1,
                quality: 0.7,
                maxWidth: 750
            }
        };
    },
    mounted() {
        
    },
    methods: {
        selectFile () {
            this.$refs.imageCropper.checkPhoto()
        },
        // 图片裁剪之后的回调
        loadImage (data) {
            // this.images.push(data)
            console.log(data)
            this.img=data;
        },
    },
};
</script>

<style lang="scss" scoped>

</style>

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐