思路: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.下面附上整个代码,可以直接拿去使用:

  1 <template>
2 <div>
3 <!-- 裁剪蒙层 -->
4 <div
5 class="prop center"
6 v-if="cutProp"
7 :style="{
8 height: this.windowHeight + 'px',
9 width: this.windowWidth + 'px',
10 }"
11 >
12 <div v-html="pre" ref="preimg" class="imgContent"></div>
13 <div class="cutHandler">
14 <button class="btn green" @click="cancel()">取消</button>
15 <button class="btn blue" @click="qdcut()">剪裁</button>
16 </div>
17 </div>
18 <!-- 删除弹窗 -->
19 <div
20 class="prop"
21 :style="{
22 height: this.windowHeight + 'px',
23 width: this.windowWidth + 'px',
24 }"
25 v-if="show"
26 >
27 <div class="text">
28 <img
29 src="../assets/remove.png"
30 alt=""
31 class="close"
32 @click="removePropClose()"
33 />
34 <div>要删除这张照片吗</div>
35 <div class="action">
36 <button class="btn green" @click="removePropClose()">取消</button>
37 <button class="btn blue" @click="remove()">确定</button>
38 </div>
39 </div>
40 </div>
41 <!-- 上传区域 -->
42 <div class="upContent">
43 <!-- 预览区域 -->
44 <div
45 class="preView"
46 v-for="(i, index) in fileList"
47 :key="index"
48 ref="preList"
49 >
50 <div class="fileList" v-if="upLodaOk">
51 <img
52 src="../assets/remove.png"
53 alt=""
54 class="remove"
55 @click="removeProp(index)"
56 />
57 <img
58 :src="fileList[index]"
59 alt=""
60 class="img"
61 @click="cut(index)"
62 ref="imgitem"
63 />
64 </div>
65 </div>
66 <!-- 上传区 -->
67 <label for="fileUp">
68 <div class="upBorder">
69 <img src="../assets/add.png" alt="" />
70 <input
71 ref="fileUp"
72 type="file"
73 id="fileUp"
74 accept="image"
75 style="display: none"
76 @change="upload()"
77 />
78 </div>
79 </label>
80 </div>
81 </div>
82 </template>
83 <script>
84 import Cropper from "cropperjs";
85 import "cropperjs/dist/cropper.css";
86 export default {
87 name: "upload",
88 data() {
89 return {
90 cutProp: false,
91 pre: "", //准备剪裁的图片
92 selIndex: "", //选择照片的索引
93 removeIndex: "", //准备删除的照片的索引
94 show: false, //删除弹出层
95 myCropper: null,
96 afterImg: "",
97 ingData: null,
98 upLodaOk: false, //是否展示预览列表
99 fileList: [], //已经上传图片的列表
100 };
101 },
102 methods: {
103 upload() {
104 let that = this;
105 console.log(this.$refs.fileUp.files);
106 if (this.$refs.fileUp.files.length != 0) {
107 const reader = new FileReader();
108 reader.readAsDataURL(this.$refs.fileUp.files[0]);
109 reader.onload = function () {
110 const img = new Image();
111 img.src = reader.result;
112 that.fileList.push(reader.result);
113 that.$refs.fileUp.value = null; //上传后重置上传input的value,这样才能同时上传相同的图片
114 console.log(reader.result);
115 };
116 this.upLodaOk = true;
117 }
118 },
119 removeProp(index) {
120 //v-for循环中的ref是个数组,根据index来取每一个对应的dom元素
121 this.removeIndex = index;
122 this.show = true;
123 },
124 removePropClose() {
125 this.show = false;
126 },
127 remove() {
128 this.fileList.splice(this.removeIndex, 1);
129 this.$refs.fileUp.value = null; //删除后重置上传input的value,这样才能同时上传相同的图片
130 console.log(this.$refs.fileUp.value);
131 this.show = false;
132 },
133 cut(index) {
134 this.selIndex = index;
135 this.pre = `<img
136 src="${this.fileList[index]}"
137 alt=""
138 class='cutImg'
139 />`;
140 this.cutProp = true;
141 console.log(this.$refs);
142 this.$nextTick(function () {
143 console.log(this.$refs.preimg.firstChild); //使用nextTick,dom更新完成后才能获取到子节点
144 this.myCropper = new Cropper(this.$refs.preimg.firstChild, {
145 aspectRatio: 1 / 1,
146 dragMode: "move",
147 outputType: "png", //防止图片背景变黑
148 crop(event) {
149 console.log(event.detail.x);
150 console.log(event.detail.y);
151 console.log(event.detail.width);
152 console.log(event.detail.height);
153 console.log(event.detail.rotate);
154 console.log(event.detail.scaleX);
155 console.log(event.detail.scaleY);
156 },
157 });
158 });
159 },
160 qdcut() {
161 let cropBox = this.myCropper.getCropBoxData();
162 console.log(this.myCropper.getCropBoxData()); //打印裁剪数据
163 let cropCanvas = this.myCropper.getCroppedCanvas({
164 width: cropBox.width,
165 height: cropBox.height,
166 }); //使用画布画出裁剪后的图片
167 let imgData = cropCanvas.toDataURL(); //导出裁剪后图片的数据
168 console.log(imgData);
169 this.fileList.splice(this.selIndex, 1, imgData);
170 console.log(this.fileList);
171 this.cutProp = false;
172 }, //确定裁剪
173 cancel() {
174 this.cutProp = false;
175 }, //取消裁剪
176 },
177 mounted() {},
178 computed: {
179 windowWidth() {
180 return document.documentElement.clientWidth;
181 },
182 windowHeight() {
183 return document.documentElement.clientHeight;
184 },
185 }, //监听屏幕的宽度和高度
186 };
187 </script>
188 <style>
189 .upBorder {
190 width: 8rem;
191 height: 8rem;
192 border: 1px silver dashed;
193 display: flex;
194 justify-content: center;
195 align-items: center;
196 }
197 .upContent {
198 display: flex;
199 justify-content: center;
200 align-items: center;
201 }
202 .img {
203 width: 8rem;
204 height: 8rem;
205 }
206
207 .fileList {
208 position: relative;
209 display: flex;
210 flex-direction: column;
211 justify-content: center;
212 align-items: center;
213 }
214 .remove {
215 position: absolute;
216 width: 1rem;
217 height: 1rem;
218 top: 0rem;
219 right: 0rem;
220 cursor: pointer;
221 }
222 .prop {
223 vertical-align: middle;
224 position: fixed;
225 top: 0;
226 left: 0;
227 z-index: 999;
228 background-color: rgba(0, 0, 0, 0.7);
229 }
230 .text {
231 border-radius: 0.2rem;
232 top: 50%;
233 left: 50%;
234 -webkit-transform: translate3d(-50%, -50%, 0);
235 transform: translate3d(-50%, -50%, 0);
236 position: fixed;
237 z-index: 1000;
238 color: black;
239 text-align: center;
240 background-color: #fff;
241 padding: 2rem 4rem;
242 white-space: nowrap;
243 }
244 .close {
245 position: absolute;
246 top: 0.3rem;
247 right: 0.3rem;
248 width: 1rem;
249 height: 1rem;
250 }
251 .action {
252 display: flex;
253 justify-content: space-between;
254 align-items: center;
255 margin-top: 1rem;
256 }
257 .btn {
258 font-size: 0.12rem;
259 color: #fff;
260 padding: 0.2rem 0.8rem;
261 }
262 .blue {
263 background-color: #1989fa;
264 border: 1px solid #1989fa;
265 }
266 .green {
267 background-color: #07c160;
268 border: 1px solid #07c160;
269 }
270 .cropper-point.point-se {
271 width: 5px;
272 height: 5px;
273 }
274 .cropper {
275 position: fixed;
276 top: 0;
277 z-index: 999;
278 }
279
280 /* .cropper-container{
281 top: 50%;
282 left: 50%;
283 -webkit-transform: translate3d(-50%, -50%, 0);
284 transform: translate3d(-50%, -50%, 0);
285 } */
286 .imgContent {
287 width: 16rem;
288 height: 16rem;
289 display: inline-block;
290 /* top: 50%;
291 left: 50%;
292 -webkit-transform: translate3d(-50%, -50%, 0);
293 transform: translate3d(-50%, -50%, 0); */
294 }
295 .cutImg {
296 display: block;
297 max-width: 100%;
298 }
299 .center {
300 display: flex;
301 flex-direction: column;
302 justify-content: center;
303 align-items: center;
304 }
305 .cropper-bg {
306 background: none;
307 }
308 .cutHandler {
309 margin-top: 2rem;
310 width: 16rem;
311 text-align: center;
312 display: flex;
313 justify-content: space-between;
314 align-items: center;
315 }
316 .cropper-modal {
317 background: rgba(0, 0, 0, 0);
318 }
319 </style>

