感谢大家的点赞和转发,欢迎大家关注本人的博客。试用期指导,项目开发,简历优化,毕业设计/论文,欢迎添加本人微信。
新人作者,欢迎关注和收藏👏🏻👏🏻
思路:1.先做出一个上传的图片的上传区
<!-- 上传区 -->
<
label
for="fileUp">
<
div
class="upBorder">
<
img
src="../assets/add.png" alt="" />
<
input
ref="fileUp"
type="file"
id="fileUp"
accept="image"
style="display: none"
@change="upload()"
/>
</
div
>
</
label
>
upload() {
let that = this;
console.log(this.$refs.fileUp.files);
if (this.$refs.fileUp.files.length != 0) {
const reader = new FileReader();
reader.readAsDataURL(this.$refs.fileUp.files[0]);
reader.onload = function () {
const img = new Image();
img.src = reader.result;
that.fileList.push(reader.result);
that.$refs.fileUp.value = null; //上传后重置上传input的value,这样才能同时上传相同的图片
console.log(reader.result);
};
this.upLodaOk = true;
}
},
给上传图片的input绑定上ref属性然后通过FileReader构造函数获取上传的文件。
2.完成已上传文件的预览区域
<!-- 预览区域 -->
<
div
class="preView"
v-for="(i, index) in fileList"
:key="index"
ref="preList"
>
<
div
class="fileList" v-if="upLodaOk">
<
img
src="../assets/remove.png"
alt=""
class="remove"
@click="removeProp(index)"
/>
<
img
:src="fileList[index]"
alt=""
class="img"
@click="cut(index)"
ref="imgitem"
/>
</
div
>
</
div
>
在upload方法中将通过FileReader构造函数获取上传的文件push到fileList数组中然后遍历渲染出已经上传的图片列表,并且给每一个图片绑定ref属性。
3.完成图片删除的功能
<!-- 删除弹窗 -->
<div
class="prop"
:style="{
height: this.windowHeight + 'px',
width: this.windowWidth + 'px',
}"
v-if="show"
>
<div class="text">
<img
src="../assets/remove.png"
alt=""
class="close"
@click="removePropClose()"
/>
<div>要删除这张照片吗</div>
<div class="action">
<button class="btn green" @click="removePropClose()">取消</button>
<button class="btn blue" @click="remove()">确定</button>
</div>
</div>
</div>
removeProp(index) {
//v-for循环中的ref是个数组,根据index来取每一个对应的dom元素
this.removeIndex = index;
this.show = true;
},
removePropClose() {
this.show = false;
},
remove() {
this.fileList.splice(this.removeIndex, 1);
this.$refs.fileUp.value = null; //删除后重置上传input的value,这样才能同时上传相同的图片
console.log(this.$refs.fileUp.value);
this.show = false;
},
点击预览图片上的x会触发删除确认弹窗,在removeProp方法中将要删除的图片的Index接收并存储的removeIndex变量中,remove方法中将fileList数组中对应索引的元素去掉并且重置一下上传属性,也可以在每次上传后重置,并且关闭弹窗
4.完成上传时的剪裁功能
<!-- 裁剪蒙层 -->
<div
class="prop center"
v-if="cutProp"
:style="{
height: this.windowHeight + 'px',
width: this.windowWidth + 'px',
}"
>
<div v-html="pre" ref="preimg" class="imgContent"></div>
<div class="cutHandler">
<button class="btn green" @click="cancel()">取消</button>
<button class="btn blue" @click="qdcut()">剪裁</button>
</div>
</div>
cut(index) {
this.selIndex = index;
this.pre = `<
img
src="${this.fileList[index]}"
alt=""
class='cutImg'
/>`;
this.cutProp = true;
console.log(this.$refs);
this.$nextTick(function () {
console.log(this.$refs.preimg.firstChild); //使用nextTick,dom更新完成后才能获取到子节点
this.myCropper = new Cropper(this.$refs.preimg.firstChild, {
aspectRatio: 1 / 1,
dragMode: "move",
outputType: "png", //防止图片背景变黑
crop(event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
});
},
qdcut() {
let cropBox = this.myCropper.getCropBoxData();
console.log(this.myCropper.getCropBoxData()); //打印裁剪数据
let cropCanvas = this.myCropper.getCroppedCanvas({
width: cropBox.width,
height: cropBox.height,
}); //使用画布画出裁剪后的图片
let imgData = cropCanvas.toDataURL(); //导出裁剪后图片的数据
console.log(imgData);
this.fileList.splice(this.selIndex, 1, imgData);
console.log(this.fileList);
this.cutProp = false;
}, //确定裁剪
cancel() {
this.cutProp = false;
}, //取消裁剪
因为本次封装的是预览时裁剪的功能,所以裁剪的是点击预览列表中的文件触发的,cut方法将选择的图片的index存储selIndex变量中,然后通过v-html指令在剪裁弹窗中加载出对应的图片来进行裁剪,裁剪使用cropper.js来进行的,注意使用时要在this.$nextTick方法的回调中来进行剪裁函数的初始化,这样才能获取到通过v-html指令插入的图片。
选择合适的裁剪尺寸后点击确认才加调用qdcut方法,通过cropper.js的内置方法getCropBoxData()获取剪裁的数据,通过getCroppedCanvas()传入对应的数据然后导出剪裁后的图片,将fileList中对应的元素替换即可完成
6.下面附上整个代码,可以直接拿去使用:
<template>
<div>
<!-- 裁剪蒙层 -->
<div
class="prop center"
v-if="cutProp"
:style="{
height: this.windowHeight + 'px',
width: this.windowWidth + 'px',
}"
>
<div v-html="pre" ref="preimg" class="imgContent"></div>
<div class="cutHandler">
<button class="btn green" @click="cancel()">取消</button>
<button class="btn blue" @click="qdcut()">剪裁</button>
</div>
</div>
<!-- 删除弹窗 -->
<div
class="prop"
:style="{
height: this.windowHeight + 'px',
width: this.windowWidth + 'px',
}"
v-if="show"
>
<div class="text">
<img
src="../assets/remove.png"
alt=""
class="close"
@click="removePropClose()"
/>
<div>要删除这张照片吗</div>
<div class="action">
<button class="btn green" @click="removePropClose()">取消</button>
<button class="btn blue" @click="remove()">确定</button>
</div>
</div>
</div>
<!-- 上传区域 -->
<div class="upContent">
<!-- 预览区域 -->
<div
class="preView"
v-for="(i, index) in fileList"
:key="index"
ref="preList"
>
<div class="fileList" v-if="upLodaOk">
<img
src="../assets/remove.png"
alt=""
class="remove"
@click="removeProp(index)"
/>
<img
:src="fileList[index]"
alt=""
class="img"
@click="cut(index)"
ref="imgitem"
/>
</div>
</div>
<!-- 上传区 -->
<label for="fileUp">
<div class="upBorder">
<img src="../assets/add.png" alt="" />
<input
ref="fileUp"
type="file"
id="fileUp"
accept="image"
style="display: none"
@change="upload()"
/>
</div>
</label>
</div>
</div>
</template>
<script>
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
export default {
name: "upload",
data() {
return {
cutProp: false,
pre: "", //准备剪裁的图片
selIndex: "", //选择照片的索引
removeIndex: "", //准备删除的照片的索引
show: false, //删除弹出层
myCropper: null,
afterImg: "",
ingData: null,
upLodaOk: false, //是否展示预览列表
fileList: [], //已经上传图片的列表
};
},
methods: {
upload() {
let that = this;
console.log(this.$refs.fileUp.files);
if (this.$refs.fileUp.files.length != 0) {
const reader = new FileReader();
reader.readAsDataURL(this.$refs.fileUp.files[0]);
reader.onload = function () {
const img = new Image();
img.src = reader.result;
that.fileList.push(reader.result);
that.$refs.fileUp.value = null; //上传后重置上传input的value,这样才能同时上传相同的图片
console.log(reader.result);
};
this.upLodaOk = true;
}
},
removeProp(index) {
//v-for循环中的ref是个数组,根据index来取每一个对应的dom元素
this.removeIndex = index;
this.show = true;
},
removePropClose() {
this.show = false;
},
remove() {
this.fileList.splice(this.removeIndex, 1);
this.$refs.fileUp.value = null; //删除后重置上传input的value,这样才能同时上传相同的图片
console.log(this.$refs.fileUp.value);
this.show = false;
},
cut(index) {
this.selIndex = index;
this.pre = `<img
src="${this.fileList[index]}"
alt=""
class='cutImg'
/>`;
this.cutProp = true;
console.log(this.$refs);
this.$nextTick(function () {
console.log(this.$refs.preimg.firstChild); //使用nextTick,dom更新完成后才能获取到子节点
this.myCropper = new Cropper(this.$refs.preimg.firstChild, {
aspectRatio: 1 / 1,
dragMode: "move",
outputType: "png", //防止图片背景变黑
crop(event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
});
},
qdcut() {
let cropBox = this.myCropper.getCropBoxData();
console.log(this.myCropper.getCropBoxData()); //打印裁剪数据
let cropCanvas = this.myCropper.getCroppedCanvas({
width: cropBox.width,
height: cropBox.height,
}); //使用画布画出裁剪后的图片
let imgData = cropCanvas.toDataURL(); //导出裁剪后图片的数据
console.log(imgData);
this.fileList.splice(this.selIndex, 1, imgData);
console.log(this.fileList);
this.cutProp = false;
}, //确定裁剪
cancel() {
this.cutProp = false;
}, //取消裁剪
},
mounted() {},
computed: {
windowWidth() {
return document.documentElement.clientWidth;
},
windowHeight() {
return document.documentElement.clientHeight;
},
}, //监听屏幕的宽度和高度
};
</script>
<style>
.upBorder {
width: 8rem;
height: 8rem;
border: 1px silver dashed;
display: flex;
justify-content: center;
align-items: center;
}
.upContent {
display: flex;
justify-content: center;
align-items: center;
}
.img {
width: 8rem;
height: 8rem;
}
.fileList {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.remove {
position: absolute;
width: 1rem;
height: 1rem;
top: 0rem;
right: 0rem;
cursor: pointer;
}
.prop {
vertical-align: middle;
position: fixed;
top: 0;
left: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.7);
}
.text {
border-radius: 0.2rem;
top: 50%;
left: 50%;
-webkit-transform: translate3d(-50%, -50%, 0);
transform: translate3d(-50%, -50%, 0);
position: fixed;
z-index: 1000;
color: black;
text-align: center;
background-color: #fff;
padding: 2rem 4rem;
white-space: nowrap;
}
.close {
position: absolute;
top: 0.3rem;
right: 0.3rem;
width: 1rem;
height: 1rem;
}
.action {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1rem;
}
.btn {
font-size: 0.12rem;
color: #fff;
padding: 0.2rem 0.8rem;
}
.blue {
background-color: #1989fa;
border: 1px solid #1989fa;
}
.green {
background-color: #07c160;
border: 1px solid #07c160;
}
.cropper-point.point-se {
width: 5px;
height: 5px;
}
.cropper {
position: fixed;
top: 0;
z-index: 999;
}
/* .cropper-container{
top: 50%;
left: 50%;
-webkit-transform: translate3d(-50%, -50%, 0);
transform: translate3d(-50%, -50%, 0);
} */
.imgContent {
width: 16rem;
height: 16rem;
display: inline-block;
/* top: 50%;
left: 50%;
-webkit-transform: translate3d(-50%, -50%, 0);
transform: translate3d(-50%, -50%, 0); */
}
.cutImg {
display: block;
max-width: 100%;
}
.center {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.cropper-bg {
background: none;
}
.cutHandler {
margin-top: 2rem;
width: 16rem;
text-align: center;
display: flex;
justify-content: space-between;
align-items: center;
}
.cropper-modal {
background: rgba(0, 0, 0, 0);
}
</style>
运行截图:
H5,PC端都可以使用
感谢大家的点赞和转发,欢迎大家关注本人的博客。试用期指导,项目开发,简历优化,毕业设计/论文,欢迎添加本人微信。
新人作者,欢迎关注和收藏👏🏻👏🏻
觉得作者写的不错或者心情愉悦的老板也可以投币打赏,感谢观看,希望能给大家带来帮助