当前位置: 首页 > news >正文

js实现框选截屏功能

实现的思路大概就是,先将dom转化为canvas画布,再对canvas进行裁切,然后通过canvas api生成图片,这里用到了一个库html2canvas

效果如图:
在这里插入图片描述
首先实现框选效果:

const mousedownEvent = (e) => {moveX = 0;moveY = 0;const [startX, startY] = [e.clientX, e.clientY];x = startX - viewer.getBoundingClientRect().left;y = startY - viewer.getBoundingClientRect().top;const divDom = document.createElement("div");divDom.id = 'screenshot';divDom.width = '1px';divDom.height = '1px';divDom.style.position = "absolute";divDom.style.top = y + "px";divDom.style.left = x + "px";const closeIcon = document.createElement("span");closeIcon.className = 'outline-close-icon';closeIcon.textContent = 'x';divDom.appendChild(closeIcon);closeIcon.addEventListener('click', () => {divDom.remove();});// document.body.appendChild(divDom)viewer.appendChild(divDom);const moveEvent = (e) => {moveX = e.clientX - startX;moveY = e.clientY - startY;if (moveX > 0) {divDom.style.width = moveX + 'px';} else {divDom.style.width = -moveX + 'px';divDom.style.left = e.clientX - viewer.getBoundingClientRect().left + 'px';}if (moveY > 0) {divDom.style.height = moveY + 'px';} else {divDom.style.height = -moveY + 'px';divDom.style.top = e.clientY - viewer.getBoundingClientRect().top + 'px';}};window.addEventListener("mousemove", moveEvent);window.addEventListener("mouseup", () => {window.removeEventListener("mousemove", moveEvent);window.removeEventListener("mousedown", mousedownEvent);document.querySelector("body").style.cursor = "default";});
};
window.addEventListener("mousedown", mousedownEvent);

全码:

const viewer = document.getElementById('viewer');
let canvas;
document.getElementById('screen-button').addEventListener('click', (e) => {// 创建一个canvas画布if (canvas) {canvas.remove();}canvas = document.createElement('canvas');canvas.setAttribute('id', 'bg_canvas');canvas.style.position = "absolute";canvas.style.zIndex = 2;canvas.style.left = 0;canvas.style.top = 0;canvas.style.width = '100%';canvas.style.height = '100%';viewer.appendChild(canvas);document.querySelector("body").style.cursor = "crosshair";let moveX;let moveY;let x;let y;const mousedownEvent = (e) => {moveX = 0;moveY = 0;const [startX, startY] = [e.clientX, e.clientY];x = startX - viewer.getBoundingClientRect().left;y = startY - viewer.getBoundingClientRect().top;const divDom = document.createElement("div");divDom.id = 'screenshot';divDom.width = '1px';divDom.height = '1px';divDom.style.position = "absolute";divDom.style.top = y + "px";divDom.style.left = x + "px";const closeIcon = document.createElement("span");closeIcon.className = 'outline-close-icon';closeIcon.textContent = 'x';divDom.appendChild(closeIcon);closeIcon.addEventListener('click', () => {divDom.remove();});// document.body.appendChild(divDom)viewer.appendChild(divDom);const moveEvent = (e) => {moveX = e.clientX - startX;moveY = e.clientY - startY;if (moveX > 0) {divDom.style.width = moveX + 'px';} else {divDom.style.width = -moveX + 'px';divDom.style.left = e.clientX - viewer.getBoundingClientRect().left + 'px';}if (moveY > 0) {divDom.style.height = moveY + 'px';} else {divDom.style.height = -moveY + 'px';divDom.style.top = e.clientY - viewer.getBoundingClientRect().top + 'px';}};window.addEventListener("mousemove", moveEvent);window.addEventListener("mouseup", () => {window.removeEventListener("mousemove", moveEvent);window.removeEventListener("mousedown", mousedownEvent);document.querySelector("body").style.cursor = "default";if (!moveX) {return;}// 把body转成canvashtml2canvas(viewer, {scale: 1,// allowTaint: true,useCORS: true  //跨域使用}).then(canvas2 => {let capture_x, capture_y;let width = moveX;let height = moveY;if (width > 0) {//从左往右画capture_x = startX - canvas.getBoundingClientRect().left + 1;} else {//从右往左画capture_x = x + width + 1;}if (height > 0) {//从上往下画capture_y = y + 1;} else {//从下往上画capture_y = y + height + 1;}printClip(canvas2, capture_x, capture_y, Math.abs(width), Math.abs(height));moveX = 0;canvas.remove();});});};window.addEventListener("mousedown", mousedownEvent);
});/*** 打印截取区域* @param canvas 截取的canvas* @param capture_x 截取的起点x* @param capture_y 截取的起点y* @param capture_width 截取的起点宽* @param capture_height 截取的起点高*/
async function printClip(canvas2, capture_x, capture_y, capture_width, capture_height) {// 创建一个用于截取的canvasconst clipCanvas = document.createElement('canvas');clipCanvas.width = capture_width;clipCanvas.height = capture_height;// 截取clipCanvas.getContext('2d').drawImage(canvas2, capture_x, capture_y, capture_width, capture_height, 0, 0, capture_width, capture_height);const clipImgBase64 = clipCanvas.toDataURL();// console.log('clipImgBase64->', clipImgBase64);// console.log('clipImgBase64->', clipImgBase64.replace(/^data:image\/\w+;base64,/, ""));const obj = {// file: new File([this.blob],'main.audio',{ type: 'audio/mp3' })file: new File([base64ToBlob(clipImgBase64.replace(/^data:image\/\w+;base64,/, ""), 'image/png')], 'test.png', { type: 'image/png' })};// 生成图片// var clipImg = new Image()// clipImg.src = clipImgBase64downloadIamge(clipImgBase64)
}/*** 下载保存图片* @param imgUrl 图片地址*/
function downloadIamge(imgUrl) {// 生成一个a元素const a = document.createElement('a');// 创建一个单击事件const event = new MouseEvent('click');// 生成文件名称const timestamp = new Date().getTime();const name = imgUrl.substring(22, 30) + timestamp + '.png';a.download = name;// 将生成的URL设置为a.href属性a.href = imgUrl;// 触发a的单击事件 开始下载a.dispatchEvent(event);
}// 将Base64编码转换为Blob对象
function base64ToBlob(base64, type) {const byteCharacters = atob(base64);const byteArrays = [];for (let offset = 0; offset < byteCharacters.length; offset += 512) {let slice = byteCharacters.slice(offset, offset + 512);let byteNumbers = new Array(slice.length);for (let i = 0; i < slice.length; i++) {byteNumbers[i] = slice.charCodeAt(i);}let byteArray = new Uint8Array(byteNumbers);byteArrays.push(byteArray);}let blob = new Blob(byteArrays, { type: type });return blob;
}

