likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 65

项目概述

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

相关知识点

实现

实现思路

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

引入 vue-cropper 插件

安装

    npm install vue-cropper
    npm install vue-cropper@next

样式引入

import 'vue-cropper/dist/index.css'

CropperModal 组件封装

<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 为例。

<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 地址base64blob
outputSize裁剪生成图片的质量10.1 ~ 1
outputType裁剪生成图片的格式jpg (jpg 需要传入jpeg)jpegpngwebp
info裁剪框的大小信息truetruefalse
canScale图片是否允许滚轮缩放truetruefalse
autoCrop是否默认生成截图框falsetruefalse
autoCropWidth默认生成截图框宽度容器的 80%0 ~ max
autoCropHeight默认生成截图框高度容器的 80%0 ~ max
fixed是否开启截图框宽高固定比例falsetruefalse
fixedNumber截图框的宽高比例[1, 1][ 宽度 , 高度 ]
full是否输出原图比例的截图falsetruefalse
fixedBox固定截图框大小不允许改变false
canMove上传图片是否可以移动truetruefalse
canMoveBox截图框能否拖动truetruefalse
original上传图片按照原始比例渲染falsetruefalse
centerBox截图框是否被限制在图片里面falsetruefalse
high是否按照设备的dpr 输出等比例图片truetruefalse
infoTruetrue 为展示真实输出图片宽高 false 展示看到的截图框宽高falsetruefalse
maxImgSize限制图片最大宽度和高度20000 ~ max
enlarge图片根据截图框输出比例倍数10 ~ max(建议不要太大不然会卡死的呢)
mode图片默认渲染方式containcontain , cover100px100% auto
limitMinSize裁剪框限制最小区域10Number, Array, String
fillColor导出时背景颜色填充#ffffffwhite

回调方法

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

内置方法

方法说明
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度

以上。

转载自:https://juejin.cn/post/7267090979537223735
评论
请登录