vue3+ts实现图片裁剪上传(像素裁剪,比例裁剪)、视频上传

功能背景

产品说想要一个图片上传的功能,并且控制图片比例为16:9,所以就需要用到图片裁剪。
需求点
1、图片按照16:9比例裁剪上传
2、视频上传,大小不可超过150M

参考博客:[vue3 el-upload+vue-cropper 实现头像裁剪上传](https://blog.csdn.net/qq_48850466/article/details/128280223)

效果图

在这里插入图片描述
在这里插入图片描述
视频
在这里插入图片描述

代码实现

用到了一个插件

"vue-cropper": "^1.0.5",

完整代码

<template>
  <el-dialog
    :title="allowVideo?'Image/Video':'Image'"
    v-model="_dialogVisible"
    :show-close="false"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    destroy-on-close
    width="600px"
  >
    <div class="avatar-container" v-loading="uploadLoading">
        <!-- {{dialogVisible}} -->
      <!-- 待上传图片 -->
      <div v-show="!options.img&&!options.video" >
        <el-upload
          class="upload"
          ref="elUpload"
          action="#"
          :on-change="upload"
          :accept="acceptRule"
          :show-file-list="false"
          :auto-upload="false"
        >
          <el-button slot="trigger" size="large"  type="primary" ref="uploadBtn">
            {{allowVideo?'Image/Video':'Image'}}
          </el-button>
        </el-upload>
        <div class="tips-wrods">Support pictures in jpg and png formats, with size no more than 2MB</div>
        <div class="tips-wrods" v-if="allowVideo">Support video in mp4 format, with size no more than 150MB</div>
      </div>
      <!-- <img :src="options.img" alt=""> -->
      <!-- 已上传图片 -->
      <div v-show="options.img" class="avatar-crop">
        <vueCropper
          class="crop-box"
          ref="cropper"
          :img="options.img"
          :autoCrop="options.autoCrop"
          :fixedBox="options.fixedBox"
          :canMoveBox="options.canMoveBox"
          :autoCropWidth="options.autoCropWidth"
          :autoCropHeight="options.autoCropHeight"
          :centerBox="options.centerBox"
          :fixed="options.fixed"
          :fixedNumber="options.fixedNumber"
          :canMove="options.canMove"
          :canScale="options.canScale"
        ></vueCropper>
      </div>
      <div v-show="options.video" class="avatar-crop">
        <video :src="options.video"></video>
      </div>
    </div>
    <template #footer>
    <span  class="dialog-footer">
      <div class="reupload" @click="reupload">
        <span v-show="(options.img||options.video)&&!uploadLoading">重新上传</span>
      </div>
      <div>
        <el-button @click="closeDialog" :disabled="uploadLoading">取 消</el-button>
        <el-button type="primary" @click="getCrop" :disabled="uploadLoading">确 定</el-button>
      </div>
    </span>
    </template>
  </el-dialog>
</template>

<script lang="ts" >
import {ref,reactive,defineComponent,computed,onMounted,} from "vue"
import { VueCropper } from 'vue-cropper'
import 'vue-cropper/dist/index.css'
import type { UploadProps, UploadUserFile } from 'element-plus'
import {uploadFile} from '@/api/request/projectManagement/index'
import {
    ElMessage
} from 'element-plus'
export default defineComponent({
  props: {
    dialogVisible: {
      type: Boolean,
    },
    allowVideo:{
      type: Boolean,
      default:true,
    }
  },
  components:{
    VueCropper
  },
  setup(props, { emit }) {
    const acceptRule = ref('image/png, image/jpeg, image/jpg,.mp4')
    onMounted(()=>{
      if(!props.allowVideo){
        acceptRule.value = 'image/png, image/jpeg, image/jpg'
      }
    })
    

    const _dialogVisible = computed({
      get(){
        return props.dialogVisible
      },
      set(val){
        emit('update:dialogVisible',false)
      }
    })
    // console.log(11111);
    const cropper=ref()
    const uploadBtn=ref()
    const elUpload=ref()
    const options=reactive({
      img: '', // 原图文件
      video: '', // 原图文件
      autoCrop: true, // 默认生成截图框
      fixedBox: false, // 固定截图框大小
      canMoveBox: true, // 截图框可以拖动
      autoCropWidth: 200, // 截图框宽度
      autoCropHeight: 200, // 截图框高度
      fixed: true, // 截图框宽高固定比例
      fixedNumber: [16, 9], // 截图框的宽高比例
      centerBox: true, // 截图框被限制在图片里面
      canMove: false, // 上传图片不允许拖动
      canScale: false // 上传图片不允许滚轮缩放
    })

    // 读取原图
    const fileType = ref('')
    //视频数据
    const videoData = ref('')
    const upload: UploadProps['onChange'] = (file:any, uploadFiles) => {
      // console.log('原始',file);
      const isIMAGE = file.raw.type === 'image/jpeg' || file.raw.type === 'image/png'
      const isLt5M = file.raw.size / 1024 / 1024 < 2

      const isVIDEO = file.raw.type === 'video/mp4'
      const isLt150M = file.raw.size / 1024 / 1024 < 150
      if(isIMAGE&&!isLt5M){
        ElMessage.warning("Picture size cannot exceed 2MB" );
        return false
      }else if(props.allowVideo&&isVIDEO&&!isLt150M){
        ElMessage.warning("Video size cannot exceed 150MB" );
        return false
      }else if(isIMAGE&&isLt5M){
        fileType.value = 'img'
        let reader = new FileReader()
        reader.readAsDataURL(file.raw)
        reader.onload = (e:any) => {
          options.img = e.target.result // base64
        }
        elUpload.value.clearFiles(); //这里处理重新上传时,upload组件change事件错误问题
      }else if(isVIDEO&&isLt150M){
        fileType.value = 'video'
        let reader = new FileReader()
        reader.readAsDataURL(file.raw)
        videoData.value = file.raw
        reader.onload = (e:any) => {
          options.video = e.target.result // base64
        }
        elUpload.value.clearFiles(); //这里处理重新上传时,upload组件change事件错误问题
      }      
    }
      // 获取截图信息
    const getCrop=async ()=> {
      if(fileType.value=='img'){
        // 获取截图的 blob 数据
        await cropper.value.getCropBlob(async data => {
          // console.log('长啥样',data)
          //上传操作
          doUpload(data)
        })
      }else{
        //上传操作
        doUpload(videoData.value)
      }
    }

    //接口上传
    const uploadLoading = ref(false)
    const doUpload = async (data) =>{
      let formData = new FormData();
      //第三个参数是规定以什么为后缀,接口是根据后缀来返回地址格式的
      formData.append("formFile", data);
      // console.log('上传',data)
      uploadLoading.value = true
      let res = await uploadFile(formData)
      if(res.success){
        ElMessage.success({
          message: 'Upload Success'
        });
        let obj = Object.assign({},res.data)
        obj.type = fileType.value
        obj.fileValue = fileType.value=='img'?1:0
        emit('getUrl',obj)
        uploadLoading.value = false
      }else{
        ElMessage.warning({
          message: 'Upload Failed'
        });
        uploadLoading.value = false
      }
      closeDialog()
    }
    // 重新上传
    const reupload=()=> {
      options.img = ''
      options.video = ''
      uploadBtn.value.ref.click()
    }
    // 关闭弹框
    const closeDialog=()=> {
      emit('update:dialogVisible',false)
      options.img = ''
      options.video = ''
    }
 return {
    _dialogVisible,
    options,
    cropper,
    uploadBtn,
    elUpload,
    upload,
    getCrop,
    reupload,
    closeDialog,
    acceptRule,
    uploadLoading,
  }
  }
})


</script>

<style lang="scss" scoped>
.dialog-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 14px;
  .reupload {
    color: #409eff;
    cursor: pointer;
  }
}
.avatar-container {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 560px;
  height: 250px;
  background-color: #f0f2f5;
  margin-right: 10px;
  border-radius: 4px;
  .tips-wrods{
    font-size: 14px;
    font-family: SourceHanSansCN-Medium, SourceHanSansCN;
    font-weight: 500;
    color: #2A2A2A;
    text-align: center;
  }
  .upload {
    text-align: center;
    margin-bottom: 24px;
  }
  .avatar-crop {
    width: 560px;
    height: 250px;
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    .crop-box {
      width: 100%;
      height: 100%;
      border-radius: 4px;
      overflow: hidden;
    }
    video{
      max-width: 560px;
      max-height: 250px;
    }
  }
}
</style>


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_小郑有点困了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值