使用canvas内置api完成图片的缩放平移和导出和添加提示
最近挺忙的,几乎没有时间去更新博客,今天正好在学习新东西,正好和大家分享一下。
最近要做一个使用canvas完成图片平移,缩放,添加标注的需求,完成的效果大概如下:
使用canvas内置api完成图片的缩放平移和导出和添加提示
最终的代码如下,这是使用canvas内置api完成的,缩放用到的是scale方法,平移用的是canvas的translate方法,代码中每个地方会有对应的注释,代码都是自己写的,如果又不懂得可以留言。
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>* {padding: 0;margin: 0;box-sizing: border-box;}.content {position: relative;margin: 50px;height: 500px;border: 5px solid red;overflow: hidden;}#canvas {position: absolute;top: 0;left: 50%;transform: translateX(-50%);border: 5px solid pink;cursor: grab;}.btnx {position: absolute;bottom: 0;left: 0;width: 100%;display: flex;align-items: center;justify-content: center;background-color: pink;height: 50px;cursor: pointer;}.btn {text-align: center;flex: 1;}.btn2 {display: none;}.btn5 {display: none;}</style>
</head><body><div class="content"><canvas id="canvas" width="500" height="400"></canvas><div class="btnx"><div class="btn btn1">裁剪</div><div class="btn btn2">取消</div><div class="btn btn3">放大x0.5</div><div class="btn btn4">缩小x0.5</div><div class="btn btn5">导出</div><div class="btn btn6">显示或隐藏tips</div></div></div><script>const btn1 = document.querySelector('.btn1')const btn2 = document.querySelector('.btn2')const btn3 = document.querySelector('.btn3')const btn4 = document.querySelector('.btn4')const btn5 = document.querySelector('.btn5')const btn6 = document.querySelector('.btn6')// canvas的domconst canvas = document.querySelector('#canvas')// canvas宽度const canvasWidth = 500// canvas高度const canvasHeight = 400// 定义画布的初始位置var canvasX = 0;var canvasY = 0;// 定义拖动的初始位置var dragStartX = 0;var dragStartY = 0;// 当前是否正在拖拽元素var isDragging = false;// 当前是否正在绘制元素var isDrawing = false;// 当前的缩放var scale = 1.0;// 存储绘制矩形的坐标和大小var rect = {};// 新增的canvas的个数let newCanvasCount = 0// 是否显示tipslet isShowtips=falseconst ctx = canvas.getContext('2d')canvas.setAttribute('width', canvasWidth)canvas.setAttribute('height', canvasHeight)// 获取图片let img = new Image();img.src = "./imgs/girl.webp";img.onload = function () {drawImageByScale()}btn2.addEventListener('click', function () {newCanvasCount = 0btn2.style.display = 'none'btn5.style.display = 'none'btn1.style.display = 'block'canvas.style.cursor = 'grab'// 充值绘制当前图片drawImageByScale()// 移除所有新增的canvas// const allCanvas = document.querySelectorAll('[id^="newcanvas-"]');// allCanvas.forEach((item, index) => {// document.body.removeChild(item)// })// console.log(allCanvas);// 移除所有的监听器clearEventListeners()// 重新添加让可以移动图标的鼠标监听器handleCanvasMove()})btn3.addEventListener('click', function () {scale += 0.5drawImageByScale()})btn4.addEventListener('click', function () {scale = Math.max(0.1, scale - 0.5)drawImageByScale()})btn5.addEventListener('click', exportImage);btn6.addEventListener('click', function () {isShowtips=!isShowtipsdrawImageByScale()})btn1.addEventListener('click', function () {clearEventListeners()btn2.style.display = 'block'btn5.style.display = 'block'btn1.style.display = 'none'canvas.style.cursor = 'crosshair'canvas.addEventListener('mousedown', handleDrawRectMouseDown);canvas.addEventListener('mousemove', handleDrawRectMouseMove);canvas.addEventListener('mouseup', handleDrawRectMouseUp);})// 让canvas可以移动handleCanvasMove()// 让canvas可以缩放handleCanvsZoom()// 绘制矩形function DrawRect() {ctx.clearRect(0, 0, canvas.width, canvas.height);if (!isDrawing) return;// 第一种样式:绘制一个填充矩形// ============开始==============// ctx.fillStyle = 'rgba(0, 0, 255, 0.5)';// drawImageByScale();// ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);// ============结束==============// 第二种样式:绘制一个边框// ============开始==============ctx.lineWidth = 10ctx.strokeStyle = 'rgba(0, 0, 255, 0.5)';drawImageByScale()ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h);// ============结束==============}// 获取鼠标在canvas中的x和y坐标function handleDrawRectMouseDown(e) {// rect.startX代表鼠标在canvas内的x坐标// e.clientX用于获取鼠标在浏览器中的横向坐标// canvas.getBoundingClientRect().left用于获取canvas的左边框相对于浏览器的距离rect.startX = e.clientX - canvas.getBoundingClientRect().left;rect.startY = e.clientY - canvas.getBoundingClientRect().top;isDrawing = true;}// 获取绘制的矩形的宽和高function handleDrawRectMouseMove(e) {if (!isDrawing) return;// rect.w和rect.h就代表了矩形的像素大小rect.w = (e.clientX - canvas.getBoundingClientRect().left) - rect.startX;rect.h = (e.clientY - canvas.getBoundingClientRect().top) - rect.startY;DrawRect();}// 停止移动function handleDrawRectMouseUp(e) {isDrawing = false;}// 鼠标按下拖拽图片function handleDragMouseDown(event) {isDragging = true;// 记录鼠标按下时的位置dragStartX = event.clientX;dragStartY = event.clientY;}// 鼠标按下移动拖拽图片function handleDragMouseMove(event) {if (isDragging) {// 计算鼠标移动的距离// deltaX的数值是根据鼠标移动的数据来计算的,鼠标速度越快这个数值越大var deltaX = event.clientX - dragStartX;var deltaY = event.clientY - dragStartY;// 更新画布的位置canvasX += deltaX;canvasY += deltaY;// 更新拖动起点位置dragStartX = event.clientX;dragStartY = event.clientY;drawImageByScale()}}// 鼠标松开拖拽图片function handleDragMouseUp() {isDragging = false;}// 导出绘制好的图片function exportImage() {newCanvasCount += 1// 创建新的 canvas 元素var croppedCanvas = document.createElement('canvas');var croppedCtx = croppedCanvas.getContext('2d');// 设置新的 canvas 尺寸croppedCanvas.setAttribute('width', rect.w + 'px')croppedCanvas.setAttribute('height', rect.h + 'px')croppedCanvas.setAttribute('id', `newcanvas-${newCanvasCount}`)document.body.appendChild(croppedCanvas)// 在新的 canvas 上绘制矩形圈起来的部分croppedCtx.drawImage(canvas, rect.startX, rect.startY, rect.w, rect.h, 0, 0, rect.w, rect.h);// console.log('rect', rect);// 将新的 canvas 导出为图片var imgData = croppedCanvas.toDataURL('image/png');// console.log('imgData', imgData);// 创建一个链接并下载图片var link = document.createElement('a');link.href = imgData;link.download = 'cropped_image.png';link.click();}// 处理移动图片function handleCanvasMove() {canvas.addEventListener('mousedown', handleDragMouseDown);canvas.addEventListener('mousemove', handleDragMouseMove);document.addEventListener('mouseup', handleDragMouseUp);}// 处理缩放图片function handleCanvsZoom() {function zoom(event) {event.preventDefault()// 根据鼠标滚轮方向更新缩放倍数if (event.deltaY < 0) {// 向上滚动,放大画布scale *= 1.1; // 增加10%} else {// 向下滚动,缩小画布scale /= 1.1; // 减小10%}drawImageByScale()}// Add event listener for mouse wheelcanvas.addEventListener('wheel', zoom);}// 清除监控器function clearEventListeners() {canvas.removeEventListener('mousedown', handleDrawRectMouseDown)canvas.removeEventListener('mousedown', handleDragMouseDown)canvas.removeEventListener('mousemove', handleDrawRectMouseMove)canvas.removeEventListener('mousemove', handleDragMouseMove)canvas.removeEventListener('mouseup', handleDrawRectMouseUp)canvas.removeEventListener('mouseup', handleDragMouseUp)}// 通过当前的缩放比和平移的位置来进行绘制图像function drawImageByScale() {// 清空画布ctx.clearRect(0, 0, canvas.width, canvas.height);// 保存当前的绘图状态ctx.save();// 缩放画布ctx.scale(scale, scale);// 平移到当前的画布位置ctx.translate(canvasX, canvasY);// 重新绘制画布内容ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight);if(isShowtips){addToolTip()}// 恢复之前保存的绘图状态,避免影响到其他绘图操作ctx.restore();}// 添加提示function addToolTip() {// 绘制选段ctx.beginPath();ctx.moveTo(280, 150);ctx.lineTo(300, 130);ctx.lineTo(360, 130);ctx.strokeStyle = 'pink'ctx.lineWidth = 3ctx.stroke();ctx.beginPath();// 绘制文字ctx.fillStyle = 'pink'ctx.font = "15px Verdana";ctx.fillText('愛你哦!!!', 310, 120)}</script>
</body></html>
相关文章:
使用canvas内置api完成图片的缩放平移和导出和添加提示
最近挺忙的,几乎没有时间去更新博客,今天正好在学习新东西,正好和大家分享一下。 最近要做一个使用canvas完成图片平移,缩放,添加标注的需求,完成的效果大概如下: 使用canvas内置api完成图片的缩…...

