vue实现图片裁剪后上传

时间:2022-04-23 07:48:00

本文实例为大家分享了vue实现图片裁剪后上传的具体代码,供大家参考,具体内容如下

一、背景

目前负责的系统(商城后台管理系统)里面有这么一个需求,为了配合前台的展示,上传的商品图片比较必须是1:1的正方形。(其它地方有时会有5:4或者16:9的需求,但较少)。所以需要对上传的图片先进行裁剪,并且按要求只能裁剪为1:1,然后在进行上传。

当然,为了兼容系统其它地方有5:4或者16:9的图片比例需求,需要给出一个参数,可以随时控制图片裁剪的比例。

二、使用什么插件实现

使用 vue-cropper 显示,该插件是基于 cropper 的二次封装,简单小巧,更合适vue项目。注意:功能没有 cropper 强大。

三、使用 cropper

3.1 封装一下cropper, 配置自己想要的参数

?
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
<template>
 <div class="Cropper">
 <el-dialog
  :visible.sync="dialogVisible"
  width="740px"
  title="图片裁剪"
  :before-close="handleClose"
  :close-on-click-modal="false">
  <div
  class="cropper-container">
  <div class="cropper-el">
   <vue-cropper
   ref="cropper"
   :img="cropperImg"
   :output-size="option.size"
   :output-type="option.outputType"
   :info="true"
   :can-move="option.canMove"
   :can-move-box="option.canMoveBox"
   :fixed-box="option.fixedBox"
   :auto-crop="option.autoCrop"
   :auto-crop-width="option.autoCropWidth"
   :auto-crop-height="option.autoCropHeight"
   :center-box="option.centerBox"
   :high="option.high"
   :info-true="option.infoTrue"
   @realTime="realTime"
   :enlarge="option.enlarge"
   :fixed="option.fixed"
   :fixed-number="option.fixedNumber"
   :limitMinSize="option.limitMinSize"
   />
  </div>
  <!-- 预览 ==>> 我不需要预览 -->
  <!-- <div class="prive-el">
   <strong>预览:</strong>
   <div class="prive-style" :style="{'width': '200px', 'height': '200px', 'overflow': 'hidden', 'margin': '10px 25px', 'display':'flex', 'align-items' : 'center'}">
   <div class="prive-style" :style="{'width': previews.w + 'px', 'height': previews.h + 'px', 'overflow': 'hidden', 'margin': '10px 25px', 'display':'flex', 'align-items' : 'center'}">
   <div class="preview" :style="previews.div">
    <img :src="previews.url" :style="previews.img">
   </div>
   </div>
   <el-button @click="uploadBth">重新上传</el-button>
  </div> -->
  </div>
  <span
  slot="footer"
  class="dialog-footer">
  <el-button @click="uploadBth">重新上传</el-button>
  <el-button
   @click="handleClose">取 消</el-button>
  <el-button
   type="primary"
   @click="saveImg">确 定</el-button>
  </span>
 </el-dialog>
 </div>
</template>
 
<script>
import { VueCropper } from 'vue-cropper';
export default {
 name: 'Cropper',
 components: {
 VueCropper
 },
 props: {
 dialogVisible: {
  type: Boolean,
  default: false
 },
 imgType: {
  type: String,
  default: 'blob'
 },
 cropperImg: {
  type: String,
  default: ''
 },
 zoomScale: {  // 裁剪比例,默认1:1
  type: Array,
  default: [1, 1]
 }
 },
 data () {
 return {
  previews: {},
  option: {
  img: '', // 裁剪图片的地址
  size: 1, // 裁剪生成图片的质量
  outputType: 'png', // 裁剪生成图片的格式 默认jpg
  canMove: false, // 上传图片是否可以移动
  fixedBox: false, // 固定截图框大小 不允许改变
  canMoveBox: true, // 截图框能否拖动
  autoCrop: true, // 是否默认生成截图框
  // 只有自动截图开启 宽度高度才生效
  autoCropWidth: 500, // 默认生成截图框宽度
  autoCropHeight: 500, // 默认生成截图框高度
  centerBox: true, // 截图框是否被限制在图片里面
  high: false, // 是否按照设备的dpr 输出等比例图片
  enlarge: 1, // 图片根据截图框输出比例倍数
  mode: 'contain', // 图片默认渲染方式
  maxImgSize: 2000, // 限制图片最大宽度和高度
  // limitMinSize: [500,500], // 更新裁剪框最小属性
  limitMinSize: 500, // 更新裁剪框最小属性
  infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
  fixed: true, // 是否开启截图框宽高固定比例 (默认:true)
  // fixedNumber: [1, 1] // 截图框的宽高比例 ==>> 这个参数目前没有作用(作者解释的)
  fixedNumber: this.zoomScale // 截图框的宽高比例
  },
 };
 },
 methods: {
 // 裁剪时触发的方法,用于实时预览
 realTime (data) {
  this.previews = data;
 },
 // 重新上传
 uploadBth () {
  this.$emit('update-cropper');
 },
 // 取消关闭弹框
 handleClose () {
  this.$emit('colse-dialog', false);
 },
 // 获取裁剪之后的图片,默认blob,也可以获取base64的图片
 saveImg () {
  if (this.imgType === 'blob') {
  this.$refs.cropper.getCropBlob(data => {
   this.$emit('upload-img', data);
  });
  } else {
  this.$refs.cropper.getCropData(data => {
   this.uploadFile = data;
   this.$emit('upload-img', data);
  });
  }
 }
 }
};
</script>
 
