基于 Vue3 实现上传图片裁剪功能

项目概述

图片上传时可以实现裁剪功能。

相关知识点

实现

实现思路

  1. 引入 vue-cropper 插件
  2. 封装 vue-cropper 组件,CropperModal
  3. 封装 Upload 组件,并在 Upload 组件中引入 CropperModal 组件
  4. beforeUpload 方法中控制 CropperModal 组件显隐

引入 vue-cropper 插件

安装

vue2 复制代码
    npm install vue-cropper
vue3 复制代码
    npm install vue-cropper@next

样式引入

main.js 复制代码
import 'vue-cropper/dist/index.css'

CropperModal 组件封装

CropperModal.vue 复制代码
<script setup>
import {ref} from 'vue'
import {VueCropper} from 'vue-cropper'

const cropper = ref()
const option = ref({
  img: 'https://avatars2.githubusercontent.com/u/15681693?s=460&v=4',
  // img: '',
  size: 1,
  full: false,
  outputType: 'png',
  canMove: true,
  fixedBox: true,
  fixed: true,
  original: false,
  canMoveBox: true,
  autoCrop: true,
  // 只有自动截图开启 宽度高度才生效
  autoCropWidth: 750,
  autoCropHeight: 340,
  centerBox: true,
  high: true,
  fixedNumber: [1, 1],
  max: 99999,
})
const previews = ref({})

const props = defineProps({
  visible: {
    type: Boolean,
    default: false,
  },
})
const emits = defineEmits(['update:visible'])

const handleOk = () => {
  cropper.value.getCropBlob((data) => {
    console.log('截图数据 >>> ', data);
    fetchUpLoad(data)
  });
}
const handleCancel = (e) => {
  emits('update:visible', false)
};
</script>

<template>
  <a-modal v-model:open="props.visible" @ok="handleOk" @cancel="handleCancel" :closable="false">
    <div class="cut">
      <VueCropper
          ref="cropper"
          :img="option.img"
          :output-size="option.size"
          :output-type="option.outputType"
          :info="true"
          :full="option.full"
          :fixed="option.fixed"
          :fixed-number="option.fixedNumber"
          :can-move="option.canMove"
          :can-move-box="option.canMoveBox"
          :fixed-box="option.fixedBox"
          :original="option.original"
          :auto-crop="option.autoCrop"
          :auto-crop-width="option.autoCropWidth"
          :auto-crop-height="option.autoCropHeight"
          :center-box="option.centerBox"
          :high="option.high"
          mode="cover"
          :max-img-size="option.max"
      />
    </div>
  </a-modal>
</template>

<style scoped lang="less">
.cut {
  height: 300px;
  margin: 30px auto;
}
</style>

Upload 组件封装

这里以 Antd - upload 为例。