数据结构——二叉树——堆
前言: 在前面我们已经学习了数据结构的基础操作:顺序表和链表及其相关内容,今天我们来学一点有些难度的知识——数据结构中的二叉树,今天我们先来学习二叉树中堆的知识,这部分内容还是非常有意思的,下面我们…...

算法学习——LeetCode力扣图论篇3(127. 单词接龙、463. 岛屿的周长、684. 冗余连接、685. 冗余连接 II)
算法学习——LeetCode力扣图论篇3 127. 单词接龙 127. 单词接龙 - 力扣(LeetCode) 描述 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> … -> sk: 每一对相…...
状态模式详解:管理对象状态的利器
在软件设计中,我们经常会遇到需要根据对象的不同状态来执行不同行为的情况。为了优雅地管理这些状态及其对应的行为,状态模式(State Pattern)应运而生。本文将深入探讨状态模式的使用条件、Java代码实现,并结合现实社会…...

探索----------------阿里云
目录 一、阿里云四大件 1、云服务器ECS 2、云数据库RDS 3、负载均衡SLB 4、对象存储OSS 5、其他的云计算产品 1)内容分发网络CDN 2)专有网络 VPC 二、linux发行版本 三、你平时对系统会怎么优化(五大负载) 1、cpu 使用率…...

