在vue3中使用vue-cropper完成头像裁剪上传图片功能

最终效果如下: vue3项目,使用了element-plus组件库,和vue-cropper头像上传插件,实现了一个头像组件 插件文档:www.npmjs.com/package/vue... 同时参考了文章:blog.csdn.net/m0_62317155...

vue-cropper一个奇怪的bug是,使用此插件就不能导入vue的h函数,否则无法正常工作。另外,vue-cropper没有ts类型声明,所以本头像组件的lang是js

本头像组件代码如下,可根据需要复制使用:

html 复制代码
<!-- 修改头像组件 -->
<script setup lang="js">
import Avatar from '@/assets/images/defaultAvatar.png'
import { useUserStore } from '@/stores'
import { ref, reactive, computed } from 'vue'
import { postUploadAvatar } from '@/api/user/index.ts' // 上传头像的api
// 头像图片截取合适尺寸后上传
import 'vue-cropper/dist/index.css'
import { VueCropper } from "vue-cropper"

const root = ref() // 本组件根元素,ElMessage渲染用
const userStore = useUserStore()
// 原本的头像
const originalAvatar = computed(() => {
  if (userStore.userInfo && userStore.userInfo.avatar) return userStore.userInfo.avatar
  else return Avatar
})
// 裁剪上传插件ref
const cropper = ref()
// 裁剪上传头像尺寸插件参数
const options = reactive({
  avatar: Avatar,
  outputType: 'jpg,jpeg,png',
  autoCrop: true,
  fixed: true,
  fixedNumber: [1, 1],
  canMove: false,
  centerBox: true
})
// 当上传图片多于1张时进行处理
const upload = ref()
const onExceed = (files) => {
  upload.value.clearFiles()
  const file = files[0]
  upload.value.handleStart(file)
}
// 是否在进行上传操作
const isUploading = ref(false)
// 当选中了图片要上传时
const onAvatarChange = (uploadObj) => {
  if (uploadObj.raw.size <= (1024 ** 2) * 10) {
    const fileReader = new FileReader()
    fileReader.onload = function (e) {
      options.avatar = e.target.result
    }
    fileReader.readAsDataURL(uploadObj.raw)
    isUploading.value = true
  } else {
    upload.value.clearFiles()
    ElMessage.error({ message: '上传图片大小不能超过10MB', appendTo: root.value })
  }
}
// 点击上传按钮,上传头像
const uploadAvatar = () => {
  // 调用插件的获取截图函数
  cropper.value.getCropBlob(async (data) => {
    try {
      const imgType = data.type.substring(6)
      const tempFile = new File([data], 'avatar.' + imgType, { type: data.type, lastModified: Date.now() })
      const responseUrl = await postUploadAvatar(tempFile)
      userStore.userInfo.avatar = responseUrl
      isUploading.value = false
      ElMessage.success({ message: '修改成功', appendTo: root.value })
    } catch (e) {
      ElMessage.error({ message: '上传失败', appendTo: root.value })
    }
  })
}
// 取消上传头像
const cancelUpload = () => {
  options.avatar = ''
  isUploading.value = false
}
</script>

<template>
  <div style="position: relative;" ref="root">
    <div v-if="!isUploading">
      <el-tooltip effect="light" content="点击上传头像" placement="top">
        <el-upload ref="upload" class="avatar-upload" accept=".jpeg,.jpg,.png" :limit="1" :auto-upload="false"
          :on-exceed="onExceed" :show-file-list="false" :on-change="onAvatarChange">
          <img class="avatar-img" :src="originalAvatar" alt="" @click="changeAvatar">
        </el-upload>
      </el-tooltip>
    </div>
    <div v-else>
      <VueCropper class="avatar-editing" ref="cropper" :img="options.avatar" :outputType="options.outputType"
        :autoCrop="options.autoCrop" :fixed="options.fixed" :fixedNumber="options.fixedNumber"
        :canMove="options.canMove" :centerBox="options.centerBox" />
      <span class="avatar-editing-span" @click="uploadAvatar">上传</span>
      <span class="avatar-editing-span" @click="cancelUpload">取消</span>
    </div>
  </div>
</template>

<style scoped lang="scss">
.avatar-img {
  display: inline-block;
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  margin-bottom: 5px;
  width: 130px;
  height: 130px;
  border-radius: 50%;
  cursor: pointer;
}

.avatar-editing {
  display: inline-block;
  position: absolute;
  left: 50%;
  top: 0;
  transform: translateX(-50%);
  margin-bottom: 5px;
  width: 130px;
  height: 130px;
}

.avatar-editing-span {
  display: inline-block;
  position: absolute;
  left: 50%;
  top: 55px;
  transform: translateX(85px);
  color: #1764FF;
  cursor: pointer;

  &:last-child {
    transform: translateX(130px);
  }
}

:deep(.el-message) {
  transform: translateX(16px);
}
</style>
相关推荐
Larcher27 分钟前
新手也能学会,100行代码玩AI LOGO
前端·llm·html
徐子颐39 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
小月鸭1 小时前
如何理解HTML语义化
前端·html
jump6801 小时前
url输入到网页展示会发生什么?
前端
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
yivifu2 小时前
JavaScript Selection API详解
java·前端·javascript
这儿有一堆花2 小时前
告别 Class 组件:拥抱 React Hooks 带来的函数式新范式
前端·javascript·react.js
十二春秋2 小时前
场景模拟:基础路由配置
前端
六月的可乐2 小时前
实战干货-Vue实现AI聊天助手全流程解析
前端·vue.js·ai编程