第七穿插连第XXXX名士兵

记录学习的点滴,成长的历程。

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

统计

Vue2组件、功能插件实例运用 - vue-cropper(图片裁剪)

vue-cropper

简介:一个优雅的图片裁剪插件。就一句话,没得更多的废话

官网:https://github.com/xyxiao001/vue-cropper

1、组件的安装

  npm安装

npm install vue-cropper --save-dev

  yarn安装

yarn add vue-cropper    --save-dev

2、使用

  2.1 主页面图片选择(element-ui上传组件)

复制代码
<!--图片选择(element-ui上传组件)-->
<el-upload
  class="upload-demo"
  ref="upload"
  action=""
  drag
  :auto-upload="false"
  :multiple="false"
  :limit="1"
  :show-file-list="false"
  :on-change="changeFile"
>
  <i class="el-icon-upload"></i>
  <div class="el-upload__text">点击上传</div>
  <div class="el-upload__tip">支持绝大多数图片格式,单张图片最大支持5MB</div>
</el-upload>
复制代码

复制代码
// changeFile方法
/**
 * 选择图片及限制图片大小
 */
changeFile(file, fileList) {
  const isLt5M = file.size / 1024 / 1024 < 5
  if (!isLt5M) {
    this.$message.error('上传文件大小不能超过 5MB!')
    return false
  }
  this.fileInfo = file
  // 图片选择好后,将图片数据转换成Blob本地地址并赋值对应参数后,最后显示图片裁剪框
  this.$nextTick(() => {
    this.dialogTitle = '基于vue的图片裁剪'
    this.dialogWidth = '500'
    this.dialogPar = {
      imgUrl: URL.createObjectURL(file.raw), // 裁剪组件无法直接访问图片文件信息,故将文件信息转成本地Blob数据地址,让裁剪组件可以直接访问
      fileInfo: file
    }
    this.dialogTemplate = 'imageCropper' // 本地封装的图片裁剪弹出窗组件
  })
},
复制代码

  2.2 图片裁剪弹出窗组件

  组件的具体使用参照官方文档 https://github.com/xyxiao001/vue-cropper,vue2项目内全局引用 和 组件内引用 用法不一样

  

  本次Demo采用组件内引入,组件的具体封装,各部分代码如下:

  2.2.1 template模板:

复制代码
<!--VueCropper图片裁剪组件加载,及该组件部分常用方法调用-->
<div class="el-col el-col-24">
  <div class="cropper-w">
    <div class="cropper text-align-c">
      <VueCropper
        ref="cropper"
        :img="option.img"
        :outputSize="option.size"
        :outputType="option.outputType"
        :info="true"
        :full="option.full"
        :canMove="option.canMove"
        :canMoveBox="option.canMoveBox"
        :original="option.original"
        :autoCrop="option.autoCrop"
        :fixed="option.fixed"
        :fixedNumber="option.fixedNumber"
        :centerBox="option.centerBox"
        :infoTrue="option.infoTrue"
        :fixedBox="option.fixedBox"
      ></VueCropper>
    </div>
  </div>
  <div class="el-col el-col-24 flex-layout btn-w">
    <el-button type="success" @click.stop="rotateLeft">向左旋转</el-button>
    <el-button type="info" @click.stop="rotateRight">向右旋转</el-button>
    <el-button type="primary" @click.stop="getCropData">获取base64数据</el-button>
    <el-button type="primary" @click.stop="getCropBlob">获取blob数据</el-button>
  </div>
</div>
复制代码

  2.2.2 配套样式:

复制代码
<style scoped lang="less">
/*配套样式*/
.cropper-w {
  .cropper {
    width: auto;
    height: 50vh;
  }
}
.btn-w {
  margin-top: 20px;
}
</style>
复制代码

  2.2.3 组件引用及功能函数:

复制代码
<script>
// 具体使用参照官方文档 https://github.com/xyxiao001/vue-cropper
// vue2 全局引用 和 组件内引用 用法不一样
import { VueCropper } from 'vue-cropper'