坑:

  1. 如果生成图片样式有问题 html就用内联样式
  2. 当截图片的时候如果不识别 就将图片url转化为base64

相关文章:

js实现框选截屏功能

实现的思路大概就是&#xff0c;先将dom转化为canvas画布&#xff0c;再对canvas进行裁切&#xff0c;然后通过canvas api生成图片&#xff0c;这里用到了一个库html2canvas 效果如图&#xff1a; 首先实现框选效果&#xff1a; const mousedownEvent (e) > {moveX 0;mo…...

Manjaro Linux 连接公司的 VPN 网络

注意&#xff1a;如果你公司的 VPN 网络是在苹果下使用的&#xff0c;本文可能不适用&#xff08;苹果系统不支持 PPTP&#xff09;。 用 Linux 和用 Windows/macOS 不一样&#xff0c;它真的需要用户操心很多东西。比如怎么连接公司的 VPN 网络…… 我是折腾了挺久&#xff0…...

Ama no Jaku

登录—专业IT笔试面试备考平台_牛客网 题目大意&#xff1a;有一个n*n且仅由0和1构成的矩阵&#xff0c;每次操作可以将一整行或一整列的所有数取反&#xff0c;问能否使所有行中构成的最小数>所有列中构成的最大数 1<n<2000 思路&#xff1a;首先&#xff0c;如果…...

视频基础知识

1.视频比特率 视频的比特率是指传输过程中单位时间传输的数据量。可以理解为视频的编码采样率。单位是kbps&#xff0c;即每秒千比特。视频比特率是决定视频清晰度的一个重要指标。比特率越高&#xff0c;视频越清晰&#xff0c;但数据量也会越大。比如一部100分钟的电影&#…...

安全渗透初级知识总结

Day1&#xff1a; xss详解&#xff1a;web攻防之XSS攻击详解——XSS简介与类型 - 知乎 (zhihu.com) Cookie&#xff1a;身份验证 网页元素属性&#xff1a; id&#xff1a; class&#xff1a;样式名称 console.log(div_class);----打印标签 tabindex"0"---这是…...

rocketmq客户端本地日志文件过大调整配置(导致pod缓存cache过高)

现象 在使用rocketmq时&#xff0c;发现本地项目中文件越来越大&#xff0c;查找发现在/home/root/logs/rocketmqlog目录下存在大量rocketmq_client.log日志文件。 配置调整 开启slf4j日志模式&#xff0c;在项目启动项中增加-Drocketmq.client.logUseSlf4jtrue因为配置使用的…...

Unity进阶-ui框架学习笔记

文章目录 Unity进阶-ui框架学习笔记 Unity进阶-ui框架学习笔记 笔记来源课程&#xff1a;https://study.163.com/course/courseMain.htm?courseId1212756805&_trace_c_p_k2_8c8d7393c43b400d89ae94ab037586fc 最上面的管理层&#xff08;canvas&#xff09; using System…...

Django实现接口自动化平台(十四)测试用例模块Testcases序列化器及视图【持续更新中】

相关文章&#xff1a; Django实现接口自动化平台&#xff08;十三&#xff09;接口模块Interfaces序列化器及视图【持续更新中】_做测试的喵酱的博客-CSDN博客 本章是项目的一个分解&#xff0c;查看本章内容时&#xff0c;要结合整体项目代码来看&#xff1a; python django…...