Upload.vue 复制代码
<script setup>
import { ref } from 'vue';
import { PlusOutlined, LoadingOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { upLoad } from '@/api/httpApi'
import CropperModal from "@/components/CropperModal.vue";

const fileList = ref([]);
const loading = ref(false);
const imageUrl = ref('');
const modalVisible = ref(false);


const beforeUpload = (file) => {
  const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  if (!isJpgOrPng) {
    message.error('You can only upload JPG file!');
  }
  const isLt2M = file.size / 1024 / 1024 < 2;
  if (!isLt2M) {
    message.error('Image must smaller than 2MB!');
  }
  return isJpgOrPng && isLt2M;
};

const handleUpload = async params => {
  console.log('file >>> ', file);
  const ObjectURL = URL.createObjectURL(params.file);
  console.log('ObjectURL >>> ', ObjectURL);

  const formData = new FormData();
  formData.append('multipartFile', params);
  const res = await upLoad(formData)
  imageUrl.value = res?.filePreviewPathFull
}

</script>

<template>
  <a-button type="primary" @click="modalVisible = true">modal</a-button>
  <a-upload
    v-model:file-list="fileList"
    name="avatar"
    list-type="picture-card"
    class="avatar-uploader"
    :show-upload-list="false"
    action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
    :before-upload="beforeUpload"
    :customRequest="handleUpload"
  >
    <a-image
      v-if="imageUrl"
      :width="200"
      :src="imageUrl"
    />
    <div v-else>
      <loading-outlined v-if="loading"></loading-outlined>
      <plus-outlined v-else></plus-outlined>
      <div class="ant-upload-text">Upload</div>
    </div>
    <CropperModal v-model:visible="modalVisible" />
  </a-upload>
</template>

<style lang="less" scoped>
.avatar-uploader > .ant-upload {
  width: 128px;
  height: 128px;
}
.ant-upload-select-picture-card i {
  font-size: 32px;
  color: #999;
}

.ant-upload-select-picture-card .ant-upload-text {
  margin-top: 8px;
  color: #666;
}
</style>

这里要注意一下,objectURL = URL.createObjectURL(object);在使用时要确保项目运行环境浏览器版本是否可用。若不确定可以使用粗暴大法 - 先将原来的图片上传,拿到 url 地址,再将其裁剪后再做一次上传。当然了,这种方法肯定是不推荐的,但是暂时想不到什么更优雅的方法。

若有小伙伴有更加优雅且兼容性更好地方法,欢迎评论讨论。

相关文档

Props

名称 功能 默认值 可选值
img 裁剪图片的地址 url 地址, base64, blob
outputSize 裁剪生成图片的质量 1 0.1 ~ 1
outputType 裁剪生成图片的格式 jpg (jpg 需要传入jpeg) jpeg, png, webp
info 裁剪框的大小信息 true true, false
canScale 图片是否允许滚轮缩放 true true, false
autoCrop 是否默认生成截图框 false true, false
autoCropWidth 默认生成截图框宽度 容器的 80% 0 ~ max
autoCropHeight 默认生成截图框高度 容器的 80% 0 ~ max
fixed 是否开启截图框宽高固定比例 false true, false
fixedNumber 截图框的宽高比例 [1, 1] [ 宽度 , 高度 ]
full 是否输出原图比例的截图 false true, false
fixedBox 固定截图框大小 不允许改变 false
canMove 上传图片是否可以移动 true true, false
canMoveBox 截图框能否拖动 true true, false
original 上传图片按照原始比例渲染 false true, false
centerBox 截图框是否被限制在图片里面 false true, false
high 是否按照设备的dpr 输出等比例图片 true true, false
infoTrue true 为展示真实输出图片宽高 false 展示看到的截图框宽高 false true, false
maxImgSize 限制图片最大宽度和高度 2000 0 ~ max
enlarge 图片根据截图框输出比例倍数 1 0 ~ max(建议不要太大不然会卡死的呢)
mode 图片默认渲染方式 contain contain , cover, 100px, 100% auto
limitMinSize 裁剪框限制最小区域 10 Number, Array, String
fillColor 导出时背景颜色填充 #ffffff, white

回调方法

  • @realTime 实时预览事件
  • @imgMoving 图片移动回调函数
  • @cropMoving 截图框移动回调函数
  • @imgLoad 图片加载的回调, 返回结果 success, error

内置方法

方法 说明
this.$refs.cropper.startCrop() 开始截图
this.$refs.cropper.stopCrop() 停止截图
this.$refs.cropper.clearCrop() 清除截图
this.$refs.cropper.changeScale() 修改图片大小 正数为变大 负数变小
this.$refs.cropper.getImgAxis() 获取图片基于容器的坐标点
this.$refs.cropper.getCropAxis() 获取截图框基于容器的坐标点
this.$refs.cropper.goAutoCrop 自动生成截图框函数
this.$refs.cropper.rotateRight() 向右边旋转90度
this.$refs.cropper.rotateLeft() 向左边旋转90度

以上。

相关推荐
昔人'9 分钟前
css使用 :where() 来简化大型 CSS 选择器列表
前端·css
昔人'12 分钟前
css `dorp-shadow`
前端·css
流***陌20 分钟前
扭蛋机 Roll 福利房小程序前端功能设计:融合趣味互动与福利适配
前端·小程序
可触的未来,发芽的智生29 分钟前
新奇特:黑猫警长的纳米世界,忆阻器与神经网络的智慧
javascript·人工智能·python·神经网络·架构
烛阴1 小时前
用 Python 揭秘 IP 地址背后的地理位置和信息
前端·python
前端开发爱好者1 小时前
尤雨溪官宣:"新玩具" 比 Prettier 快 45 倍!
前端·javascript·vue.js
why技术1 小时前
从18w到1600w播放量,我的一点思考。
java·前端·后端
欧阳呀1 小时前
Vue+element ui导入组件封装——超级优雅版
前端·javascript·vue.js·elementui
清风徐来QCQ1 小时前
css总结
前端
天***88962 小时前
js封装一个双精度算法实现
开发语言·前端·javascript