使用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>

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

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

相关文章

Leetcode 88. 合并两个有序数组

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&#xff0c;合并后数组…

【键值皆有序map 线段树 数学 】100240. 最小化曼哈顿距离

本文涉及知识点 键值皆有序map 线段树 数学 LeetCode100240. 最小化曼哈顿距离 给你一个下标从 0 开始的数组 points &#xff0c;它表示二维平面上一些点的整数坐标&#xff0c;其中 points[i] [xi, yi] 。 两点之间的距离定义为它们的曼哈顿距离。 请你恰好移除一个点&am…

PySpark的学习

一. 什么是PySpark 使用过的bin/pyspark 程序 , 要注意 , 这个只是一个 应用程序 , 提供一个 Python 解释器执行环境来运行 Spark 任务 现在说的 PySpark, 指的是 Python 的运行类库 , 是可以在 Python 代码中 :import pyspark PySpark 是 Spark 官方提供的一个 Python …

后端SpringBoot+Mybatis 查询订单数据库奇怪报错加一

排错过程&#xff1a; 看报错意思是SQL语句存在错误&#xff0c;然后使用图形化工具运行这个SQL语句 其实这里稍微细心想一下就能发现问题&#xff0c;但是当时没深入想&#xff0c;就觉得order表前加了数据库名字影响不大&#xff0c;所以感觉SQL语句是没问题的&#xff0c;然…

C语言 05 变量与常量

变量 变量就像在数学中学习的 x&#xff0c;y 一样&#xff0c;可以直接声明一个变量&#xff0c;并利用这些变量进行基本的运算&#xff0c;声明变量的格式为&#xff1a; 数据类型 变量名称 初始值;&#xff08;其中初始值可以不用在定义变量时设定&#xff09; 是赋值操作…

HarmonyOS实战开发-switch、chart组件的使用

介绍 本篇Codelab基于switch组件和chart组件&#xff0c;实现线形图、占比图、柱状图&#xff0c;并通过switch切换chart组件数据的动静态显示。要求实现以下功能&#xff1a; 实现静态数据可视化图表。打开开关&#xff0c;实现静态图切换为动态可视化图表。 相关概念 swit…

3. WiFi基本原理

1. WiFi简介 WiFi的全称是Wireless Fidelity。它是一种无线网络通信技术&#xff0c;由Wi-Fi联盟拥有&#xff0c;目的是改善基于IEEE 802.11标准的无线网络产品之间的互通性&#xff0c;允许电子设备在没有物理连接的情况下进行高速数据传输。此外&#xff0c;WiFi也被视为IE…

vue中v-model与:model以及v-bind区别

一、v-model &#xff08;常用于表单&#xff09; v-model 是 v-model:value 的缩写&#xff0c;通常用于表单上的双向数据绑定&#xff08;表单接受值 value&#xff0c;故v-model默认收集的就是 value &#xff0c;所以缩写直接省略 value&#xff09;&#xff0c;可以实现子…

Holiday Notice

Holiday Notice 放假通知 要是每个公司都能放假放的多&#xff0c;把加班折算放假落实到位&#xff0c;还怕我们不努力干活&#xff0c;巴不得把全年都干完了&#xff0c;然后休息。

STP保护机制

Switch#show spanning-tree summary totals---查看生成树汇总信息 1.BPDU guard BPDU防护&#xff0c;避免开启了portfast特性的端口错误的接入进交换机。如果发现&#xff0c;则逻辑的将接口关闭掉。 思科&#xff1a; Switch(config)#spanning-tree portfast edge bpdugu…

python爬取B站视频

参考&#xff1a;https://cloud.tencent.com/developer/article/1768680 参考的代码有点问题&#xff0c;请求头需要修改&#xff0c;上代码&#xff1a; import requests import re # 正则表达式 import pprint import json from moviepy.editor import AudioFileClip, Vid…

区间预测 | Matlab实现带有置信区间的GRNN广义回归神经网络时间序列未来趋势预测

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 Matlab实现带有置信区间的GRNN广义回归神经网络时间序列未来趋势预测 带有置信区间的GRNN(广义回归神经网络)时间序列未来趋势预测结合了广义回归神经网络(GRNN)的预测能力和置信区间的统计度量,以提供对未来…

day14-二叉树part01(递归法/迭代法)

递归三部曲(时刻牢记) 1.确认递归函数需要的参数与返回值 一般为传入一个根节点 传入一个数组用来存放结果数组 2.确定终止条件 3.确定单层递归逻辑 递归的实现就是&#xff1a;每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中&#xff0c;然后递归返回的…

云备份项目认识、环境搭建以及所使用的库的介绍

一、云备份认识 将本地计算机一个受监管的文件夹的文件上传到服务器中&#xff0c;有服务器组织&#xff0c;客户端可以通过网页将文件查看并且下载下来&#xff0c;下载过程支持断点续传功能&#xff0c;并且服务器会对上传的文件进行热点管理&#xff0c;长时间没人访问的文…

内网穿透时报错【Bad Request This combination of host and port requires TLS.】的原因

目录 前言&#xff1a;介绍一下内网穿透 1.内网直接https访问&#xff08;可以正常访问&#xff09; 程序配置的证书 2.内网穿透后,通过外网访问 3.原因 4.内网非https的Web应用&#xff0c;使用https后&#xff0c;也变成了https访问 5.题外话 感觉自己的web应用配置了…

使用 Seq2Seq 模型进行文本摘要

目录 引言 1 导入数据集 2 清洗数据集 3 确定允许的最大序列长度 4 选择合理的文本和摘要 5 对文本进行标记 6 删除空文本和摘要 7 构建模型 7.1 编码器 7.2 解码器 8 训练模型 9 测试模型 10 注意 11 整体代码 引言 文本摘要是指在捕捉其本质的同时缩短长文本的…

PHP性能提升方案

一、背景与介绍 PHP语言开发效率高&#xff0c;特别应用于适合中小型项目&#xff0c;对于创业初期敏捷开发验证项目可行性或者Demo演示绝对占据优势。 但是随着现在Web应用的复杂性&#xff0c;针对项目要适应高并发、高流量的访问特性&#xff0c;PHP确实在性能方面相对Go、J…

LeetCode-2202 K次操作后最大的顶端元素(JAVA)

题目要求&#xff1a; 给你一个下标从 0 开始的整数数组 nums &#xff0c;它表示一个 栈 &#xff0c;其中 nums[0] 是栈顶的元素。 每一次操作中&#xff0c;你可以执行以下操作 之一 &#xff1a; 如果栈非空&#xff0c;那么 删除 栈顶端的元素。如果存在 1 个或者多个被…

分布式之分布式事务详解

分布式事务与实战运用 什么是分布式事务&#xff1f; 业务场景&#xff1a;用户A转账100元给用户B&#xff0c;这个业务比较简单&#xff0c;具体的步骤&#xff1a; 1、用户A的账户先扣除100元 2、再把用户B的账户加100元 如果在同一个数据库中进行&#xff0c;事务可以保证…

209基于matlab的无人机路径规划

基于matlab的无人机路径规划&#xff0c;包括2D路径和3D路径&#xff0c;三种优化算法&#xff0c;分别是蝙蝠算法&#xff08;BA&#xff09;、蝙蝠算法融合差分进化算法(DEBA)、结合人工势场方法的改进混沌蝙蝠算法(CPFIBA)。输出距离迭代曲线和规划的路径。程序已调通&#…