如何高效实现文件传输:小文件采用零拷贝、大文件采用异步io+直接io

一般会如何实现文件传输&#xff1f; 服务器提供文件传输功能&#xff0c;需要将磁盘上的文件读取出来&#xff0c;通过网络协议发送到客户端。如果需要你自己编码实现这个文件传输功能&#xff0c;你会怎么实现呢&#xff1f; 通常&#xff0c;你会选择最直接的方法&#xf…...

Docker运行MySQL5.7

步骤如下&#xff1a; 1.获取镜像&#xff1a; docker pull mysql:5.7 2.创建挂载目录&#xff1a; mkdir /home/mydata/data mkdir /home/mydata/log mkdir /home/mydata/conf 3.先启动docker把配置文件拷贝出来&#xff1a; docker run -it --name temp mysql:5.7 /bi…...

-jar和 javaagent命令冲突吗?

当使用 -jar 命令运行 Java 应用程序时&#xff0c;Java 虚拟机 (JVM) 会忽略任何设置的 -javaagent 命令。这是因为 -jar 命令会覆盖其他命令行选项&#xff0c;包括 -javaagent。 这是因为 -jar 命令是用于运行打包为 JAR 文件的 Java 应用程序的快捷方式。它会忽略其他命令…...

LLC和MAC子层的应用

计算机局域网标准IEEE802 由于局域网只是一个计算机通信网&#xff0c;而且局域网不存在路由选择问题&#xff0c;因此它不需要网络层&#xff0c;而只有最低的两个层次。然而局域网的种类繁多&#xff0c;其媒体接入控制的方法也各不相同。 为了使局域网中的数据链路层不致过…...

【MySQL】之复合查询

【MySQL】之复合查询 基本查询多表查询笛卡尔积自连接子查询单行子查询多行子查询多列子查询在from子句中使用子查询 合并查询小练习 基本查询 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为大写的J按照部门号升序而雇员的工资降序排序使用…...

Vue系列第五篇:Vue2(Element UI) + Go(gin框架) + nginx开发登录页面及其校验登录功能

本篇使用Vue2开发前端&#xff0c;Go语言开发服务端&#xff0c;使用nginx代理部署实现登录页面及其校验功能。 目录 1.部署结构 2.Vue2前端 2.1代码结构 2.1源码 3.Go后台服务 3.2代码结构 3.2 源码 3.3单测效果 4.nginx 5.运行效果 6.问题总结 1.部署结构 2.Vue2…...

u盘里的数据丢失怎么恢复 u盘数据丢失怎么恢复

在使用U盘的时候不知道大家有没有经历过数据丢失或者U盘提示格式化的情况呢&#xff1f;U盘使用久了就会遇到各种各样的问题&#xff0c;而关于U盘数据丢失&#xff0c;大家又知道多少呢&#xff1f;当数据丢失了&#xff0c;我们应该怎样恢复数据&#xff1f;这个问题困扰了很…...

Mysql-约束

约束 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确、有效性和完整性。 分类&#xff1a; 约束描述关键字非空约束限制该字段的数据不能为nullNOT NULL唯一约束保证该字段的所有数据都是唯一…...

数据结构问答7

1. 图的定义和相关术语 答: 定义:图是由顶点集V和边集E组成,其中V为有限非空集。 相关术语:n个顶点,e条边,G=(V,E) ① 邻接点和端点:无向图中,若存在一条边(i, j),则称i,j为该边的端点,且它们互为邻接点;在有向图中,若存在一条边<i, j>,则称i,j分别为…...

[Spark] 大纲

1、Spark任务提交流程 2、SparkSQL执行流程 2.1 RBO&#xff0c;基于规则的优化 2.2 CBO&#xff0c;基于成本的优化 3、Spark性能调优 3.1 固定资源申请和动态资源分配 3.2 数据倾斜常见解决方法 3.3 小文件优化 4、Spark 3.0 4.1 动态分区裁剪(Dynamic Partition Pr…...

【NLP】使用 Keras 保存和加载深度学习模型

一、说明 训练深度学习模型是一个耗时的过程。您可以在训练期间和训练后保存模型进度。因此&#xff0c;您可以从上次中断的地方继续训练模型&#xff0c;并克服漫长的训练挑战。 在这篇博文中&#xff0c;我们将介绍如何保存模型并使用 Keras 逐步加载它。我们还将探索模型检查…...

视频标注是什么?和图像数据标注的区别?

视频数据标注是对视频剪辑进行标注的过程。进行标注后的视频数据将作为训练数据集用于训练深度学习和机器学习模型。这些预先训练的神经网络之后会被用于计算机视觉领域。 自动化视频标注对训练AI模型有哪些优势 与图像数据标注类似&#xff0c;视频标注是教计算机识别对象…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作

一、上下文切换 即使单核CPU也可以进行多线程执行代码&#xff0c;CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短&#xff0c;所以CPU会不断地切换线程执行&#xff0c;从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...