美文网首页
手机端上传图片前进行裁剪

手机端上传图片前进行裁剪

作者: WangYatao | 来源:发表于2020-04-23 13:06 被阅读0次

    1.创建组件ImageCropper

    需要安装lrz 这里使用npm安装

    npm i lrz

    <style scoped>
        .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;
        }
    
        .cropper-img {
            position: absolute;
            z-index: -1;
        }
    
        input[type="file"] {
            opacity: 0;
            position: fixed;
            top: -1000px;
            left: -1000px;
        }
    
        .btn {
            font-size: .3rem;
            padding: .1rem .3rem;
            border: .02rem solid;
            color: #FFF;
            border-radius: .1rem;
        }
    
        .btn .sure {
            color: #45b795;
        }
    </style>
    
    <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="checkPhoto">重选</div>
                </div>
                <div class="box box-f1 box-jc box-fh">
                    <div class="btn sure" @click="confirm">确定</div>
                </div>
            </div>
        </div>
    </template>
    
    <script>
        // import EXIF from '../assets/js/exif-small'
        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,
                            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) {
                        // console.log(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
                        })
                    // .always(() => {
                    //   this.$store.commit('isLoading', false)
                    // })
                },
                // readImage ($event) {
                //   var self = this
                //   var file = $event.target.files[0]
                //   var reader = new window.FileReader()
                //   reader.onload = () => {
                //     EXIF.getData(file, function () {
                //       let orientation = EXIF.getTag(this, 'Orientation')
                //       if (!orientation) orientation = 1
                //       self.orientation = orientation
                //       self.$refs.img.onload = () => {
                //         self.initCropper()
                //       }
                //       self.$refs.img.src = reader.result
                //       $event.target.value = null
                //     })
                //   }
                //   reader.readAsDataURL(file)
                // },
                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>
    

    使用方法

    参数说明:
    cropperConfig: {
      width: 1, // 裁剪宽度(比例)
      height: 1, // 裁剪高度(比例)
      quality: 0.7, // 图片质量(0~1之间)
      maxWidth: 750 // 导出的图片的最大宽度
    }
    
    <template>
        <div class="container">
            <img class="box" v-for="(value, key) in images" :value="value" :key="key" :src="value" />
            <div class="setting">
                <div class="box box-ver">
                    <div class="box box-fh line box-ac">
                        裁剪宽度:
                        <input class="box box-f1" placeholder="width" type="tel" v-model="cropperConfig.width" />
                    </div>
                    <div class="box  box-fh line box-ac">
                        裁剪高度:
                        <input class="box box-f1" placeholder="height" type="tel" v-model="cropperConfig.height" />
                    </div>
                    <div class="box  box-fh line box-ac">
                        图片质量:
                        <input class="box box-f1" placeholder="height" type="tel" v-model="cropperConfig.quality" />
                    </div>
                    <div class="box  box-fh line box-ac">
                        最大宽度:
                        <input class="box box-f1" placeholder="height" type="tel" v-model="cropperConfig.maxWidth" />
                    </div>
                </div>
                <div class="box box-ac box-jc">
                    <div @click="selectFile()" class="btn">选择图片</div>
                    <div @click="uploadImg()" class="btn">上传</div>
                </div>
            </div>
            <image-cropper ref="imageCropper" :cropperConfig="cropperConfig" :callback="loadImage"></image-cropper>
        </div>
    </template>
    <script>
        import imageCropper from '@/components/imageCropper'
        export default {
            name: 'index',
            components: {
                imageCropper
            },
            data () {
                return {
                    images: [],
                    cropperConfig: {
                        width: 1,
                        height: 1,
                        quality: 0.7,
                        maxWidth: 750
                    }
                }
            },
            mounted () {
            },
            methods: {
                selectFile () {
                    this.$refs.imageCropper.checkPhoto()
                },
                // 图片裁剪之后的回调
                loadImage (data) {
                    this.images.push(data)
                },
                uploadImg () {
                    alert('上传')
                }
            }
        }
    </script>
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
        .container{
            height:100%;
            overflow-y: scroll;
            -webkit-overflow-scrolling: touch;
            padding-top:4rem;
        }
        .container img{
            display:block;
            width:50%;
            float:left;
        }
        .setting{
            position:fixed;
            width:100%;
            top:0;
            left:0;
            border-bottom:1px dashed #45b795;
            padding-bottom:.1rem;
        }
        .setting .line{
            margin:0 .2rem;
            height:.75rem;
            color:#45b795;
        }
        .setting input{
            display: block;
            height:.5rem;
            padding:0 .15rem;
            font-size:.32rem;
            border:1px solid #45b795;
            outline: none;
        }
        .setting .btn{
            color:#FFF;
            margin:.1rem .5rem;
            width:2rem;
            height:.6rem;
            line-height:.6rem;
            font-size:.32rem;
            text-align: center;
            background-color: #45b795;
            border-radius:.1rem;
        }
    </style>
    

    原文:https://segmentfault.com/a/1190000009997448

    相关文章

      网友评论

          本文标题:手机端上传图片前进行裁剪

          本文链接:https://www.haomeiwen.com/subject/jhnoihtx.html