使用html2canvas制作一个截图工具

0 效果

在这里插入图片描述

1 下载html2canvas

npm install html2canvas --save

2 创建ClipScreen.js

import html2canvas from 'html2canvas';
// 样式
const cssText = {box: 'overflow:hidden;position:fixed;left:0;top:0;right:0;bottom:0;background-color:rgba(255,255,255,0.9);z-index: 100000;',img: '',mask: 'position:absolute;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,0.6);',rect: 'position:absolute;border:1px solid #3e8ef7;box-sizing:border-box;cursor:move;user-select:none;background: url() no-repeat;',toolBox: 'position:absolute;top:0;left:0;padding:0 10px;background:#eee;line-height:2em;text-align:right;',toolBtn: 'font-weight:bold;color:#111;margin:0 1em;user-select:none;font-size:12px;cursor:pointer;',
}
/*** dom节点截图工具(基于html2canvas)* dom: 要截图的目标dom* options: { *   // 以下三个回调方法作用域this指向构造函数*   success: function(res), //截图完成触发 参数为截图结果*   fail: function(), //取消截图触发*   complete: function(), //截图结束触发 success和fail都会触发* }* * 调用示例:* new ClipScreen(dom节点, {*   success: function (res) {},*   complete: function () {},* });*/
class ClipScreen {constructor(dom, options) {if (window.ClipScreen) return false;window.ClipScreen = this;this.dom = dom;this.options = options;html2canvas(this.dom, {useCORS: true}).then((canvas) => {let dataURL = canvas.toDataURL("image/png");this.imgUrl = dataURL;this.start();});}// 初始化start() {this.border = 2; //用于计算选区拖拽点和边界的判断this.win_w = window.innerWidth;this.win_h = window.innerHeight;let box = this.box = document.createElement('div');box.id = 'ClipScreen';box.style.cssText = cssText.box;let img = document.createElement('img');img.style.cssText = cssText.img;img.src = this.imgUrl;let mask = document.createElement('div');mask.style.cssText = cssText.mask;box.appendChild(img);box.appendChild(mask);document.body.appendChild(box);img.onload = (e) => {let w = img.offsetWidth,h = img.offsetHeight,win_w = window.innerWidth,win_h = window.innerHeight,left = (win_w - w) / 2,top = (win_h - h) / 2;img.style.position = 'absolute';img.style.left = left + 'px';img.style.top = top + 'px';img.style.width = w + 'px';img.style.height = h + 'px';this.axis = {left,top}this.img = img;this.bindEvent(mask);}}// 绑定蒙版事件、键盘事件bindEvent(mask) {document.onkeydown = (e) => {if (e.keyCode == 27) {this.cancel();}}mask.onmousedown = (e) => {let offsetX = e.offsetX,offsetY = e.offsetY;document.onmousemove = (e) => {let x = e.offsetX,y = e.offsetY,sx = offsetX,sy = offsetY,w = Math.abs(offsetX - x),h = Math.abs(offsetY - y);if (x < offsetX) sx = x;if (y < offsetY) sy = y;this.createRect(sx, sy, w, h);}document.onmouseup = (e) => {this.moveToolBox();this.rect.style.pointerEvents = 'initial';this.unbindMouseEvent();}}}// 创建矩形截图选区createRect(x, y, w, h) {let rect = this.rect;if (!rect) {rect = this.rect = document.createElement('div');rect.style.cssText = cssText.rect;rect.style.backgroundImage = 'url(' + this.imgUrl + ')';// this.newImg = document.createElement('img');// this.newImg.style.cssText = cssText.rect_img;// rect.appendChild(this.newImg);let doms = this.createPoints(rect);this.box.appendChild(rect);this.bindRectEvent(doms);}let border = this.border;if (x <= border) x = border;if (y <= border) y = border;if (x + w >= this.win_w - border) x = this.win_w - border - w;if (y + h >= this.win_h - border) y = this.win_h - border - h;rect.style.pointerEvents = 'none';rect.style.display = 'block';rect.style.left = x + 'px';rect.style.top = y + 'px';rect.style.width = w + 'px';rect.style.height = h + 'px';rect.style.backgroundPosition = (-x + this.axis.left - 1) + 'px ' + (-y + this.axis.top - 1) + 'px';if (this.toolBox) this.toolBox.style.display = 'none';}// 创建截图选区各个方位拉伸点createPoints(rect) {letlt = document.createElement('span'),tc = document.createElement('span'),rt = document.createElement('span'),rc = document.createElement('span'),rb = document.createElement('span'),bc = document.createElement('span'),lb = document.createElement('span'),lc = document.createElement('span');let c_style = 'position:absolute;width:5px;height:5px;background:#3e8ef7;';lt.style.cssText = c_style + 'left:-3px;top:-3px;cursor:nw-resize;';tc.style.cssText = c_style + 'left:50%;top:-3px;margin-left:-3px;cursor:ns-resize;';rt.style.cssText = c_style + 'right:-3px;top:-3px;cursor:ne-resize;';rc.style.cssText = c_style + 'top:50%;right:-3px;margin-top:-3px;cursor:ew-resize;';rb.style.cssText = c_style + 'right:-3px;bottom:-3px;cursor:nw-resize;';bc.style.cssText = c_style + 'left:50%;bottom:-3px;margin-left:-3px;cursor:ns-resize;';lb.style.cssText = c_style + 'left:-3px;bottom:-3px;cursor:ne-resize;';lc.style.cssText = c_style + 'top:50%;left:-3px;margin-top:-3px;cursor:ew-resize;';let res = {lt,tc,rt,rc,rb,bc,lb,lc}for (let k in res) {rect.appendChild(res[k])}res.rect = rect;return res;}// 生成 、移动工具moveToolBox() {let toolBox = this.toolBox;if (!toolBox) {toolBox = this.toolBox = document.createElement('div');toolBox.style.cssText = cssText.toolBox;let save = document.createElement('span'),cancel = document.createElement('span');save.innerText = '完成';cancel.innerText = '取消';save.style.cssText = cancel.style.cssText = cssText.toolBtn;toolBox.appendChild(cancel);toolBox.appendChild(save);this.box.appendChild(toolBox);this.bindToolBoxEvent(save, cancel);}toolBox.style.display = 'block';let border = this.border;let t_w = this.toolBox.offsetWidth,t_h = this.toolBox.offsetHeight,r_t = this.rect.offsetTop,r_h = this.rect.offsetHeight;let t = r_t + r_h + 10,l = this.rect.offsetLeft + this.rect.offsetWidth - t_w;if (l <= border) l = border;if (t >= this.win_h - border - t_h) t = r_t - t_h - 10;if (r_h >= this.win_h - border - t_h) {t = r_t + r_h - t_h - 10;l -= 10;}toolBox.style.top = t + 'px';toolBox.style.left = l + 'px';}// 绑定工具栏事件bindToolBoxEvent(save, cancel) {save.onclick = () => {this.success();}cancel.onclick = () => {this.cancel();}}// 绑定截图选区事件bindRectEvent(o) {o.rect.addEventListener("mousedown", (e) => {let border = this.border;let $target = e.target;let offsetX = e.x,offsetY = e.y;let r_w = o.rect.offsetWidth,r_h = o.rect.offsetHeight,r_l = o.rect.offsetLeft,r_t = o.rect.offsetTop;if ($target == o.rect) {offsetX = e.offsetX;offsetY = e.offsetY;document.onmousemove = (e) => {let dif_x = e.x - offsetX,dif_y = e.y - offsetY;if (dif_x <= border) dif_x = border;if (dif_y <= border) dif_y = border;if (dif_x + r_w >= this.win_w - border) dif_x = this.win_w - border - r_w;if (dif_y + r_h >= this.win_h - border) dif_y = this.win_h - border - r_h;o.rect.style.left = dif_x + 'px';o.rect.style.top = dif_y + 'px';o.rect.style.backgroundPosition = (-dif_x + this.axis.left - 1) + 'px ' + (-dif_y + this.axis.top - 1) + 'px';this.toolBox.style.display = 'none'}} else {document.onmousemove = (e) => {this.toolBox.style.display = 'none'this.transform($target, o, offsetX, offsetY, r_w, r_h, r_l, r_t, e)}}document.onmouseup = (e) => {this.moveToolBox();this.unbindMouseEvent();}})}// 拉伸选区transform($t, o, offsetX, offsetY, r_w, r_h, r_l, r_t, e) {let border = this.border;let x = e.x,y = e.y;if (x <= border) x = border;if (y <= border) y = border;if (x >= this.win_w - border) x = this.win_w - border;if (y >= this.win_h - border) y = this.win_h - border;let dif_x = x - offsetX,dif_y = y - offsetY;let min = 10;let left = r_l,top = r_t,width = r_w,height = r_h;if ($t == o.lt) {if (r_w - dif_x <= min || r_h - dif_y <= min) return false;left = r_l + dif_x;top = r_t + dif_y;width = r_w - dif_x;height = r_h - dif_y;} else if ($t == o.tc) {if (r_h - dif_y <= min) return false;top = r_t + dif_y;height = r_h - dif_y;} else if ($t == o.rt) {if (r_w + dif_x <= min || r_h - dif_y <= min) return false;top = r_t + dif_y;width = r_w + dif_x;height = r_h - dif_y;} else if ($t == o.rc) {if (r_w + dif_x <= min) return false;width = r_w + dif_x;} else if ($t == o.rb) {if (r_w + dif_x <= min || r_h + dif_y <= min) return false;width = r_w + dif_x;height = r_h + dif_y;} else if ($t == o.bc) {if (r_h + dif_y <= min) return false;height = r_h + dif_y;} else if ($t == o.lb) {if (r_w - dif_x <= min || r_h + dif_y <= min) return false;left = r_l + dif_x;width = r_w - dif_x;height = r_h + dif_y;} else if ($t == o.lc) {if (r_w - dif_x <= min) return false;left = r_l + dif_x;width = r_w - dif_x;}o.rect.style.left = left + 'px';o.rect.style.top = top + 'px';o.rect.style.width = width + 'px';o.rect.style.height = height + 'px';o.rect.style.backgroundPosition = (-left + this.axis.left - 1) + 'px ' + (-top + this.axis.top - 1) + 'px';}// 解绑事件unbindMouseEvent() {document.onmousemove = null;document.onmouseup = null;}// 生成base64图片getImagePortion(imgDom, new_w, new_h, s_x, s_y) {let sx = s_x - this.axis.left,sy = s_y - this.axis.top;let t_cv = document.createElement('canvas');let t_ct = t_cv.getContext('2d');t_cv.width = new_w;t_cv.height = new_h;let b_cv = document.createElement('canvas');let b_ct = b_cv.getContext('2d');b_cv.width = imgDom.width;b_cv.height = imgDom.height;b_ct.drawImage(imgDom, 0, 0);t_ct.drawImage(b_cv, sx, sy, new_w, new_h, 0, 0, new_w, new_h);let res = t_cv.toDataURL();return res;}// 完成success() {let imgBase64 = this.getImagePortion(this.img, this.rect.offsetWidth, this.rect.offsetHeight, this.rect.offsetLeft, this.rect.offsetTop);if (this.options) {this.options.success && this.options.success.call(this, imgBase64);}this.close();}// 取消cancel() {if (this.options) {this.options.fail && this.options.fail.call(this);}this.close();}// 关闭close() {if (this.options) {this.options.complete && this.options.complete.call(this);}this.distroy();}// 销毁distroy() {window.ClipScreen = undefined;this.box.remove();}
}
export default ClipScreen

