在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>
相关推荐
不懂英语的程序猿几秒前
【SF顺丰】顺丰开放平台API对接(注册、API测试篇)
前端·后端
前端九哥4 分钟前
🚀 新一代图片格式 AVIF,对比 WebP/JPEG 有多强?【附真实图片对比】
前端
谦谦橘子5 分钟前
服务端渲染原理解析姐妹篇
前端·javascript·react.js
i编程_撸码6 分钟前
webpack详细打包配置,包含性能优化、资源处理...
前端
小小小小宇7 分钟前
React 中 useMemo 和 useCallback 源码原理
前端
Trae首席推荐官11 分钟前
Trae 版本更新|支持自定义智能体、MCP等,打造个人专属“AI 工程师”
前端·trae
木三_copy11 分钟前
前端截图工具--html2canvas和html-to-image的一些踩坑
前端
小桥风满袖13 分钟前
Three.js-硬要自学系列7 (查看几何体顶点位置和索引、旋转,缩放,平移几何体)
前端·css·three.js
kim__jin15 分钟前
Vue3 使用项目内嵌iFrame
前端
独立开阀者_FwtCoder28 分钟前
# 一天 Star 破万的开源项目「GitHub 热点速览」
前端·javascript·面试