<style lang="scss" scoped>
.Cropper {
 .cropper-el {
 height: 700px;
 width: 700px;
 flex-shrink: 0;
 }
 .cropper-container {
 display: flex;
 justify-content: space-between;
 .prive-el {
  flex: 1;
  align-self: center;
  text-align: center;
  .prive-style {
  margin: 0 auto;
  flex: 1;
  -webkit-flex: 1;
  display: flex;
  display: -webkit-flex;
  justify-content: center;
  -webkit-justify-content: center;
  overflow: hidden;
  background: #ededed;
  margin-left: 40px;
  }
  .preview {
  overflow: hidden;
  }
  .el-button {
  margin-top: 20px;
  }
 }
 }
}
</style>
<style lang="scss">
.cropper-box-canvas img{
 width: 100% !important;
 height: 100% !important;
}
</style>

3.2 将 el-upload 和 cropper 组合,封装,其他地方可以直接调用

?
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
<template>
 <div>
 <!-- 注意:必须关闭自动上传属性 auto-upload -->
 <el-upload
  :http-request="Upload"
  :multiple="true"
  list-type="picture-card"
  :file-list="productImageList"
  :on-remove="removeImage"
  :limit="12"
  :before-upload="beforeAvatarUpload"
  ref="fileUpload"
  :auto-upload="false"
  :on-change="selectChange"
  action=""
  class="cropper-upload-box"
 >
  <i slot="default" class="el-icon-plus"></i>
 </el-upload>
 
 <cropper
  v-if="showCropper"
  :dialog-visible="showCropper"
  :cropper-img="cropperImg"
  :zoomScale="zoomScale"
  @update-cropper="updateCropper"
  @colse-dialog="closeDialog"
  @upload-img="uploadImg"
 />
 </div>
</template>
 
