Vue3 + cropper 实现裁剪头像的功能(裁剪效果可实时预览、预览图可下载、预览图可上传到SpringBoot后端、附完整的示例代码和源代码)
创始人
2024-11-14 07:06:24
0

文章目录

  • 0. 前言
  • 1. 裁剪效果(可实时预览)
  • 2. 安装 cropper
  • 3. 引入 Vue Cropper
    • 3.1 局部引入(推荐使用)
    • 3.2 全局引入
  • 4. 在代码中使用
    • 4.1 template部分
    • 4.2 script部分
  • 5. 注意事项
  • 6. SpringBoot 后端接收图片
    • 6.1 UserController.java
    • 6.2 Result.java
  • 7. 完整的示例代码
    • 7.1 Homeview.vue
    • 7.2 request.js
    • 7.3 main.js
    • 7.4 vite.config.js
  • 8. 完整的源代码

0. 前言

裁剪头像的需求十分常见,主要目的是为了统一用户头像的尺寸,避免因为用户上传的图片尺寸大小不一致导致页面布局出现问题

高效实现需求的方法,就是避免重复造轮子,在这里推荐使用 cropper 实现头像裁剪功能 (原因是 cropper 功能强大、上手简单、文档详细)


cropper 的Gitee地址:vue-cropper

cropper Vue3在线示例:cropper Vue3在线示例

1. 裁剪效果(可实时预览)

在这里插入图片描述

2. 安装 cropper

# npm 安装 npm install vue-cropper@next 
# yarn 安装 yarn add vue-cropper@next 

3. 引入 Vue Cropper

3.1 局部引入(推荐使用)

哪个组件需要使用 Vue Cropper,就在哪个组件导入

import 'vue-cropper/dist/index.css' import { VueCropper }  from 'vue-cropper' 

3.2 全局引入

main.js 文件

import VueCropper from 'vue-cropper' import 'vue-cropper/dist/index.css'  const app = createApp(App) app.use(VueCropper) app.mount('#app') 

4. 在代码中使用

注意事项:

要为 组件设置宽和高,并用一个外层容器包裹 组件

4.1 template部分

 

4.2 script部分

