使用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。 这个想法是为了防止神经网络变得过于依赖特定连接的共同适应,因为这可能是过度拟合的症状。直观上&#…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
