前言
最近公司项目需要用到图片裁剪技术,便着手写了一个,可以说是用Vue实现的原生裁剪,毕竟也只是去操作dom,不过vue有个黑魔法,ref属性,使用的方法和原生dom一模一样但是更节省dom操作时的消耗
裁剪思路
这边大致介绍一下裁剪图片的思路,通过input[type=file]将图片文件流读取,转换成base64后赋给一个img标签进行显示出图片,实现图片预览,在img标签的背后放一个宽高和它一样的canvas并把刚才获取的base64给canvas,此时就是一张img图片在上一张canvas图片在下(两个同一张图),之后用clip(之后会专门发一篇博客供大家理解clip的使用)对图片进行矩形裁剪,虽说是裁剪不过只是视觉上的裁剪,图片的base64仍然和原来一样。将传入clip的4个属性值转换成被裁剪图片对应在原图片的x轴,y轴,矩形区域宽,矩形区域高,将这些参数通过canvas的getImageData方法传进去可以获得刚才背后的canvas所对应区域的一个canvas区域(就是我们裁剪的区域,该区域的x,y宽,高和原图片clip所视觉裁剪的一样),然后我们新建一个canvas标签,通过putImageData方法,将这个通过getImageData得到的数据赋给新创建的canvas,在通过toDataURL方法输出新建canvas的base64,大致思路是这样,可能还有点懵,下面上代码和解析,为了更加逼真,在裁剪时我多添加了一个黑色背景
<template>
<div>
<input type="file" @change="selectImage($event)">
<div class="canvasDiv" v-if="showImg">
<!--img背后的canvas-->
<canvas ref="canvas" class="canvas"></canvas>
<!--裁剪时候的背景-->
<div class="bg" @mouseup="mouseupEvent($event)"></div>
<!--需要裁剪的图片-->
<img ref="img" class="img">
<!--裁剪图片区域内的div,为了通过鼠标拖拽进行生成裁剪区域-->
<div class="mouseDiv"
@mousedown="mousedownEvent($event)"
@mouseup="mouseupEvent($event)"
@mousemove="mousemoveEvent($event)"
>
</div>
</div>
</div>
</template>
<script>
export default {
data () {
return {
showImg:false,
imgType:false,
pointX:0,
pointY:0,
canvasStyle:{}
}
},
methods:{
//文件流导出
selectImage(event){
//获取input[type=file]
let file = event.currentTarget;
if(!file.files || !file.files[0]){
return;
}
this.showImg = true;
let reader = new FileReader();
//读取文件流
reader.onload = (evt)=>{
/*
this.$refs.img.src相当于对img节点的src属性操作
evt.target.result为图片文件装换的base64编码
*/
this.$refs.img.src = evt.target.result;
let imgDOM = this.$refs.img;
//当节点渲染完之后
this.$nextTick(()=>{
//通过Image将在图片背后的canvas画出来
let myImg = new Image();
myImg.onload = ()=>{
//这里一点要乘2,否则显示出来的比例不正常,下面的一些代码也是
this.$refs.canvas.width = imgDOM.offsetWidth * 2;
this.$refs.canvas.height = imgDOM.offsetHeight * 2;
//画背后canvas
this.$refs.canvas.getContext('2d').drawImage(myImg,0,0, imgDOM.offsetWidth * 2 , imgDOM.offsetHeight * 2 ,);
}
myImg.src = evt.target.result;
})
}
reader.readAsDataURL(file.files[0]);
},
//获取鼠标刚要截取的位置
mousedownEvent(event){
this.imgType = true;
this.pointX = event.offsetX;
this.pointY = event.offsetY;
},
//当鼠标松开时候(即形成拖拽区域结束)
mouseupEvent(event){
this.imgType = false;
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
this.$nextTick(()=>{
//将img被裁剪的x,y,w,h所对应的canvas区域拿出来
let imgData = this.$refs.canvas.getContext('2d').getImageData(
this.canvasStyle.x * 2,
this.canvasStyle.y * 2,
this.canvasStyle.w * 2,
this.canvasStyle.h * 2);
canvas.width = this.canvasStyle.w * 2;
canvas.height = this.canvasStyle.h * 2;
//将裁剪的区域给新创建的canvas
ctx.putImageData(imgData,0,0,0,0, this.canvasStyle.w * 2, this.canvasStyle.h * 2);
//输出base64
console.log(canvas.toDataURL("image/jpeg"));
// window.open(canvas.toDataURL("image/jpeg"));
})
},
//鼠标拖拽(通过一个参数imgType控制,当鼠标按下并移动时候才触发实现拖拽)
mousemoveEvent(event){
if(this.imgType){
//获取鼠标拖动的矩形区域
let x = event.offsetX;
let y = event.offsetY;
let top = y < this.pointY ? y : this.pointY;
let right = x > this.pointX ? x : this.pointX;
let bottom = y > this.pointY ? y : this.pointY;
let left = x < this.pointX ? x : this.pointX;
this.canvasStyle = {
x:left,
y:top,
w:right - left,
h:bottom - top,
}
//对图片进行裁剪
this.$refs.img.style.clip = `rect(${top}px,${right}px,${bottom}px,${left}px)`;
}
},
}
}
</script>
<style scoped>
.canvasDiv{
position: fixed;
width: 500px;
left: calc(50% - 250px);
top: 100px;
}
.bg{
position: fixed;
width: 100%;
height: 100%;
background: black;
opacity: 0.5;
top: 0;
left: 0;
z-index: 2;
}
.img{
position: absolute;
width: 500px;
top: 0;
left:0;
z-index: 3;
user-select: none;
}
.mouseDiv{
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 10;
}
.canvas{
width: 100%;
}
</style>
效果预览
以上就是全部内容,如果写的不对或者不好还请大佬们谅解!!
clip属性参数详解