3 使用

① 引入ClipScreen.js
② 获取需要截图的div

  <div style="position: absolute; top: 0; left: 0; bottom: 0; right: 0;" ref="toImage"><button type="primary" size="mini" @click="screenshot">一键截图</button><div style="background-color: red; margin-top: 20px;"><div style="height: 200px; width: 400px;">123</div><div style="height: 200px; width: 400px;">123</div></div></div>
    screenshot() {let canvasItem = this.$refs.toImage;new ClipScreen(canvasItem, {success: function(res) { // 完成截图let screenshotImage = document.createElement('a');screenshotImage.href = res;screenshotImage.download = '网页截图';screenshotImage.click();},fail: function() {}, // 取消截图complete: function(res) {} // 结束截图})}

4 其他

截取iframe内容时,会空白,暂时未解决。
也可以使用 js-web-screen-shot

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

vue3前端开发-小兔鲜项目-路由拦截器增加token的携带

vue3前端开发-小兔鲜项目-路由拦截器增加token的携带&#xff01;实际开发中&#xff0c;很多业务接口的请求&#xff0c;都要求必须是登录状态&#xff01;为此&#xff0c;这个token信息就会频繁的被加入到了请求头部信息中。request请求头内既然需要频繁的携带这个token.我们…

如何为 DigitalOcean 上的托管数据库收集可观测指标

DigitalOcean 在 2024 年 5 月开始支持在托管数据库&#xff08;PostgreSQL、MySQL、Redis和Kafka&#xff09;中收集可观测指标。我们将在本偏内容中&#xff0c;告诉大家如何使用部署在 DigitalOcean App Platform 上的网络应用程序&#xff0c;为 DigitalOcean 上的 Postgre…

Git的应用及码云的应用

Git 准备一台主机&#xff0c;安装应用git [rootgit ~]# yum -y install git [rootgit ~]# rpm -ql git 查看版本信息 [rootgit ~]# git [rootgit ~]# mkdir /yy000 创建目录 使用git指令&#xff0c;一定要cd到初始化之后的目录 cd到yy000目录中使用init指令促使初始化 [ro…

uniapp开发精选短视频视频小程序实战笔记20240725,实现顶部轮播图和热门短剧

创建项目 创建项目,叫video_app。 在pages.json里面修改一下标题: 新建search搜索页面和me我的页面。 此时界面预览效果如下: 引入静态资源 主要是static里面的内容,全部复制过来。 配置底部导航栏 pages.json,放到顶层,和全部样式同级: "tabBar&quo…

ChatGPT的原理和成本

ChatGPT就是人机交互的一个底层系统&#xff0c;某种程度上可以类比于操作系统。在这个操作系统上&#xff0c;人与AI之间的交互用的是人的语言&#xff0c;不再是冷冰冰的机器语言&#xff0c;或者高级机器语言&#xff0c;当然&#xff0c;在未来的十来年内&#xff0c;机器语…

Origin多个图层的层叠顺序调整

如果你有多个图层在一个图表中&#xff0c;可以在右上方的对象管理器中调整它们之间的层级关系—— 在对象管理器中处于第一位的图层&#xff0c;是层级最低的图层&#xff0c;即处于图表的最次优先显示&#xff1b;反之&#xff0c;处于最后一位的图层&#xff0c;是层级最高…

LeetCode 热题 HOT 100 (011/100)【宇宙最简单版】

【图论】No. 0200 岛屿数量 【中等】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#xf…

C语言 | Leetcode C语言题解之第279题完全平方数

题目&#xff1a; 题解&#xff1a; // 判断是否为完全平方数 bool isPerfectSquare(int x) {int y sqrt(x);return y * y x; }// 判断是否能表示为 4^k*(8m7) bool checkAnswer4(int x) {while (x % 4 0) {x / 4;}return x % 8 7; }int numSquares(int n) {if (isPerfect…

本宫欢喜新荣记香港:米其林美学月子餐鉴赏和疗愈月子护理之旅

作为大湾区新晋“顶流”母婴护理中心和待产月子大健康解决方案提供者&#xff0c;已具备16年母婴月子专业护理经验的本宫欢喜&#xff0c;近两年备受国内和亚太备孕、待产妈妈群体和家庭珍爱。今年7月下旬&#xff0c;本宫欢喜大湾区百万影响力KOL妈妈系列体验之旅中&#xff0…

【数据结构】搜索二叉树

二叉搜索树 二叉树的博客 在之前的数据结构的文章中已经基本对二叉树有一定的了解&#xff0c;二叉搜索树也是一种数据结构&#xff0c;下面将对二叉搜索树进行讲解。 二叉搜索树的概念 二叉搜索树又称为二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有下面性…

ICIP-2020-A Non-local Mean Temporal Filter for VideoCompression

在 libvpx、VP8、VP9 和 HEVC 等各种编码器实现中&#xff0c;早就发现在预处理阶段过程中从源视频信号去除噪声对客观压缩效率的提升存在好处。通常使用常规的块匹配运动搜索来构建运动轨迹&#xff0c;并沿着轨迹比较每对像素&#xff0c;根据像素间的差异确定时域滤波器系数…

网络原理_初识

目录 一、局域网LAN 二、广域网WAN 三、网络通信基础 3.1 IP地址 3.2 端口号 3.3 协议 3.4 五元组 3.5 OSI七层模型 3.6 TCP/IP五层模型 3.7 网络设备所在分层 3.8 封装和分用 总结 一、局域网LAN 局域网&#xff0c;即 Local Area Network&#xff0c;Local 即标…

Godot游戏制作 05收集物品

创建新场景&#xff0c;添加Area2D节点&#xff0c;AnimatedSprite2D节点 &#xff0c;CollisionShape2D节点 添加硬币 按F键居中&#xff0c;放大视图。设置动画速度设为10FPS&#xff0c;加载后自动播放&#xff0c;动画循环 碰撞形状设为圆形&#xff0c;修改Area2D节点为Co…

看2024如何利用IT项目管理软件实现项目稳定输出,创造价值

曾经做为一个在大型互联网公司工作了10年的项目实施工作人员来讲&#xff0c;亲眼见证了IT项目管理软件的兴起和发展&#xff0c;也深刻体会到它在提升项目效率和管理水平方面的巨大价值。它就像一把神奇的钥匙&#xff0c;打开了项目管理的新世界&#xff0c;让原本混乱无序的…

docer笔记3

docker笔记3 容器基本命令 容器基本命令 下载镜像 docker pull cento新建容器并启动 docker run [可选参数] image# 参数说明 --name“Name” 容器名字 tomcat01 tomcat02 用来区分容器 -d 后台方式运行 -it 使用交互方式运行&#xff0c;进入容器查…

leetcode3098. 求出所有子序列的能量和

官解 class Solution(object):# 定义常量mod int(1e9 7) # 模数&#xff0c;用于防止结果溢出inf float(inf) # 无穷大&#xff0c;用于初始化时的特殊值def sumOfPowers(self, nums, k):n len(nums) # 数组长度res 0 # 用于存储最终结果# 三维动态规划表&#xff0c;…

七、SpringBoot日志

1. 得到日志对象 import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //打印日志…

【C语言】数组栈的实现

栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。 压栈&#…

springboot校园跑腿服务系统-计算机毕业设计源码15157

摘要 本文介绍了一种基于Springboot和uniapp的校园跑腿服务系统的设计与实现。该系统旨在为大学校园提供一种方便快捷的跑腿服务&#xff0c;满足学生和教职员工的日常需求。首先&#xff0c;系统采用了Springboot作为后端框架&#xff0c;利用其轻量级、高效的特性&#xff0c…

学习笔记 韩顺平 零基础30天学会Java(2024.7.22)

P407 接口使用细节2 P407 接口课堂练习 对于最后一个的输出&#xff1a;B因为实现了A的接口&#xff0c;所以和继承一样&#xff0c;B可以访问A的变量 P409 接口VS继承 接口对单继承机制&#xff08;是指只能继承一个类&#xff09;进行了补充 也可以理解为&#xff0c;子类通过…