移动端上传图片进行平移放大缩小操作后生成海报

3,679 阅读2分钟

使用vue+html2canvas+exif-js
github地址
线上demo

主要功能

  • 上传图片
  • 对图片进行操作:移动、放大、缩小
  • 合成海报

具体功能:

上传图片

html:

<input type="file" value="" accept="image/*" @click="getPhoto" id="image-input">

js

getPhoto () {
    var imageInput = document.querySelector('#image-input')
    var that = this
    imageInput.addEventListener('change', function (e) {
        reads = new FileReader()
        reads.readAsDataURL(this.files[0])
        reads.addEventListener('load', function (e) {
            that.imgUrl = this.result
            that.myImg.position.x = 0
            that.myImg.position.y = 0
            that.myImg.scale = 1
            var orientation
            that.previewImg.addEventListener('load', function () {
                Exif.getData(that.previewImg, function() { // 获取图像的数据
                Exif.getAllTags(this); // 获取图像的全部数据,值以对象的方式返回
                orientation = Exif.getTag(this, "Orientation"); // 获取图像的拍摄方向
                var rotateCanvas = document.createElement("canvas"),
                rotateCtx = rotateCanvas.getContext("2d");
                 // 针对图像方向进行处理
                switch (orientation) {
                    case 1 :
                        rotateCanvas.width = that.previewImg.width;
                        rotateCanvas.height = that.previewImg.height;
                        rotateCtx.drawImage(that.previewImg, 0, 0, that.previewImg.width, that.previewImg.height);
                        break;
                    case 6 : // 顺时针 90 度
                        rotateCanvas.width = that.previewImg.height;
                        rotateCanvas.height = that.previewImg.width;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(90 * Math.PI / 180);
                        rotateCtx.drawImage(that.previewImg, 0, -that.previewImg.height, that.previewImg.width, that.previewImg.height);
                        break;
                    case 8 :
                        rotateCanvas.width = that.previewImg.height;
                        rotateCanvas.height = that.previewImg.width;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(-90 * Math.PI / 180);
                        rotateCtx.drawImage(that.previewImg, -that.previewImg.width, 0, that.previewImg.width, that.previewImg.height);
                        break;
                    case 3 : // 180 度
                        rotateCanvas.width = that.previewImg.width;
                        rotateCanvas.height = that.previewImg.height;
                        rotateCtx.translate(0, 0);
                        rotateCtx.rotate(Math.PI);
                        rotateCtx.drawImage(that.previewImg, -that.previewImg.width, -that.previewImg.height, that.previewImg.width, that.previewImg.height);
                        break;
                    default :
                        rotateCanvas.width = that.previewImg.width;
                        rotateCanvas.height = that.previewImg.height;
                        rotateCtx.drawImage(that.previewImg, 0, 0, that.previewImg.width, that.previewImg.height);
                    }
                    var rotateBase64 = rotateCanvas.toDataURL("image/jpeg", 0.5);
                });
            })
        }) 
    })
}

移动图片

对图片和相框绑定@touchstart @touchmove @touchend

getInitPosition (e) {
    event.preventDefault()
    if (this.imgUrl) {
        var length = e.touches.length
         if (length > 1) {
            let pointOne = e.touches[0]
            let pointTwo = e.touches[1]
            this.initTouchX = pointOne.clientX - pointTwo.clientX
            this.initTouchY = pointOne.clientY - pointTwo.clientY
        } else {
            var touches = e.touches[0]
            this.initTouchX = touches.clientX
            this.initTouchY = touches.clientY
        }   
    }
},
 getMovePosition (e) {
    event.preventDefault()
    if (this.imgUrl) {
        var length = e.touches.length
        if (length > 1) {
            let pointOne = e.touches[0]
            let pointTwo = e.touches[1]
            this.changeTouchX = pointOne.clientX - pointTwo.clientX
            this.changeTouchY = pointOne.clientY - pointTwo.clientY
            var scale = (this.changeTouchX - this.initTouchX) > (this.changeTouchY - this.initTouchY) ? (this.changeTouchX / this.initTouchX) : (this.changeTouchY / this.initTouchY) 
            scale *= this.myImg.lastScale
            this.myImg.scale = scale > 3 ? 3 : scale < 0.5 ? 0.5 : scale
        } else {
            var touches = e.touches[0]
            this.changeTouchX = touches.clientX - this.initTouchX
            this.changeTouchY = touches.clientY - this.initTouchY
            this.myImg.position.x = this.lastTouchX + (this.changeTouchX / this.myImg.scale)
            this.myImg.position.y = this.lastTouchY + (this.changeTouchY / this.myImg.scale)
        }   
    }
},
getLeavePosition (e) {
    this.myImg.lastScale = this.myImg.scale
    if (e.touches.length > 0) {
        var touches = e.touches[0]
        this.initTouchX = touches.clientX
        this.initTouchY = touches.clientY
    }
    this.lastTouchX = this.myImg.position.x
    this.lastTouchY = this.myImg.position.y
},

合成图片

createPhoto () {
    if (this.imgUrl) {
        let photoBox = document.querySelector('.photo-box')
        newImgWidth = photoBox.style.offsetWidth
        let newImgHeight = photoBox.style.offsetHeight
        let scale = window.devicePixelRatio
        let that = this
        html2canvas(photoBox, {
            width: newImgWidth,
            height: newImgHeight,
            scale: scale,
            useCORS: true
        }).then(function (canvas) { 
            var dataUrl = canvas.toDataURL('image/jpg')
            localStorage.imgData = dataUrl
            that.$router.push({
                name: 'share',
                params: {
                    storage: 'imgData'
                }
            })
        })
    } else {
        alert('请上传图片')
    }
}

遇到的问题

  1. 在浏览器上阻止缩放的问题
    在tounchmove时使用event.preventDefault()
  2. 合成图片的清晰度 在html2canvas写参数时,scale = window.devicePixelRatio
  3. 对图片进行缩放时,对距离的判断
    这里需要区分两种情况:
    • 两指缩放
    • 一指移动
      在两指缩放后会有一个手放手另一个手继续操作的情况。这样在touchend时,e.touches还存在一个元素。所以需要在这里做一个判断。
  4. 使用vue的:style="内联样式"时遇上的写法问题
    :style="{transform:'scale('+ myImg.scale+ ') translate('+myImg.position.x+'px,'+myImg.position.y+'px)'}"