使用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。 这个想法是为了防止神经网络变得过于依赖特定连接的共同适应,因为这可能是过度拟合的症状。直观上&#…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
