当前位置: 首页 > news >正文

头像剪切上传

头像剪切上传

    • 文章说明
    • 核心Api
    • 示例源码
    • 效果展示
    • 源码下载

文章说明

本文主要为了学习头像裁剪功能,以及熟悉canvas绘图和转文件的相关操作,参考教程(Web渡一前端–图片裁剪上传原理)

核心Api

主要就一个在canvas绘图的操作
context.drawImage(image, imgX, imgY, rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);

以及canvas转为file对象的操作
let formData = new FormData();
const file = new File([blob], data.selectFileName, {type: data.selectFileType})
formData.append(“file”, file);

关于其中的绘制区域的大小缩放以及移动,也算是一个小难点;一般也有另一种裁剪区域风格,即四条线风格,可通过代码进行理解

示例源码

AvatarUpload.vue

<template><div class="container"><div class="img-container"><div class="select-file" @click="selectFile" v-if="!data.selectFile"><p>jpg/png file with a size less than 5MB<em>click to upload</em></p></div><img alt="" :src="data.src" class="img" v-if="data.selectFile" draggable="false":style="{'height' : data.imgHeight }"/><div class="rectangle" v-if="data.selectFile" @mousedown="dragStart($event)" @mousemove="changePos($event)"@mousewheel="wheel($event)"></div></div><canvas width="100" height="100" class="canvas"></canvas><el-button type="primary" class="upload-button" @click="uploadAvatar">上传</el-button></div>
</template><script>
import {onBeforeUnmount, onMounted, reactive} from "vue";
import {axiosRequest, message} from "@/util/api";
import {MethodType} from "@/util/constant";export default {setup: function () {const data = reactive({src: null,imgHeight: "300px",selectFile: false,selectFileName: "",selectFileType: "",});async function selectFile() {const pickerOpts = {types: [{description: "Images",accept: {"image/*": [".png", ".jpeg", ".jpg"],},},],excludeAcceptAllOption: true,multiple: false,};try {const fileHandle = await window.showOpenFilePicker(pickerOpts);const file = await fileHandle[0].getFile();data.selectFileName = file.name;data.selectFileType = file.type;const reader = new FileReader();reader.readAsDataURL(file);reader.onload = function (e) {data.src = e.target.result;data.selectFile = true;image = new Image();image.src = e.target.result;setTimeout(() => {data.imgHeight = image.height + "px";rectangle = imgContainer.getElementsByClassName("rectangle")[0];rectangleWidth = rectangle.clientWidth;rectangleHeight = rectangle.clientHeight;context.drawImage(image, (image.width / 2 - rectangleWidth / 2), (image.height / 2 - rectangleHeight / 2), rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);}, 0);};} catch (e) {if (!(e.name === 'AbortError' && e.message === 'The user aborted a request.')) {throw e;}}}let isDragging = false;let mouseUpListener;let containerX;let containerY;let imgContainer;let imgWidth;let imgHeight;let rectangle;let initialX;let initialY;let rectangleWidth;let rectangleHeight;let imgX;let imgY;let headerHeight;let canvas;let context;let image;function dragStart(e) {isDragging = true;containerX = imgContainer.offsetLeft;containerY = imgContainer.offsetTop;imgWidth = imgContainer.clientWidth;imgHeight = imgContainer.clientHeight;initialX = e.offsetX;initialY = e.offsetY;rectangle = imgContainer.getElementsByClassName("rectangle")[0];rectangleWidth = rectangle.clientWidth;rectangleHeight = rectangle.clientHeight;}function changePos(e) {if (!isDragging) {return;}const x = e.clientX - containerX - initialX + rectangleWidth / 2;const y = e.clientY - containerY - initialY - headerHeight + rectangleHeight / 2;if (x >= rectangleWidth / 2 + 3 && x < imgWidth - rectangleWidth / 2 - 2) {imgX = x - rectangleWidth / 2;rectangle.style.left = x + "px";centerX = imgX + rectangleWidth / 2;context.drawImage(image, imgX, imgY, rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);}if (y >= rectangleHeight / 2 + 3 && y < imgHeight - rectangleHeight / 2 - 4) {imgY = y - rectangleHeight / 2;rectangle.style.top = y + "px";centerY = imgY + rectangleHeight / 2;context.drawImage(image, imgX, imgY, rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);}}onMounted(() => {mouseUpListener = () => {isDragging = false;}document.addEventListener("mouseup", mouseUpListener);const containerList = document.getElementsByClassName("container");const container = containerList[containerList.length - 1];headerHeight = container.parentNode["getBoundingClientRect"]().y;imgContainer = container.getElementsByClassName("img-container")[0];canvas = container.getElementsByClassName("canvas")[0];context = canvas.getContext("2d");});onBeforeUnmount(() => {document.removeEventListener("mouseup", mouseUpListener);});const gap = 2;const minRange = 20;let centerX;let centerY;function wheel(e) {if (!centerX) {centerX = image.width / 2;}if (!centerY) {centerY = image.height / 2;}if (e.deltaY > 0) {if (rectangleWidth + gap >= image.width || rectangleHeight + gap >= image.height) {return;}if ((centerX - rectangleWidth / 2 - gap < 0) || (centerY - rectangleHeight / 2 - gap < 0)) {return;}rectangleWidth += gap;rectangleHeight += gap;rectangle.style.width = rectangleWidth + "px";rectangle.style.height = rectangleHeight + "px";} else {if (rectangleWidth - gap < minRange || rectangleHeight - gap < minRange) {return;}rectangleWidth -= gap;rectangleHeight -= gap;rectangle.style.width = rectangleWidth + "px";rectangle.style.height = rectangleHeight + "px";}context.drawImage(image, (centerX - rectangleWidth / 2), (centerY - rectangleHeight / 2), rectangleWidth, rectangleHeight, 0, 0, canvas.width, canvas.height);}function uploadAvatar() {if (!data.selectFile) {message("请先选择图片", "info");return;}canvas.toBlob((blob) => {let formData = new FormData();const file = new File([blob], data.selectFileName, {type: data.selectFileType})formData.append("file", file);axiosRequest(MethodType.post, "/user/uploadAvatar", formData, (res) => {message(res.data.msg, "info");});}, data.selectFileType);}return {data,selectFile,dragStart,changePos,wheel,uploadAvatar,}}
}
</script><style scoped>
.container {margin: 0 auto;padding-top: 100px;width: fit-content;user-select: none;display: flex;justify-content: center;align-items: center;
}.img-container {position: relative;width: fit-content;
}.rectangle {width: 100px;height: 100px;border: 1px dashed #409eff;position: absolute;left: 50%;top: 50%;transform: translateX(-50%) translateY(-50%);z-index: 999;box-shadow: #888888 0 0 1px 1px;cursor: pointer;
}.select-file {width: 500px;height: 300px;border: 1px dashed #dcdfe6;border-radius: 20px;display: flex;justify-content: center;align-items: center;
}.select-file:hover {border: 1px dashed #409eff;cursor: pointer;
}.select-file p {font-size: 14px;color: #606266;
}.select-file p em {color: #409eff;font-style: normal;margin-left: 5px;
}.img {border-radius: 20px;border: 1px dashed #409eff;
}.canvas {margin-left: 100px;border: 1px dashed #409eff;float: left;border-radius: 50%;
}.upload-button {position: absolute;width: 180px;height: 50px;top: 460px;font-size: 20px;
}
</style>