Tidb和MySQL性能简单测试对比
一、单SQL性能对比 由于TiDB的并发能力优秀,但是单个SQL执行延迟较差,为了客观对比,所以只用1个线程来压测tidb和mysql,以观察延迟情况 二、并发SQL性能对比 TiDB:v6.5.2 MySQL:8.0.26 (单机) 三、结论 …...
2024.2.6力扣每日一题——魔塔游戏
2024.2.6 题目来源我的题解方法一 贪心优先队列 题目来源 力扣每日一题;题序:LCP 30 我的题解 方法一 贪心优先队列 思路:使用贪心的思想,从左到右遍历,若遇到加上当前房间的生命值后小于等于0,由于需要…...
C# OAuth单点登录的实现
原理 单点登录(Single Sign-On,简称SSO)是一种身份验证技术,它允许用户使用一组凭据(如用户名和密码)登录多个相关但独立的系统,而无需在每个系统中都进行登录操作。下面是一个简单的SSO实现示…...

AtCoder Beginner Contest 347 (ABCDEF题)视频讲解
A - Divisible Problem Statement You are given positive integers N N N and K K K, and a sequence of length N N N, A ( A 1 , A 2 , … , A N ) A(A_1,A_2,\ldots,A_N) A(A1,A2,…,AN). Extract all elements of A A A that are multiples of K K K, divi…...

