来,效果图
1、实现思路
先通过浏览器打开本地图片,再用canvas加载图片,然后通过getImageData把canvas的画布输出,然后用你想裁剪成多大的另一个canvas通过putImageData按照对应的画布位置接收然后将这个canvas用toDataURL输出base64的图片然后在把这个base64转化成图片文件就可以进行上传了。
2、基础实现
文件我是放在 components 里面(就是随便一个放组件的位置)
上传中
选择图片
选择文件
确定修改
退出修改
请点击左边的选择文件
上面是全部的代码,要贴的话,只管这个就行。下面为部分核心代码段说明
如何加载本地图片就不多说了,首先是如何canvas移动图片,无非就是获取鼠标的坐标位置(clientX)减去最外面的元素(position)的坐标位置减去自身元素位置的偏移(150,为什么是150呢,我样式是这么写的),然后减去上mousedown事件点下去鼠标的初始化坐标位置(ponitX)这个,这样得出来值就是就是让图片实际偏移的位置了。(ps:如果有更好的拿到这种位置的方法请务必告诉我,谢谢啦)下面是计算位置的代码:
//编辑图片
editImg(){
let e = event ? event : window.event;
let position = this.$refs.myImgTailoring;
if(this.dragFlag){ //是否拖动
this.valueX = e.clientX - position.offsetLeft - 150 - this.ponitX;
this.valueY = e.clientY- position.offsetTop - this.pointY;
this.loadingImg(this.valueX, this.valueY, 550, this.zoom)
}
}
这个是大概的结构图
放大就是监听滚轮事件按照一定的比例缩放缩放图片的宽度,然后高度按照宽度的比例进行等比例缩放就实现放大了。具体实现如下面代码:
这个监听滚轮的代码,网上一大堆 。
//。。。。。省略部分代码
var scrollFunc = (e) => {
if (e.wheelDelta) { //判断浏览器IE,谷歌滑轮事件
if (e.wheelDelta > 0) { //当滑轮向上滚动时
this.zoom ++;
}
if (e.wheelDelta < 0) { //当滑轮向下滚动时
this.zoom --;
}
} else if (e.detail) { //Firefox滑轮事件
if (e.detail> 0) { //当滑轮向上滚动时
this.zoom ++;
}
if (e.detail< 0) { //当滑轮向下滚动时
this.zoom --;
}
}
};
//滚动滑轮触发scrollFunc方法
window.onmousewheel = document.onmousewheel = scrollFunc;
width = width * (1 + zoom *0.02);//放大倍率
let showHeight = width / img.width * img.height; //根据宽度等比例缩放高度
上面这些弄完后通过擦除canvas画布,然后写入新的画布实现动画效果。
//载入图片
//saveflag 是否保存坐标
//centerFlag,高度是否居中
//scollFlag, 是否为滚轮放大
loadingImg(x, y, width, zoom, centerFlag, saveFlag, scollFlag){
let canvas = this.$refs.myImgTailoringCanvas;
canvas.width = 550;
canvas.height = 400;
let ctx = canvas.getContext('2d');
let img = new Image();
img.src = this.imgData;
img.onload = () =>{
//清空画布
ctx.clearRect(0, 0, 550, 400);
// 将图片画到canvas上面上去!
width = width * (1 + zoom *0.02);//放大倍率
let showHeight = width / img.width * img.height; //根据宽度等比例缩放高度
let showTop = centerFlag ? 200 - showHeight/2 : this.imgY + y;//计算偏移
x += this.imgX;//计算偏移
if(saveFlag){//不再拖拽时更新最后的显示坐标
this.imgX = x;
this.imgY = showTop;
}
if(scollFlag){ //放大不计算偏移
ctx.drawImage(img, this.imgX, this.imgY, width, showHeight);
return
}
ctx.drawImage(img, x, showTop, width, showHeight);
}
},
为什么要在img.onload里面进行操作呢,因为你执行完了,图片都未必加载完,所以在这里是最稳妥的。
就是按照上面的思路,先把显示图片的canvas的画布输出,然后用另一个canvas去按照辅助线的位置进行接收,这样就实现了裁剪。
saveImg(){
if(this.imgData == ''){
return
}
let canvas = this.$refs.myImgTailoringCanvas;
let tempCanvas = this.$refs.myImgTailoringTempCanvas
let line = this.$refs.myImgTailoringLine;
tempCanvas.width = this.imgWidth;
tempCanvas.height = this.imgHeight;
let ctx = canvas.getContext('2d');
let tctx = tempCanvas.getContext('2d');
//截取框里面的内容
tctx.putImageData( ctx.getImageData(line.offsetLeft - this.imgWidth/2, line.offsetTop - this.imgHeight/2, this.imgWidth, this.imgHeight), 0, 0)
}
这样一来,我们就有tempCanvas截取后的画布了,到这里,几乎已经结束了。可以为所欲为。嘿嘿嘿。把这个canvas输出base64,然后将base64转化为文件就得到裁剪后的图片了。代码如下
let base64Img = tempCanvas.toDataURL("image/png");
这个base64转文件代码是我在网上找的,来源为 https://www.jianshu.com/p/e26aaca7e201十分感谢
//base64格式转换为img文件
dataURLtoFile(dataurl, filename = 'file') {
let arr = dataurl.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let suffix = mime.split('/')[1]
let bstr = atob(arr[1])
let n = bstr.length
let u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime
})
}
这个功能的实现,我是模仿elementUI的上传图片的格式进行实现的,他主要接收参数是 一个是上传文件的url,一个上传前的方法,一个上传成功后的方法。因为这里有了验证,所以不需要上传前的方法(当然,你还是想要,你可以自己在这个基础上添加就行)。参数如下
props:{
show: { //显示
default: true,
type: Boolean,
},
imgWidth: { //要截取的图片宽度
default: 200,
type: Number
},
imgHeight: { //要截取的图片的高度
default: 200,
type: Number
},
url: { //图片 上传路径
default: '', //user, pwd,
type: String,
},
onSuccess: { //成功后执行的函数
type: Function
}
},
然后是上传方法。通过axios来实现
uploadImg(file){
var forms = new FormData();
forms.append('file',file)
let config = {
headers:{'Content-Type':'multipart/form-data'}
};
this.loading = true;
axios.post(this.url, forms, config)
.then(res=>{
this.loading = false
this.$emit('close', false);
this.onSuccess(res);
})
.catch(err => {
this.loading = false;
//补充异常处理代码
console.log(err)
})
},
如何使用
得终于到了这个换接,使用方法很简单的参考elementUI的就行,代码如下:
引入:
import myImgTailoring from '@/components/myImgTailoring/myImgTailoring.vue'
使用:
//上传成功后调用的方法
最后
因为这个是基于canvas开发,所以在此基础上你还可以添加自定义文字啊,图案啊。是不是很过瘾。你可能会问,回显呢?哪个自己做吧,参考elementUI,因为该插件执行了你的上传成功方法,想怎么回显就怎么回显。以上,我上传图片的图片素材和部分代码来源于网上,如果素材或者代码引起侵权,我将删除次文章。感谢各位阅读。