思路: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. PicLite 开发日志 v0.0.2

    PicLite 开发日志 (v0.0.2) 感谢您阅读本片文章! Gitee 地址:https://gitee.com/XiaoQuQuSD/pic-lite. 新增功能 添加复制 url 的格式选项 ...

  2. 数据结构篇(2) ts实现单链表

    interface NodeItem { prev: NodeItem | null next: NodeItem | null data: any } class NodeItem { prev: ...

  3. 干货|SQL语句大全,所有的SQL都在这里了(建议收藏)

    一个执着于技术的公众号 一.基础 1.登录数据库 mysql -uroot -p123123 2.创建数据库 create database <数据库名> 3.删除数据库 drop dat ...

  4. AliIAC 智能音频编解码器:在有限带宽条件下带来更高质量的音频通话体验

    随着信息技术的发展,人们对实时通信的需求不断增加,并逐渐成为工作生活中不可或缺的一部分.每年海量的音视频通话分钟数对互联网基础设施提出了巨大的挑战.尽管目前全球的互联网用户绝大多数均处于良好的网络状况 ...

  5. Java 统计新客户

    上周做了一个订单数据统计的任务,统计的是订单的新客户数量,本文做一个解题过程的记录和整理. 新客户的定义 新客户指的是选取时间段有订单,时间段之前没有订单. 比如下面的订单数据: 时间段 2月1日之前 ...

  6. MyCat应用实战

    下载Mycat https://github.com/MyCATApache/Mycat-Server/releases Mycat基础分库 基础配置 修改server.xml <user na ...

  7. CSP-J游记

    祝大家 CSP-J/CSP-S 稳过第一轮 ~(- ∨ -)~ ~~ 建议扩大110%食用 ~~ 中秋快乐鸭(希望大家不会收到损友送的砖头月饼 : − ) :-) :−)) 咳咳,昨天是我们可爱初赛来 ...

  8. Python 生成图片验证码

    验证码图片生成 #!/usr/bin/env python # -*- coding: utf-8 -*- # refer to `https://bitbucket.org/akorn/wheezy ...

  9. 用Arduino显示颜色序列(u8g2,OLED)

    目录 用Arduino显示颜色序列(u8g2,OLED) 用Arduino显示颜色序列(u8g2,OLED) 提前祝大家新年快乐! 主控:Arduino Mega 2560 硬件:126×64 OLE ...

  10. 基于Kubernetes v1.24.0的集群搭建(二)

    上一篇文章主要是介绍了,每台虚拟机的环境配置.接下来我们开始有关K8S的相关部署. 另外补充一下上一篇文章中的K8S的change​log链接: https://github.com/kubernet ...