【vue2+antvx6】报错Cannot read properties of undefined (reading ‘toUpperCase‘)
我的代码是这样的 <el-collapseref"collapse"v-model"active"accordionclass"collapseStart"change"collapsechange"><el-collapse-item:name"String(index 1)"v-for"(i, index) in List":key"in…...
主流的开发语言、环境及其特点
主流的开发语言及其特点: 1. Python:以其简洁的语法和强大的库支持而闻名,适用于数据科学、人工智能和网络开发等领域。 2. Java:跨平台的编程语言,广泛应用于企业级应用、Android 开发和大型系统开发。 3. C…...
Android知识 - 代码混淆ProGuard规则介绍
ProGuard 的规则及示例 规则概述 ProGuard 是一个代码优化工具,它通过移除未使用的代码、重命名类、字段和方法等方式来减小应用的大小。在 ProGuard 的配置文件中,我们可以定义一系列的规则来控制优化和混淆的过程。 规则语法 ProGuard 的规则通常包…...
【Linux的进程篇章 - 冯诺依曼的体系结构】
Linux学习笔记---005 Linux冯诺依曼体系结构理解1、冯诺依曼体系结构1.1、冯诺依曼体系结构1.2、硬件层面1.3、数据层面1.4、那么冯诺依曼体系能干什么呢? 2、操作系统(Operastor System)2.1、概念2.2、操作系统层的核心功能 3、进程的初步理解 Linux冯诺依曼体系结…...
flask-(数据连接池的使用,定制命令,信号的使用,表关系的建立和查询)
文章目录 连接池实例flask定制命令flask 缓存的使用flask信号的使用sqlalchemy原生操作sqlalchemy操作表flask orm操作表一对多的增加和跨表查询 (一对一只需要关联字段加上 ,uniqueTrue)多对多关系的增加和查询多对多基本的增删改查 连接池 import pymy…...

设计模式学习笔记 - 设计模式与范式 -行为型:2.观察者模式(下):实现一个异步非阻塞的EventBus框架
概述 《1.观察者模式(上)》我们学习了观察者模式的原理、实现、应用场景,重点节介绍了不同应用场景下,几种不同的实现方式,包括:同步阻塞、异步非阻塞、进程内、进程间的实现方式。 同步阻塞最经典的实现…...

数据挖掘|贝叶斯分类器及其Python实现
分类分析|贝叶斯分类器及其Python实现 0. 分类分析概述1. Logistics回归模型2. 贝叶斯分类器2.1 贝叶斯定理2.2 朴素贝叶斯分类器2.2.1 高斯朴素贝叶斯分类器2.2.2 多项式朴素贝叶斯分类器 2.3 朴素贝叶斯分类的主要优点2.4 朴素贝叶斯分类的主要缺点 3. 贝叶斯分类器在生产中的…...

Linux文件(系统)IO(含动静态库的链接操作)
文章目录 Linux文件(系统)IO(含动静态库的链接操作)1、C语言文件IO操作2、三个数据流stdin、stdout、stderr3、系统文件IO3.1、相关系统调用接口的使用3.2、文件描述符fd3.3、文件描述符的分配规则3.3、重定向3.4、自制shell加入重…...

CI/CD实战-jenkins结合ansible 7
配置主机环境 在jenkins上断开并删除docker1节点 重新给master添加构建任务 将server3,server4作为测试主机,停掉其上后面的docker 在server2(jenkins)主机上安装ansible 设置jenkins用户到目标主机的免密 给测试主机创建用户并…...

内网渗透-(黄金票据和白银票据)详解(一)
目录 一、Kerberos协议 二、下面我们来具体分析Kerberos认证流程的每个步骤: 1、KRB_AS-REQ请求包分析 PA-ENC-TIMESTAMP PA_PAC_REQUEST 2、 KRB_AS_REP回复包分析: TGT认购权证 Logon Session Key ticket 3、然后继续来讲相关的TGS的认证过程…...

学习transformer模型-Dropout的简明介绍
Dropout的定义和目的: Dropout 是一种神经网络正则化技术,它在训练时以指定的概率丢弃一个单元(以及连接)p。 这个想法是为了防止神经网络变得过于依赖特定连接的共同适应,因为这可能是过度拟合的症状。直观上&#…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...