头像剪切上传

头像剪切上传

    • 文章说明
    • 核心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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/720393.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2.8k star! 用开源免费的edge-tts平替科大讯飞的语音合成服务

edge-tts是github上的一个开源项目&#xff0c;可以免费将文本转为语音&#xff0c;别看它只有2.8k star&#xff0c;替代科大讯飞的收费TTS服务完全没问题&#xff0c;因为这个项目实际是调用的微软edge的在线语音合成服务&#xff0c;支持40多种语言&#xff0c;300多种声音&…

注意力机制(代码实现案例)

学习目标 了解什么是注意力计算规则以及常见的计算规则.了解什么是注意力机制及其作用.掌握注意力机制的实现步骤. 1 注意力机制介绍 1.1 注意力概念 我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的), 是因为我们大脑能够很快把注意力放在事物…

EdgeX Foundry 安装部署

文章目录 一、概述1.官方文档2.Docker Compose 生成器3.创建 docker-compose 文件 二、安装准备1. 克隆服务器2.安装 Docker3.安装 docker-compose 三、非安全模式部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Fou…

Android之Handler原理解析与问题分享

一、Handler运行原理剖析 1.关系剖析图 如果把整个Handler交互看做一个工厂&#xff0c;Thread就是动力MessageQueue是履带Looper是转轴Loooper的loop方法就是开关&#xff0c;当调用loop方法时整个工厂开始循环工作&#xff0c;处理来自send和post提交到MessageQueue的消息&a…

08. Nginx进阶-Nginx动静分离

简介 什么是动静分离&#xff1f; 通过中间件将动态请求和静态请求进行分离。分离资源&#xff0c;减少不必要的请求消耗&#xff0c;减少请求延时。 动静分离的好处 动静分离以后&#xff0c;即使动态服务不可用&#xff0c;静态资源仍不受影响。 动静分离示意图 动静分离…

Day16:信息打点-语言框架开发组件FastJsonShiroLog4jSpringBoot等

目录 前置知识 指纹识别-本地工具-GotoScan&#xff08;CMSEEK&#xff09; Python-开发框架-Django&Flask PHP-开发框架-ThinkPHP&Laravel&Yii Java-框架组件-Fastjson&Shiro&Solr&Spring 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/…

Python的http模块requests

模块简介&#xff1a; requests 库是一个 python中比较有名的 http请求的库&#xff0c;能处理 get,post,put,delete 等 restful请求&#xff0c;能设置 header&#xff0c;cookie,session 等操作&#xff0c;也是作为爬虫的基础库&#xff0c;它目前还不能异步请求,如果要支持…

适用于 Windows 的 5 款最佳免费数据恢复软件榜单

每个计算机用户都曾经历过数据丢失的情况。很容易错误地删除重要的文件和文件夹&#xff0c;当发生这种情况时&#xff0c;可能会导致不必要的心痛和压力。值得庆幸的是&#xff0c;可以恢复 Windows PC 上丢失的数据。在本文中&#xff0c;我们将分享您可以使用的五种最佳 Win…

苹果因在iOS音乐流媒体市场上的反向引导行为,在欧盟被罚款18.4亿欧元

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Docker之自定义镜像上传阿里云

目录 一. Alpine制作jdk镜像 1.1 alpine Linux 简介 1.2 基于alpine制作jdk8镜像 1.2.1 下载镜像 1.2.2 创建并编辑Dockerfile 1.2.3 上传文件 1.2.4 执行Dockerfile构建镜像 1.2.5 测试 二. Alpine制作jre镜像 2.1 首先下载jre或者上传 2.2 解压 2.3 测试 2.4 返回上级目录&a…

云手机的境外舆情监控应用——助力品牌公关

在当今数字化时代&#xff0c;社交媒体已成为品牌传播和互动的主要平台。随之而来的是海量的信息涌入&#xff0c;品牌需要及时了解并应对海外社交媒体上的舆情变化。本文将介绍如何通过云手机进行境外舆情监控&#xff0c;更好地帮助企业公关及时作出决策。 1. 境外舆情监控与…

【linux】linux系统调用及文件IO操作

一、系统调用 1、概述 系统调用&#xff1a; 就是操作系统内核 提供给用户可以操作内核 一组函数接口。用户 借助 系统调用 操作内核。比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件&#xff0c;可以通过时钟相关的系统调用获得系统时间或设置定时…

如何创建测试计划?这些要考虑到

以下为作者观点&#xff1a; 创建一个彻底和有效的测试计划对软件测试的成功至关重要。它可以帮助识别过程中可能出现的潜在问题或问题。 什么是测试计划&#xff1f; 测试计划是一份文件&#xff0c;概述了软件测试过程的策略、目标、资源和时间表。测试计划通常包括一些细…

uniapp 项目 浏览器chrome使用vue devtool 识别不了 in not detect

问题 uniapp的项目&#xff0c;vue2&#xff0c; chrome 分析 添加了运行时&#xff0c;指定模板h5.html 指定的h5.html重置了运行根目录&#xff0c;导致了vue dev tool在运行时&#xff0c;chrome上识别不了。 解决&#xff1a; 方法1&#xff1a; 只能调试的时候,不加sati…

java常用应用程序编程接口(API)——Arrays概述

前言&#xff1a; 学到Arrays了&#xff0c;整理下心得。打好基础&#xff0c;daydayup! Arrays 用来操作数组的一个工具类 Arrays的常见方法 方法名说明public static String toString(类型[] arr)返回数组的内容public static int[ ] copyOfRange(类型[ ] arr,启示索引&…

Linux - 安装 maven(详细教程)

目录 一、下载二、安装三、配置环境变量四、镜像资源配置 一、下载 官网&#xff1a;https://maven.apache.org/download.cgi 打开 maven 的官网下载页面&#xff0c;点击 bin.tar.gz 文件链接 即可下载最新版本的 maven 如果想要下载旧版本的 meven&#xff0c;则点击 Maven…

图形系统开发实战课程:进阶篇(上)——9.空间算法(一)

图形开发学院&#xff5c;GraphAnyWhere 课程名称&#xff1a;图形系统开发实战课程&#xff1a;进阶篇(上)课程章节&#xff1a;“图形样式”原文地址&#xff1a;https://www.graphanywhere.com/graph/advanced/2-9.html 第九章 空间算法&#xff08;一&#xff09; \quad 在…

SpringBoot 自定义映射规则resultMap collection一对多

介绍 collection是封装一对多关系的&#xff0c;通常情况下是一个列表&#xff0c;association是一对一&#xff0c;通常情况是一个对象。例如&#xff1a;查询班级下所有的学生&#xff0c;一个班级可以有多个学生&#xff0c;这就是一对多。 案例 有一个学生表&#xff0c…

Go语言中的时间控制:定时器技术详细指南

Go语言中的时间控制&#xff1a;定时器技术详细指南 引言定时器基础创建和使用time.Timer使用time.Ticker实现周期性任务定时器的内部机制小结 使用time.Timer实现简单的定时任务创建和启动定时器停止和重置定时器定时器的实际应用小结 利用time.Ticker处理重复的定时任务创建和…

这里推荐一款unity3d人物动物控制器详细的等学会再写文章

unity3d Animal Controller 1.4.0a 动物NPC行为控制器 动物控制器&#xff08;AC&#xff09;是一个基于脚本架构的动画框架控制器。它适用于任何动物或人形角色的根运动或原地动画。 人和动物的各种动作都有; 小白必选、 我只是运行乐demo就感觉牛 demo路径&#xff1a;Asset…