需求:
类似一些美图软件中的给照片加相框的操作
1. 用户选择照片,照片可以移动、可以放大缩小
2. 选择海报模版
3. 生成海报
最后生成的海报效果图类似下图
使用的技术栈是:vue+html2canvas+exif-js
具体思路和代码:
<template>
<section>
<div id='posterwrap' v-show='!sharing' ref="wrap">
<div ref='posterwrap-inner' class="posterwrap-inner">
<p v-show='!temp.imgsrc' @click='chooseimg'>p>
<div class="preview-img-wrap">
<img @touchstart.prevent.stop='touchStart($event, 0)' @touchmove.prevent.stop='touchMove($event, 0, -1)' @touchend.prevent.stop='touchEnd($event, 0)' :style='{width: 99 * myphoto.scale + "%", height: 99 * myphoto.scale + "%", left: myphoto.x + "px", top: myphoto.y + "px"}' :src="myphoto.imgsrc" id='preview' alt="" ref="img" >
div>
<img @touchstart.prevent.stop='touchStart($event, 0)' @touchmove.prevent.stop='touchMove($event, 0)' @touchend.prevent.stop='touchEnd($event, 0)' :src="temp" alt="" class="tag-item">
div>
div>
<div class="footer">
<div id="tags-preview">
<img class="tag-item" @click='addTag' src="@/assets/img/temp1.png" alt="">
<img class="tag-item" @click='addTag' src="@/assets/img/temp2.png" alt="">
div>
<div class="banner">
<div @click='rephoto' data-xeslog-params="key=click-upload-img" class="rephoto footer-btn">重新选图div>
<div class="share footer-btn" @click='screenshots' data-xeslog-params="key='click-finish-share'">制作完成div>
div>
div>
div>
<canvas id='canvas'>canvas>
<canvas id='canvas2'>canvas>
<canvas id='canvas3' style="display:none">canvas>
<input type="file" value="" accept="image/*" id='chooseimg' @change='previewFile'>
<div v-if='share' data-xeslog-params="key=click-share-img" class="result">
<p class="share-tit">长按保存海报p>
<p >分享到朋友圈,有机会返还学费哦p>
<div class="myposter-wrap">
<img v-if="this.$route.query.isapp == 0" id='myposter' :src="myposterlast" alt="">
div>
div>
<div class="shadow" v-if="finish">
<div class="post-doing" v-if="finish">
<img src ="@/assets/img/loading.gif" alt="">
<p>{{shadow}}p>
div>
div>
section>
template>
1.点击’选择图片’的按钮时候,实际触发的是,在input的
change
事件里面,使用new FileReader()
读取图片的bsae64,如果调起的是用户的相机拍照照片,在各别机型上传出来的照片会有90度或180度旋转的现象,解决这一问题使用的是exif-js
来将旋转了的图片放正。然后得到正确位置的图片的base64赋给图片预览区的img
2. 通过上面的步骤,我们可以在预览区生成了预览的图片,接下来要实现的是点击需要的海报模版相框,这个逻辑比较简单,就是将点击的模版src赋给模版img
3. 模版选择好了,接下来要进行照片的编辑了,照片的缩放和移动,相对还是比较难写一些的,通过touchstart事件的e.touches
来判断是否为多指操作和两指之间的距离判断来判定是缩放还是移动。如果是缩放,需要计算放大的比例。如果是移动,需要计算移动的比例,具体代码如下
_getDistance (xLen, yLen) {//计算距离
return Math.sqrt(xLen * xLen + yLen * yLen)
},
touchStart (e, index) {//缩放或者移动
if (e.touches.length > 1) {
let point1 = e.touches[0]
let point2 = e.touches[1]
let xLen = Math.abs(point2.pageX - point1.pageX)
let yLen = Math.abs(point2.pageY - point1.pageY)
this.myphoto.touchDistance = this._getDistance(xLen, yLen)
} else {
this.myphoto.move = 1
this.myphoto.sx = e.touches[0].pageX
this.myphoto.sy = e.touches[0].pageY
}
}
},
touchMove (e, index, direction) {//拖动
direction = direction == -1 ? -1 : 1
if (e.touches.length > 1) {
let xLen = Math.abs(e.touches[0].pageX - e.touches[1].pageX)
let yLen = Math.abs(e.touches[1].pageY - e.touches[1].pageY)
let touchDistance = this._getDistance(xLen, yLen) - this.myphoto.touchDistance
if (this.myphoto.touchDistance) {
let scale = Math.abs(this.myphoto.scale + touchDistance / 3000)
this.myphoto.scale = (scale > 3 ? 3 : scale < 0.5 ? 0.5 : scale)
}
} else {
this.myphoto.x -= (this.myphoto.sx - e.touches[0].pageX) * direction
this.myphoto.y -= (this.myphoto.sy - e.touches[0].pageY) * direction
this.myphoto.sx = e.touches[0].pageX
this.myphoto.sy = e.touches[0].pageY
}
},
touchEnd (e, index) {//行为结束
this.myphoto.touchDistance = 0
this.myphoto.move = 0
if (e.touches.length) {
this.myphoto.move = 1
this.myphoto.sx = e.touches[0].pageX
this.myphoto.sy = e.touches[0].pageY
}
},
4.再接下来就是截屏来,将有了照片的模版使用html2canvas截图
screenshots () {
var _this = this
var wrap = document.getElementById('posterwrap')
var canvas = document.getElementById('canvas')
_this.finish = 1
_this.shadow = '正在努力制作海报'
canvas.width = wrap.offsetWidth * 2 + 5
canvas.height = wrap.offsetHeight * 2
canvas.style.width = wrap.offsetWidth + 'px'
canvas.style.height = wrap.offsetHeight + 'px'
console.log(canvas)
var context = canvas.getContext('2d')
context.scale(2, 2)
context.translate(-20, -10)
html2canvas(_this.$refs['posterwrap-inner'], {
allowTaint: true,
taintTest: false,
backgroundColor: null,
// dpi: 3000,
scale: 2,
canvas: canvas,
width: canvas.width,
height: canvas.height,
removeContainer: false,
useCORS: true,
onrendered: function (canvas) {//将截屏图片的base64给生成区
var dataUrl = canvas.toDataURL('image/jpg')
_this.sharing = 1
_this.myposterlast = dataUrl
_this.share = 1
_this.myphoto = {
touchDistance: 0,
scale: 1,
move: 0,
x: 0,
y: 0,
sx: 0,
sy: 0,
imgsrc: ''
}
_this.taglist = _this.myphoto
}
})
},
其中有遇到一个很坑的问题:html2canvas截屏图片有白边/黑边 的问题
具体见另一篇博客:html2canvas截屏图片有白边/黑边 的问题记录