const option = ref({   autoCrop: true, // 是否默认生成截图框   autoCropHeight: '240px', // 默认生成截图框宽度(默认值:容器的 80%, 可选值:0 ~ max), 真正裁剪出来的图片的宽度为 autoCropHeight * 1.25   autoCropWidth: '240px', // 默认生成截图框宽度(默认值:容器的 80%, 可选值:0 ~ max), 真正裁剪出来的图片的宽度为 autoWidth * 1.25   canMove: true, // 上传图片是否可以移动   canScale: true, // 图片是否允许滚轮缩放   centerBox: true, // 截图框是否被限制在图片里面   fixed: true, // 是否固定截图框的宽高比例   fixedBox: true, // 是否固定截图框大小   fixedNumber: [1, 1], // 截图框的宽高比例([ 宽度 , 高度 ])   img: 'https://img2.baidu.com/it/u=2339635883,2403687892&fm=253&fmt=auto&app=138&f=JPEG', // 裁剪图片的地址(可选值:url 地址, base64, blob)   infoTrue: true, // infoTrue为 true 时显示预览图片的宽高信息,infoTrue为 false 时表示显示裁剪框的宽高信息   mode: 'contain', // 截图框可拖动时的方向(可选值:contain , cover, 100px, 100% auto)   origin: false, // 上传的图片是否按照原始比例渲染   outputSize: 1, // 裁剪生成图片的质量(可选值:0.1 ~ 1)   outputType: 'png', // 裁剪生成图片的格式(可选值:png, jpeg, webp) })  // 实时预览 const realTime = (data) => {   // console.log('realTime data =', data)   previews.value = data } 

5. 注意事项

  1. cropper 对象的 getCropBlob 方法和 getCropData 方法都是异步方法
  2. 虽然 getCropBlob 获取的的 Blob 对象在控制台打印时只有 size 和 type 属性,但是仍然可以使用window.URL.createObjectURL(blob)来生成 url ,从 Java 的角度来说,相当于重写了 Blob 类的 toString 方法
  3. 前端用 formData 上传文件时, key 要与后端接口中 @RequestParam(“avatar”) 指定的参数名一致

在这里插入图片描述

6. SpringBoot 后端接收图片

后端环境:

  • JDK:17.0.7
  • SpringBoot:3.0.2

6.1 UserController.java

import cn.edu.scau.controller.vo.Result; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile;  import java.io.File; import java.io.IOException; import java.util.Objects; import java.util.UUID;  @RestController @RequestMapping("/user") public class UserController {      @PostMapping("/updateAvatar")     public Result updateAvatar(@RequestParam("avatar") MultipartFile avatar) {         System.err.println("文件名:" + avatar.getOriginalFilename());         System.err.println("文件大小(KB):" + avatar.getSize() / 1024);          try {             // 拿到图片文件后,可以将图片上传到阿里云、腾讯云、minio等第三方存储服务,然后返回图片的访问地址             // 这里直接保存到本地              String fileName = UUID.randomUUID().toString();             String suffix = Objects.requireNonNull(avatar.getOriginalFilename()).substring(avatar.getOriginalFilename().lastIndexOf("."));             avatar.transferTo(new File("F:\\Blog\\crop-avatar\\" + fileName + suffix));         } catch (IOException ioException) {             throw new RuntimeException(ioException);         }          return Result.success();     }  }  

6.2 Result.java

import java.io.Serializable;  /**  * 后端统一返回结果  *  * @param   */ public class Result implements Serializable {      private Integer code;     private String message;     private T data;      public static  Result success() {         Result result = new Result<>();         result.code = 200;         result.message = "success";         return result;     }      public static  Result success(T object) {         Result result = new Result<>();         result.data = object;         result.code = 200;         result.message = "success";         return result;     }      public static  Result fail(String message) {         Result result = new Result<>();         result.message = message;         result.code = 500;         return result;     }      public Integer getCode() {         return code;     }      public void setCode(Integer code) {         this.code = code;     }      public String getMessage() {         return message;     }      public void setMessage(String message) {         this.message = message;     }      public T getData() {         return data;     }      public void setData(T data) {         this.data = data;     }      @Override     public String toString() {         return "Result{" +                 "code=" + code +                 ", message='" + message + '\'' +                 ", data=" + data +                 '}';     }  } 

7. 完整的示例代码

7.1 Homeview.vue

     

7.2 request.js

import axios from 'axios'  const request = axios.create({   baseURL: '/api',   timeout: 60000,   headers: {     'Content-Type': 'application/json;charset=UTF-8'   } })  request.interceptors.request.use(  )  request.interceptors.response.use(response => {   if (response.data) {     return response.data   }   return response }, (error) => {   return Promise.reject(error) })  export default request 

7.3 main.js

import '@/assets/main.css'  import {createApp} from 'vue' import {createPinia} from 'pinia' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import zhCn from 'element-plus/es/locale/lang/zh-cn' import * as ElementPlusIconsVue from '@element-plus/icons-vue'  import App from './App.vue' import router from './router' import 'default-passive-events'  const app = createApp(App)  app.use(createPinia()) app.use(ElementPlus, {locale: zhCn}) for (const [key, component] of Object.entries(ElementPlusIconsVue)) {   app.component(key, component) } app.use(router)  app.mount('#app') 

7.4 vite.config.js

import {fileURLToPath, URL} from 'node:url'  import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue'  // https://vitejs.dev/config/ export default defineConfig({   plugins: [     vue()   ],   resolve: {     alias: {       '@': fileURLToPath(new URL('./src', import.meta.url))     }   },   server: {     proxy: {       '/api': {         target: 'http://localhost:8001',         changeOrigin: true,         rewrite: (path) => {           return path.replace('/api', '')         }       }     }   } }) 

8. 完整的源代码

前端:cropper-avatar-frontend

后端:cropper-avatar-backend

相关内容

热门资讯

透视安装!wepoker透视脚... 透视安装!wepoker透视脚本视频,闲聚鱼虾蟹辅助器软件,安装教程(有挂助手)-哔哩哔哩1、点击下...
黑科技辅助!德扑之星ai计算(... 黑科技辅助!德扑之星ai计算(智能ai辅助工具)软件透明挂黑科技(果然是真的有挂)-哔哩哔哩是一款可...
五分钟了解!胡乐辅助器免费版下... 五分钟了解!胡乐辅助器免费版下载(辅助挂)从前是有挂(专业辅助必胜教程)-哔哩哔哩;五分钟了解!胡乐...
辅助透视!hardrock透视... 辅助透视!hardrock透视工具(透视)底牌透视挂辅助神器(可靠开挂辅助必赢教程)-哔哩哔哩;ha...
透视辅助!pokerworld... 透视辅助!pokerworld破解版下载,爱玩联盟辅助下载,第三方教程(有挂方法)-哔哩哔哩爱玩联盟...
黑科技辅助!wpk ai辅助在... 黑科技辅助!wpk ai辅助在哪买(智能ai辅助工具)软件透明挂黑科技(其实有挂)-哔哩哔哩;亲,有...
第五分钟了解!哥哥打大a有挂(... 第五分钟了解!哥哥打大a有挂(辅助挂)一向真的是有挂(专业辅助爆料教程)-哔哩哔哩;哥哥打大a有挂是...
透视科技!hhpoker有透视... 透视科技!hhpoker有透视挂挂(透视)底牌透视挂辅助程序(可靠开挂辅助透明教程)-哔哩哔哩;1....
透视挂透视!德州局透视脚本,多... 透视挂透视!德州局透视脚本,多乐游戏修改器,透明挂教程(有挂细节)-哔哩哔哩1、多乐游戏修改器ai辅...
黑科技辅助!wepoke用可以... 黑科技辅助!wepoke用可以模拟器(智能ai辅助工具)软件透明挂黑科技(都是是有挂)-哔哩哔哩;1...