运行截图:

 H5,PC端都可以使用
 

vue封装原生的可预览裁剪上传图片插件H5,PC端都可以使用的更多相关文章

  1. Jcrop+uploadify+php实现上传头像预览裁剪

    最近由于项目需要,所以做了一个上传头像预览并且可以预览裁剪的功能,大概思路是上传的图片先保存到服务器,然后通过ajax从服务器获取到图片信息,再利用Jcrop插件进行裁剪,之后通过PHP获取到的四个裁 ...

  2. previewImage.js图片预览缩放保存插件

    previewImage.js好用的图片预览缩放保存插件

  3. 上传预览图片的插件jquery-fileupload

    上传预览图片的插件jquery-fileupload github地址:https://github.com/blueimp/jQuery-File-Upload 中文文档:http://www.jq ...

  4. [RN] React Native 使用 图片预览和放大 插件 react-native-image-zoom-viewer 过程中,放大报错问题

    React Native 使用 图片预览和放大 插件 react-native-image-zoom-viewer 过程中,放大报错问题 报错如下: Cannot record touch end w ...

  5. vue项目上传Github预览

    最近在用Vue仿写cnode社区,想要上传到github,并通过Github pages预览,在这个过程中遇到了一些问题,因此写个笔记,以便查阅. 完成Vue项目以后,在上传到github之前,需要修 ...

  6. 在 vue 中使用 vieiwer 图片预览插件

    https://blog.csdn.net/WestLonly/article/details/79801800?utm_source=blogxgwz0 首先,感谢原作者 官网链接 github地址 ...

  7. vue图片点击放大预览

    第一种:viewerjs使用介绍(PC.移动端都兼容) 1.先安装依赖 npm install v-viewer --save 2.main.js内引用并注册调用 //main.js import V ...

  8. webform的原生操作图片预览和上传

    1.使用input标签进行图片操作,input的标签有一个accept属性,accept 属性只能与 <input type="file"> 配合使用.它规定能够通过文 ...

  9. 【VUE】图片预览放大缩小插件

    From: https://www.jianshu.com/p/e3350aa1b0d0 在看项目时,突然看到预览图片的弹窗,感觉好僵硬,不能放大,不能切换,于是便在网上找下关于图片预览的插件,有找到 ...