export default {
  name: 'imageCropper',
  components: { VueCropper },
  props: {
    dialogPar: {
      type: Object,
      required: false,
      default: () => {
        return {}
      }
    }
  },
  data() {
    return {
      // 裁剪组件基础配置option,更多属性或更多具体说明参考官方文档
      option: {
        img: this.dialogPar.imgUrl, // 裁剪图片的地址
        info: true, // 裁剪框的大小信息
        outputSize: 0.8, // 裁剪生成图片的质量
        outputType: 'jpeg', // 裁剪生成图片的格式(jpg(jpg 需要传入jpeg))
        canScale: false, // 图片是否允许滚轮缩放(这个属性貌似没得用,不管设置true,false都可以滚轮缩放)
        autoCrop: true, // 是否默认生成截图框
        // autoCropWidth: 300, // 默认生成截图框宽度
        // autoCropHeight: 200, // 默认生成截图框高度
        fixedBox: true, // 固定截图框大小 不允许改变
        fixed: true, // 是否开启截图框宽高固定比例
        fixedNumber: [1, 1], // 截图框的宽高比例(这是比例,按两个值的比值大小进行截图框的宽高的设置,[1,1]和[100,100]是一样的)
        full: true, // 是否输出原图比例的截图
        canMove: true, // 上传图片是否可以移动
        canMoveBox: false, // 截图框能否拖动
        original: false, // 上传图片按照原始比例渲染
        centerBox: false, // 截图框是否被限制在图片里面
        infoTrue: true // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
      },
      base64Data: {
        dataURL: '', // 用url方式表示的base64图片数据
        type: 'image/jpeg' //文件类型
      }
    }
  },
  methods: {
    /**
     * 向左边旋转90度
     */
    rotateLeft() {
      this.$refs.cropper.rotateLeft() // 只能固定向左边旋转90度,不接受设定旋转角度
    },
    /**
     * 向右边旋转90度
     */
    rotateRight() {
      this.$refs.cropper.rotateRight() // 只能固定向右边旋转90度,不接受设定旋转角度
    },

    /**
     * 将 base64数据 直接转换为 file对象
     */
    dataUrlToFile: function (dataUrl, fileName) {
      let arr = dataUrl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n)
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n)
      }
      return new File([u8arr], fileName, { type: mime })
    },

    /**
     * base64 转Blob
     */
    base64ToBlob(base64Data) {
      let arr = base64Data.dataURL.split(',')
      let mime = arr[0].match(/:(.*?);/)[1] || base64Data.type
      // 去掉url的头,并转化为byte
      let bytes = window.atob(arr[1])
      // 处理异常,将ascii码小于0的转换为大于0
      let ab = new ArrayBuffer(bytes.length)
      // 生成视图(直接针对内存):8位无符号整数,长度1个字节
      let ia = new Uint8Array(ab)
      for (let i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i)
      }
      return new Blob([ab], {
        type: mime
      })
    },

    /**
     * 获取截图的 base64 数据
     */
    getCropData() {
      this.$refs.cropper.getCropData(data => {
        // do something
        // 由于服务端文件上传接口统一接收file对象,从而 base64编码 数据在提交前需要将编码数据转为file对象
        // 将 base64编码数据 转成 file对象,有两种方式
        // 1、直接用 new File()方法 把 base64数据 转成 file对象
        let file = this.dataUrlToFile(data, this.dialogPar.fileInfo.name)

        // 2、先将 base64编码数据 转成 blob数据,然后用file对象内置方法,将blob数据转成file对象
        // this.base64Data.dataURL = data
        // let blobData = this.base64ToBlob(this.base64Data)
        // const file = new window.File([blobData], this.dialogPar.fileInfo.name, { type: data.type })

        // 获取图片数据后,将数据上传给父组件(imgUrl:图片预览链接地址,fileData:截取的图片数据)
        // 由于 base64 链接数据,img标签src属性可以直接使用,故此处不做处理
        this.$emit('emitPar', { imgUrl: data, fileData: file })
      })
    },

    /**
     * 获取截图的 blob 数据
     */
    getCropBlob() {
      this.$refs.cropper.getCropBlob(data => {
        // do something
        // 由于服务端文件上传接口统一接收file对象,从而数据在提交前需要将blob数据转为file对象,转换方法如下:
        const file = new window.File([data], this.dialogPar.fileInfo.name, { type: data.type })
        // 获取图片数据后,将数据上传给父组件(imgUrl:图片本地预览 blob数据地址,fileData:截取的图片数据 转换成的file对象)
        this.$emit('emitPar', { imgUrl: URL.createObjectURL(data), fileData: file })
      })
    }
  }
}
</script>
复制代码

  2.3 裁剪好的图片 文件上传

  在 2.2介绍的组件中 我们已经裁剪出我们需要的图片,并通过 $emit方法 将对应的数据上传到了父组件(主页面)中,此时就需要调用上传接口将图片信息上传到对应的服务器了,从而更新对应的所需图片数据。

复制代码
/**
 * 根据子组件上传值,控制Dialog 对话框显隐
 * @param {Object} data:子组件(图片裁剪)上传到父组件数据<br/>
 * 例: {state: 控制状态(默认关闭:false)}
 */
receiveEmitPar(data) {
  this.dialogTemplate = ''
  if (data && data.imgUrl) {
    this.previewImgUrl = data.imgUrl  // 本地预览地址
    this.fileInfo.raw = data.fileData // 由于文件上传(包括文件和图片)http request统一封装为读取raw属性,故此处将子组件转换好的file对象赋值给raw属性
    this.$http.vueCropper.uploadPublicFile(this.fileInfo).then(res => {
      this.serverAddressImgUrl = res.realUrl  // 上传后返回的服务器地址 图片预览
    })
  }
  this.$refs.upload.uploadFiles = []
}
复制代码

  上传返回的服务器图片地址

  本地预览和上传后服务器图片地址预览

3、将 Blob数据类型转成File类型,base64编码数据 转成File类型

上文中因为服务端文件上传接口统一接收file对象,所以裁剪后的图片数据不管是 blob数据类型 还是 base64编码数据,都要转成file对象才能上传。而这里面不管是将 blob数据类型直接转成file对象,还是将 base64编码数据先转成 blob数据类型再转成file对象,都绕不开 File()构造函数

File()构造器 创建新的 File 对象实例,语法:

var myFile = new File(bits, name[, options]);

4、总结

总的来说 vue-cropper 是一款很优秀的图片裁剪组件,可以让我们在日常开发中涉及到一些图片裁剪需求时减少很大工作量;但美中不足的是,该款组件只能裁剪出正方形或长方形的图片【虽然后面可以用样式 border-radius: 50% 让图片显示成圆形】,但有时候有些奇葩产品就是要直接裁剪成圆形,这个时候就比较难搞了,又不得不重新造轮子。

最后,非常感谢这些封装插件的大佬,正是这些轮子的问世,使得前端开发减少了不知多少工作量,也让这些功能看起来更高大上更美观了。

posted on   第七穿插连第XX名士兵  阅读(2023)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示