灵心如玉,守一生无惧|

SadicZhou

园龄:3年2个月粉丝:7关注:4

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

思路:1.先做出一个上传的图片的上传区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- 上传区 -->
    <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>

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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.完成已上传文件的预览区域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 预览区域 -->
     <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>
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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>
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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端都可以使用
 

本文作者:SadicZhou

本文链接:https://www.cnblogs.com/SadicZhou/p/16460609.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   SadicZhou  阅读(932)  评论(2编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 尚好的青春 孙燕姿
  2. 2 孙燕姿
  3. 3 克卜勒 孙燕姿
- 孙燕姿
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.