随机推荐

  1. JVM垃圾回收篇

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. 基础概念 GC=jvm垃圾回收,垃圾回收机制是由垃圾回收器Garbage ...

  2. (数据科学学习手札136)Python中基于joblib实现极简并行计算加速

    本文示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 我们在日常使用Python进行各种数据计算 ...

  3. Citrix Virtual Apps and Desktops 7 2203 LTSR虚拟云桌面单机教程

    哈喽大家好,欢迎来到虚拟化时代君(XNHCYL). 大家好,我是虚拟化时代君,一位潜心于互联网的技术宅男.这里每天为你分享各种你感兴趣的技术.教程.软件.资源.福利--(每天更新不间断) 一 .主要内 ...

  4. 多态——JavaSE基础

    多态 同一个方法可以根据对象的不同采取不同的动作 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多 基本条件: 有继承关系 子类重写父类方法 父类引用指向子类对象Father f1 = ne ...

  5. django框架10

    内容概要 ajax结合sweetalert forms组件钩子函数 forms组件字段参数 forms组件字段类型 forms组件源码分析 cookie与session简介 django操作cooki ...

  6. 3.C++逐行读取txt文件数据,利用getline -windows编程

      引言:今天学会了getline的用法,顺手编写一个逐行读取txt文件的程序.关于getline的用法可以看我之前的博客:2.C++标准库函数:getline函数 定界流输入截取函数 -zobol的 ...

  7. 【SpringBoot】快速入门

    博客主页:准Java全栈开发工程师 00年出生,即将进入职场闯荡,目标赚钱,可能会有人觉得我格局小.觉得俗,但不得不承认这个世界已经不再是以一条线来分割的平面,而是围绕财富旋转的球面,成为有钱人不是为 ...

  8. 这不会又是一个Go的BUG吧?

    hello,大家好呀,我是小楼. 最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响. 出问题的是Go的读写锁,如果你是写Java的,不必划走,更要看看本文,本文的重点 ...

  9. 记一次APP渗透登录验证绕过思路

    前言: 起初是抓包时候查看返回状态码不一致,所以觉得是否可以通过修改状态码来达到绕过的目的,但是拦截响应包再替换手速不够,技术大哥就去搜了下,找到了一个方法,可以自动替换响应包内容. 在偏下方一点的地 ...

  10. opencv-python获取视频信息

    代码 import cv2 if __name__ == '__main__': # 读取视频 capture = cv2.VideoCapture('./videos/person.mp4') # ...