使用html2canvas制作一个截图工具

news2024/11/16 23:57:34

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) {
    let
      lt = 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.coloradmin.cn/o/1948904.html

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

相关文章

【嵌入式硬件】快衰减和慢衰减

1.引语 在使用直流有刷电机驱动芯片A4950时,这款芯片采用的是PWM控制方式,我发现他的正转、反转有两种控制方式,分别是快衰减和慢衰减。 2.理解 慢衰减:相当于加在电机(感性原件)两端电压消失,将电机两端正负短接。 快衰减:相当于加在电机(感性原件)两端电压消失,将电机…

一篇文章讲清楚html css js三件套之html

目录 HTML HTML发展史 HTML概念和语法 常见的HTML标签: HTML 调试 错误信息分析 HTML文档结构 HTML5的新特性 结论 HTML HTML是网页的基础&#xff0c;它是一种标记语言&#xff0c;用于定义网页的结构和内容。HTML标签告诉浏览器如何显示网页元素&#xff0c;例如段落…

快速安装torch-gpu和Tensorflow-gpu(自用,Ubuntu)

要更详细的教程可以参考Tensorflow PyTorch 安装&#xff08;CPU GPU 版本&#xff09;&#xff0c;这里是有基础之后的快速安装。 一、Pytorch 安装 conda create -n torch_env python3.10.13 conda activate torch_env conda install cudatoolkit11.8 -c nvidia pip ins…

WINUI——Microsoft.UI.Xaml.Markup.XamlParseException:“无法找到与此错误代码关联的文本。

开发环境 VS2022 .net core6 问题现象 在Canvas内的子控件要绑定Canvas的兄弟控件的一个属性&#xff0c;在运行时出现了下述报错。 可能原因 在 WinUI&#xff08;特别是用于 UWP 或 Windows App SDK 的版本&#xff09;中&#xff0c;如果你尝试在 XAML 中将 Canvas 内的…

CSS 的工作原理

我们已经学习了CSS的基础知识,它的用途以及如何编写简单的样式表。在本课中,我们将了解浏览器如何获取 CSS 和 HTML 并将其转换为网页。 先决条件:已安装基本软件,了解处理文件的基本知识以及 HTML 基础知识(学习 HTML 简介。目的:要了解浏览器如何解析 CSS 和 HTML 的基…

pytorch前馈神经网络--手写数字识别

前言 具体内容就是&#xff1a; 输入一个图像&#xff0c;经过神经网络后&#xff0c;识别为一个数字。从而实现图像的分类。 资源&#xff1a; https://download.csdn.net/download/fengzhongye51460/89578965 思路&#xff1a; 确定输入的图像&#xff1a;会单通道灰度的…

即时战略游戏:帝国时代2 for Mac 3.3.1769 中文移植版

帝国时代II蛮王崛起是一款非常经典的即时战略游戏&#xff0c;新的地图&#xff0c;四个新战役&#xff0c;新的AI进行整合。帝国时代2玩家将要探索来自“国王时代”和“征服者”扩张的所有原始单人游戏&#xff0c;选择跨越一千年历史的18个文明&#xff0c;并在线上挑战其他玩…

17 敏捷开发—Scrum(2)

从上一篇 「16 敏捷开发实践&#xff08;1&#xff09;」中了解了Scrum是一个用于开发和维护复杂产品的框架&#xff0c;是一个增量的、迭代的开发过程。一般由多个Sprint&#xff08;迭代冲刺&#xff09;组成&#xff0c;每个Sprint长度一般为2-4周。下面全面介绍Scrumde 角色…

2024第29届郑州全国商品交易会

第29届郑州全国商品交易会 2024第四届餐饮与供应链专题展 邀 请 函郑州全国商品交易会&#xff08;简称郑交会&#xff09;是全国大型性经贸活动&#xff0c;一直秉承“政府指导&#xff0c;市场化运作”的模式&#xff0c;自1995年以来已成功举办了二十八届&#xff0c;是国内…

k8s多集群管理工具kubecm

文章目录 一、概述二、安装1、官网链接2、各平台安装2.1、MacOS2.2、Linux2.3、Windows 三、实例1、验证2、配置kubecm自动补全&#xff08;选做&#xff09;2.1、Bash2.2、Zsh2.3、fish2.4、PowerShell 3、创建存放kubeconfig文件的目录4、添加到 $HOME/.kube/config4.1、kube…

Pytorch笔记1

建议点赞收藏关注&#xff01;持续更新至pytorch大部分内容更完。 整体框架如下 目录 gpu加速数据数据结构张量TensorVariable 预处理数据增强 模型构建模块组织复杂网络初始化网络参数定义网络层 损失函数创建损失函数设置损失函数超参数选择损失函数 优化器管理模型参数管理…

JavaWeb学习——请求响应、分层解耦

目录 一、请求响应学习 1、请求 简单参数 实体参数 数组集合参数 日期参数 Json参数 路径参数 总结 2、响应 ResponseBody&统一响应结果 二、分层解耦 1、三层架构 三层架构含义 架构划分 2、分层解耦 引入概念 容器认识 3、IOC&DI入门 4、IOC详解 …

SSM学习9:SpringBoot简介、创建项目、配置文件、多环节配置

简介 SpringBoot式用来简化Spring应用的初始搭建以及开发过程的一个框架 项目搭建 File -> New -> Project 选中pom.xml文件&#xff0c;设置为maven项目 项目启动成功 可以访问BasicController中的路径 配置文件 在resources目录下 application.properties 默…

【初阶数据结构】8.二叉树(3)

文章目录 4.实现链式结构二叉树4.1 前中后序遍历4.1.1 遍历规则4.1.2 代码实现 4.2 结点个数以及高度等4.3 层序遍历4.4 判断是否为完全二叉树4.5层序遍历和判断是否为完全二叉树完整代码 4.实现链式结构二叉树 用链表来表示一棵二叉树&#xff0c;即用链来指示元素的逻辑关系…

巴斯勒相机(Basler) ACE2 dart 系列说明和软件

巴斯勒相机(Basler) ACE2 dart 系列说明和软件

NeuralGCM:革新气候预测的机器学习新纪元

在地球变暖成为全球关注焦点的今天&#xff0c;精确预测气候变化及其影响成为了科学界亟待解决的重大课题。传统基于物理的气候模型&#xff08;GCM&#xff0c;全球气候模型&#xff09;在预测大气、海洋、冰层等复杂系统时虽已取得显著进展&#xff0c;但计算成本高、耗时长且…

系统模块时序图的重要性:解锁系统模块交互的全景视图

在复杂的系统开发中,理解和管理不同模块之间的交互是成功的关键。时序图是一种有效的工具,可以帮助我们清晰地展示这些交互,提升设计和开发的效率。本文将深入探讨系统模块之间的时序图,并通过实例展示其实际应用。 1. 什么是系统模块之间的时序图? 系统模块之间的时序图…

Eclipse 生成 jar 包

打开 Jar 文件向导 Jar 文件向导可用于将项目导出为可运行的 jar 包。 打开向导的步骤为: 在 Package Explorer 中选择你要导出的项目内容。如果你要导出项目中所有的类和资源&#xff0c;只需选择整个项目即可。点击 File 菜单并选择 Export。在输入框中输入"JAR"…

Robot Operating System——Parameter设置的预处理、校验和成功回调

大纲 预处理校验成功回调完整代码测试总结 在《Robot Operating System——对Parameter设置进行校验》一文中&#xff0c;我们通过Node的add_on_set_parameters_callback方法&#xff0c;设置了一个回调函数&#xff0c;用于校验传递过来的Parameter参数。但是这个方法并不能对…

【UbuntuDebian安装Nginx】在线安装Nginx

云计算&#xff1a;腾讯云轻量服务器 操作系统&#xff1a;Ubuntu-v22 1.更新系统软件包列表 打开终端并运行以下命令来确保你的系统软件包列表是最新的&#xff1a; sudo apt update2.安装 Nginx 使用以下命令安装 Nginx&#xff1a; sudo apt install nginx3.启动 Nginx…