效果展示

在这里插入图片描述

关于裁剪区域的风格,设置为四条线可移动那种,需要改动一些代码,考虑后续补充

源码下载

参见Gitee链接(WEB-OS-SYSTEM)

相关文章:

头像剪切上传

头像剪切上传 文章说明核心Api示例源码效果展示源码下载 文章说明 本文主要为了学习头像裁剪功能&#xff0c;以及熟悉canvas绘图和转文件的相关操作&#xff0c;参考教程&#xff08;Web渡一前端–图片裁剪上传原理&#xff09; 核心Api 主要就一个在canvas绘图的操作 context…...

24计算机考研调剂 | 北京信息科技大学

北京信息科技大学接收调剂研究生 考研调剂招生信息 学校:北京信息科技大学 专业:工学->控制科学与工程->控制理论与控制工程 年级:2024 招生人数:- 招生状态:正在招生中 联系方式:********* (为保护个人隐私,联系方式仅限APP查看) 补充内容 各位同学&#xff0c;…...

06 - 镜像管理

1 了解镜像 Docker镜像是一个特殊的文件系统&#xff0c;除了提供容器运行时所需的程序、库、资源、配置等文件外&#xff0c;还包含了一些为运行时准备的一些配置参数&#xff08;如匿名卷、环境变量、用户等&#xff09;。 但注意&#xff0c; 镜像不包含任何动态数据&#…...

最简单 导航栏 html css

dhl.html <!DOCTYPE html> <html><head><meta charset"utf-8"><title>导航栏</title><link type"text/css" rel"stylesheet" href"css/dhl.css"></head><div class"dhl&quo…...

PostgreSQL的学习心得和知识总结(一百三十一)|深入理解PostgreSQL数据库如何使用psql中的变量

目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《PostgreSQL数据库内核分析》 2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》 3、PostgreSQL数据库仓库链接,点击前往 4、日本著名PostgreSQL数据库专家 铃木启修 网站…...

支付宝小程序模板开发,实现代小程序备案申请

大家好&#xff0c;我是小悟 支付宝小程序备案整体流程总共分为五个环节&#xff1a;备案信息填写、平台初审、工信部短信核验、通管局审核和备案成功。 服务商可以代小程序发起备案申请。在申请小程序备案之前&#xff0c;需要确保小程序基本信息已填写完成、小程序至少存在一…...

怎么培养孩子的学习习惯?

问&#xff1a;在亲子阅读中&#xff0c;应该用哪些方法引导孩子自己主动阅读呢&#xff1f; 有很多家长会问如何培养孩子主动阅读的兴趣&#xff1f; 我想给你四个词来分享&#xff0c;分别是环境、选择的权利、适龄&#xff0c;还有增强回路。第一个环境&#xff0c;就是把…...