<script>
import Cropper from "@/components/cropper";
import { client, randomWord } from '@/utils/alioss'
export default {
 name: "CropperUpload",
 data() {
 return {
  productImageList: [],
 
  showCropper: false, // 是否显示裁剪框
  cropperImg: "" // 需要裁剪的图片
 };
 },
 props: {
 defaultImgList: {  // 默认显示的图片列表
  type: Array,
  default: () => []
 },
 zoomScale: {   // 裁剪比例,默认1:1
  type: Array,
  default: [1, 1]
 }
 },
 components: {
 Cropper
 },
 watch: {
 defaultImgList: {
  handler: function(newVal, oldVal){
  this.productImageList = newVal // 赋值
  },
  deep: true
 }
 },
 methods: {
 beforeAvatarUpload(file) {
  const isLt2M = file.size / 1024 / 1024 < 2;  // 原图片
  // const isLt2M = this.uploadFile.size / 1024 / 1024 < 1;  //裁剪后的图片(会比原图片大很多,应该是转成Blob的原因导致)
  if (!isLt2M) {
  this.$message.error("上传图片大小不能超过 2MB!");
  this.noCanUpload = true  // 如果这里被拦截,将自动删除不能上传的图片
  return false
  }
  // return isLt2M
 },
 removeImage(file, fileList) {
  const index = this.productImageList.findIndex(item => {
  return item.uid == file.uid;
  });
  if (index > -1) {
  this.productImageList.splice(index, 1);
  }
  this.$emit('getUploadImg', this.productImageList) // 把最新上传的图片列表返回
 },
 Upload(file) {
  var fileName = `img/${randomWord(
  true,
  20
  )}${+new Date()}${file.file.name.substr(file.file.name.indexOf("."))}`;
  // client().put(fileName, file.file).then(result => {
  client()
  .put(fileName, this.uploadFile)
  .then(result => {
   // 上传裁剪后的图片
   console.log(result);
   this.productImageList.push({
   url: result.url,
   uid: file.file.uid,
   saveUrl: "/" + result.name
   });
   this.showCropper = false;
   this.$emit('getUploadImg', this.productImageList) // 把最新上传的图片列表返回
  })
  .catch(err => {
   this.showCropper = false;
   console.log(err);
  });
 },
 
 // 更新图片
 updateCropper() {
  if(!this.noCanUpload){
  let fileList = this.$refs.fileUpload.uploadFiles  // 获取文件列表
  let index02 = fileList.findIndex(item => {  // 把取消裁剪的图片删除
   return item.uid == this.currentFile.uid;
  });
  fileList.splice(index02, 1)
  }
 
  let index = this.$refs.fileUpload.$children.length - 1;
  this.$refs.fileUpload.$children[index].$el.click();
 },
 // 关闭窗口
 closeDialog() {
  this.showCropper = false;
  
  if(!this.noCanUpload){
  let fileList = this.$refs.fileUpload.uploadFiles  // 获取文件列表
  let index = fileList.findIndex(item => {  // 把取消裁剪的图片删除
   return item.uid == this.currentFile.uid;
  });
  fileList.splice(index, 1)
  }
 },
 // 上传图片
 uploadImg(file) {
  this.uploadFile = file;
  // this.$refs.fileUpload.submit();
 
  // 判断裁剪后图片的宽高
  let img = new Image()
  img.src = window.URL.createObjectURL(file);  // Blob转成url 才能给img显示
  img.onload = () => {
  let minProp = Math.min(img.width, img.height) //裁剪后的图片宽,高 ==> 取最小值
  if( minProp < 500){  // 如果最小值比设置的最小值(默认为500)小
   this.$message.error(`请保证图片短边最小为500`);
   return false
  }
  this.$refs.fileUpload.submit();
  }
 },
 selectChange(file) {
  this.noCanUpload = false
  let files = file.raw;
  var reader = new FileReader();
  reader.onload = e => {
  let data;
  if (typeof e.target.result === "object") {
   // 把Array Buffer转化为blob 如果是base64不需要
   data = window.URL.createObjectURL(new Blob([e.target.result]));
  } else {
   data = e.target.result;
  }
  this.cropperImg = data;
 
  // 图片图片尺寸,如果是正方形,则直接上传;否则调用裁剪
  let img = new Image()
  img.src = this.cropperImg;
  img.onload = () => {
   if(img.width == img.height){ // 本来就是正方形 => 直接上传
   this.uploadFile = files;
   this.$refs.fileUpload.submit(); // 调用上传方法
   }else{
   this.showCropper = true;  // 不是正方形的图片才开启裁剪
   this.currentFile = file  // 保存当前操作裁剪的图片
   }
  }
  };
  // 转化为base64
  // reader.readAsDataURL(file)
  // 转化为blob
  reader.readAsArrayBuffer(files);
  
  // this.showCropper = true;  // 默认开启裁剪
 }
 }
};
</script>
 
<style lang="scss">
.cropper-upload-box{
 display: flex;
 .el-upload{
 width: 148px;
 height: 148px;
 }
}
</style>

3.3 其他页面中调用裁剪组件

?
1
2
3
4
5
6
<!--
 zoomScale:定义的裁剪比例;
 defaultImgList: 默认显示的图片列表
 @getUploadImg:这个事件将得到更新后(上传、删除)的图片列表,在页面中重新赋值给默认的列表变量后就可以做页面中的逻辑处理了
 -->
<cropper-upload :zoomScale='[1,1]' :defaultImgList="productImageList" @getUploadImg="getUploadImg"></cropper-upload>

自此,图片裁剪功能实现!!!

3.4 看一下页面中的效果

vue实现图片裁剪后上传

​​​​​​​以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/halo1416/article/details/111151730

相关文章