vue实现图片裁剪后上传


Posted in Vue.js onDecember 16, 2020

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

一、背景

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

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

二、使用什么插件实现

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

三、使用 cropper

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

<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 组合,封装,其他地方可以直接调用

<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 其他页面中调用裁剪组件

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

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

3.4 看一下页面中的效果

vue实现图片裁剪后上传

​​​​​​​以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Vue.js 相关文章推荐
Vue组件生命周期运行原理解析
Nov 25 Vue.js
Vue如何实现验证码输入交互
Dec 07 Vue.js
为什么推荐使用JSX开发Vue3
Dec 28 Vue.js
vue element和nuxt的使用技巧分享
Jan 14 Vue.js
vue 计算属性和侦听器的使用小结
Jan 25 Vue.js
vue实现拖拽进度条
Mar 01 Vue.js
vue使用节流函数的踩坑实例指南
May 20 Vue.js
Vue elementUI表单嵌套表格并对每行进行校验详解
Feb 18 Vue.js
vue实现移动端div拖动效果
Mar 03 Vue.js
vue实现Toast组件轻提示
Apr 10 Vue.js
vue使用watch监听属性变化
Apr 30 Vue.js
vue实现在data里引入相对路径
Jun 05 Vue.js
Vue-router中hash模式与history模式的区别详解
Dec 15 #Vue.js
vue项目中企业微信使用js-sdk时config和agentConfig配置方式详解
Dec 15 #Vue.js
Vue解决移动端弹窗滚动穿透问题
Dec 15 #Vue.js
8个非常实用的Vue自定义指令
Dec 15 #Vue.js
vue从后台渲染文章列表以及根据id跳转文章详情详解
Dec 14 #Vue.js
Vue在H5 项目中使用融云进行实时个人单聊通讯
Dec 14 #Vue.js
vue的hash值原理也是table切换实例代码
Dec 14 #Vue.js
You might like
改变Apache端口等配置修改方法
2008/06/05 PHP
php之XML转数组函数的详解
2013/06/07 PHP
php无限分类且支持输出树状图的详细介绍
2013/06/19 PHP
WordPress中用于获取及自定义头像图片的PHP脚本详解
2015/12/17 PHP
基于PHP实现微信小程序客服消息功能
2019/08/12 PHP
锋利的jQuery 要点归纳(一) jQuery选择器
2010/03/21 Javascript
在多个页面使用同一个HTML片段《续》
2011/03/04 Javascript
Jquery颜色选择器ColorPicker实现代码
2012/11/14 Javascript
jQuery实现用户注册的表单验证示例
2013/08/28 Javascript
innerText 使用示例
2014/01/23 Javascript
javascript中var的重要性分析
2015/02/11 Javascript
JavaScript使用Prototype实现面向对象的方法
2015/04/14 Javascript
Bootstrap3 内联单选和多选框
2016/12/29 Javascript
纯js实现图片匀速淡入淡出效果
2017/08/22 Javascript
bootstrap fileinput实现文件上传功能
2017/08/23 Javascript
node实现登录图片验证码的示例代码
2018/04/20 Javascript
详解elementui之el-image-viewer(图片查看器)
2019/08/30 Javascript
微信小程序分享小程序码的生成(带参数)以及参数的获取
2020/03/25 Javascript
为什么JavaScript中0.1 + 0.2 != 0.3
2020/12/03 Javascript
Javascript中的奇葩知识,你知道吗?
2021/01/25 Javascript
python处理xml文件的方法小结
2017/05/02 Python
python 3.7.0 安装配置方法图文教程
2018/08/27 Python
Python装饰器用法实例分析
2019/01/14 Python
python图片二值化提高识别率代码实例
2019/08/24 Python
opencv3/C++实现视频读取、视频写入
2019/12/11 Python
深入探究HTML5的History API
2015/07/09 HTML / CSS
世界上最大的餐具公司:Oneida
2016/12/17 全球购物
有abstract方法的类一定要用abstract修饰吗
2016/03/14 面试题
法人代表委托书
2014/04/04 职场文书
文秘应届生求职信
2014/07/05 职场文书
物业消防安全责任书
2014/07/23 职场文书
教师学习党的群众路线教育实践活动心得体会
2014/10/31 职场文书
《七律·长征》教学反思
2016/02/16 职场文书
iPhone13将有八大升级
2021/04/15 数码科技
解决Mysql中的innoDB幻读问题
2022/04/29 MySQL
JS实现刷新网页后之前浏览位置保持不变示例详解
2022/08/14 Javascript