deeplearning with pytorch (三)

一.基本概念 1.Convolutional Neural Network Intro mnist数据集 2.Image Filter / Image Kernel Image Kernels explained visually 访问这个网站可以直观看到image kernels对图片的影响 3.Convolutional Layer and RGB 为什么要用巻积神经网络代替人工神经网络 上图是…...

LLaMA模型的发布与创新潮流

时间线从2023年2月24日开始&#xff0c;Meta推出了LLaMA模型&#xff0c;虽然开源了代码&#xff0c;却没有开源模型权重。LLaMA模型可以说是相对较小的&#xff0c;拥有7B、13B、33B和65B参数的几种版本&#xff0c;训练时间相对较长&#xff0c;因此相对于其大小而言能力强大…...

Python之Web开发初学者教程—ubuntu中安装配置redis

Python之Web开发初学者教程—ubuntu中安装配置redis 准备环境&#xff1a;VMWare&#xff0c;ubuntu18.04.6 LTS 一、安装 从 Ubuntu 存储库安装 Redis&#xff0c;打开Ubuntu的终端&#xff0c;输入下面的命令&#xff1a; sudo apt-get install redis-server &#xff08…...

如何在Vue中进行单元测试?

前端开发中&#xff0c;单元测试是一个非常重要的环节&#xff0c;它可以帮助我们在开发过程中发现潜在的问题&#xff0c;并确保我们的代码在不断迭代的过程中依然能够保持稳定。在Vue中进行单元测试同样非常重要&#xff0c;本文将介绍如何在Vue项目中进行单元测试。 在Vue中…...

开源组件安全风险及应对

在软件开发的过程中&#xff0c;为了提升开发效率、软件质量和稳定性&#xff0c;并降低开发成本&#xff0c;使用开源组件是开发人员的不二选择&#xff08;实际上&#xff0c;所有软件开发技术的演进都是为了能够更短时间、更低成本地构建软件&#xff09;。这里的开源组件指…...

nginx出现 “414 request-uri too large”

nginx出现 “414 request-uri too large” 1.修改传参方式 POST 2.字段能变成后端获取就自己获取&#xff0c;不用前端传 3.修改nginx配置&#xff0c;添加client_header_buffer_size 512k;large_client_header_buffers 4 512k;配置...

堆和二叉树的动态实现(C语言实现)

✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅ ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨ &#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1f33f;&#x1…...

Vue前端+快速入门【详解】

目录 1.Vue概述 2. 快速入门 3. Vue指令 4.表格信息案例 5. 生命周期 1.Vue概述 1.MVVM思想 原始HTMLCSSJavaScript开发存在的问题&#xff1a;操作麻烦&#xff0c;耦合性强 为了实现html标签与数据的解耦&#xff0c;前端开发中提供了MVVM思想&#xff1a;即Model-Vi…...

day06_菜单管理(查询菜单,添加菜单,添加子菜单,修改菜单,删除菜单,角色分配菜单,查询菜单,保存菜单,动态菜单)

文章目录 1 菜单管理1.1 表结构介绍1.2 查询菜单1.2.1 需求说明1.2.2 页面制作1.2.3 后端接口SysMenuSysMenuControllerSysMenuServiceMenuHelperSysMenuMapperSysMenuMapper.xml 1.2.4 前端对接sysMenu.jssysMenu.vue 1.3 添加菜单1.3.1 需求说明1.3.3 页面制作1.3.3 后端接口…...

探究与以太坊智能合约的交互

# 概述 智能合约是部署在区块链上的一串代代码&#xff0c;通常我们与智能合约的打交道 可以通过前端的Dapp&#xff0c;etherscan&#xff0c;metamask 等方式。作为开发人员可以通过调用提供的相关包来与之交互&#xff0c;如web3.js&#xff0c;ether.js , web3.j(java 语言…...

Windows如何安装docker-desktop

下载 docker-desktop设置环境安装wsl可能遇到的错误 下载 docker-desktop 下载官网&#xff1a;https://www.docker.com/products/docker-desktop/ 设置环境 如果没有Hyper-V选项的,按照以下步骤 添加一个文件Hyper-V.bat 添加以下内容,并双击运行后重启电脑 pushd "%~…...

芯片设计后端遇到的各种文件类型和文件后缀

芯片设计后端遇到的各种文件类型和文件后缀 文件类型 描述 文件后缀 netlist网表文件 verilog文件格式&#xff0c;记录了芯片里各个instance的逻辑连接关系 .v (for Verilog netlists) Lib&#xff0c;liberty timing file 记录了cell的timing信息及一定power信息。有的…...

【Web】Java反序列化之CC7链——Hashtable

目录 链子原理分析(借尸还魂) 如何构造相等hash 又谈为何lazyMap2.remove("yy") 不过真的需要两个LazyMap吗 EXP 双LazyMap exp HashMap&LazyMap exp 链子原理分析(借尸还魂) 先看Hashtable#readObject origlength和elements分别是原始数组的长度和元素…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...