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

基于 Vue 3 + html2canvas 实现网页任意区域截图组件

文章目录

  • 一、前言
  • 二、技术栈与原理简析
  • 三、项目准备
    • 3.1 安装依赖
    • 3.2 页面基础布局
  • 四、截图功能实现
    • 4.1 整个区域截图
  • 五、框选截图功能
    • 5.1 实现裁剪区域选择
    • 5.2 根据选区截图
    • 5.3 选区样式绘制
  • 六、样式美化
  • 七、使用示例

一、前言

在实际业务中,经常会有用户截图保存页面内容的需求,比如:

  • 表单填写后的结果截图
  • 页面中某个模块的操作记录截图
  • 可视化大屏、报表区域导出为图片

而我们可以通过 html2canvas 实现将 DOM 节点截图为图片。本篇文章将基于 Vue 3 + html2canvas 封装一个「可指定区域截图」的组件,支持:

  • ✅ 任意 DOM 区域截图
  • 🎯 鼠标框选截图区域
  • 🖼️ 图片预览与下载
  • 🧩 可插入业务模块作为截图内容

二、技术栈与原理简析

  • Vue 3 + <script setup>:组合式 API 更方便封装功能
  • html2canvas:将 HTML 节点渲染为 Canvas,再导出为图片
  • DOM 操作 + 鼠标事件监听:实现可视化选区

原理核心就是:将目标 DOM 渲染为 canvas,然后将 canvas 导出为 base64 或 Blob 图片。


三、项目准备

3.1 安装依赖

npm install html2canvas

3.2 页面基础布局

我们需要一个区域放置截图目标 + 控制按钮:

<template>
  <div class="screenshot-wrapper">
    <div class="toolbar">
      <button @click="captureFull">📷 截图整个区域</button>
      <button @click="toggleCropMode">✂️ 框选截图</button>
    </div>

    <!-- 可截图区域 -->
    <div class="capture-target" ref="captureRef">
      <slot />
    </div>

    <!-- 框选截图遮罩 -->
    <div v-if="cropMode" class="crop-mask" @mousedown="startCrop" />
  </div>
</template>

四、截图功能实现

4.1 整个区域截图

import html2canvas from 'html2canvas';

const captureRef = ref(null);

function captureFull() {
  html2canvas(captureRef.value).then(canvas => {
    const img = canvas.toDataURL('image/png');
    downloadImage(img);
  });
}

function downloadImage(dataUrl) {
  const a = document.createElement('a');
  a.href = dataUrl;
  a.download = 'screenshot.png';
  a.click();
}

五、框选截图功能

5.1 实现裁剪区域选择

const cropMode = ref(false);
const cropBox = reactive({ x: 0, y: 0, w: 0, h: 0 });
let startX = 0, startY = 0;

function toggleCropMode() {
  cropMode.value = !cropMode.value;
}

function startCrop(e) {
  const mask = e.currentTarget;
  startX = e.offsetX;
  startY = e.offsetY;

  const moveHandler = (moveEvent) => {
    cropBox.x = Math.min(startX, moveEvent.offsetX);
    cropBox.y = Math.min(startY, moveEvent.offsetY);
    cropBox.w = Math.abs(startX - moveEvent.offsetX);
    cropBox.h = Math.abs(startY - moveEvent.offsetY);
    drawCropBox(mask);
  };

  const upHandler = () => {
    document.removeEventListener('mousemove', moveHandler);
    document.removeEventListener('mouseup', upHandler);
    captureByCropBox();
    cropMode.value = false;
  };

  document.addEventListener('mousemove', moveHandler);
  document.addEventListener('mouseup', upHandler);
}

5.2 根据选区截图

function captureByCropBox() {
  html2canvas(captureRef.value).then(canvas => {
    const croppedCanvas = document.createElement('canvas');
    const ctx = croppedCanvas.getContext('2d');

    croppedCanvas.width = cropBox.w;
    croppedCanvas.height = cropBox.h;

    ctx.drawImage(
      canvas,
      cropBox.x, cropBox.y, cropBox.w, cropBox.h,
      0, 0, cropBox.w, cropBox.h
    );

    const croppedImg = croppedCanvas.toDataURL('image/png');
    downloadImage(croppedImg);
  });
}

5.3 选区样式绘制

function drawCropBox(maskEl) {
  let box = maskEl.querySelector('.crop-box');
  if (!box) {
    box = document.createElement('div');
    box.className = 'crop-box';
    maskEl.appendChild(box);
  }
  box.style.left = `${cropBox.x}px`;
  box.style.top = `${cropBox.y}px`;
  box.style.width = `${cropBox.w}px`;
  box.style.height = `${cropBox.h}px`;
}

六、样式美化

<style scoped>
.screenshot-wrapper {
  position: relative;
}
.toolbar {
  margin-bottom: 10px;
}
.capture-target {
  border: 1px solid #ccc;
  padding: 10px;
  background: #fff;
  position: relative;
}
.crop-mask {
  position: absolute;
  top: 0; left: 0;
  width: 100%;
  height: 100%;
  cursor: crosshair;
  background-color: rgba(0,0,0,0.05);
}
.crop-box {
  position: absolute;
  border: 2px dashed #409EFF;
  background-color: rgba(64, 158, 255, 0.2);
  pointer-events: none;
}
</style>

七、使用示例

<template>
  <Screenshot>
    <div class="content">
      <h2>这是可截图区域</h2>
      <p>这里的内容将被截图,包括文字、图表等</p>
      <img src="https://via.placeholder.com/300x150" />
    </div>
  </Screenshot>
</template>

<script setup>
import Screenshot from './components/Screenshot.vue';
</script>

到这里,这篇文章就和大家说再见啦!我的主页里还藏着很多 篇 前端 实战干货,感兴趣的话可以点击头像看看,说不定能找到你需要的解决方案~
创作这篇内容花了很多的功夫。如果它帮你解决了问题,或者带来了启发,欢迎:
点个赞❤️ 让更多人看到优质内容
关注「前端极客探险家」🚀 每周解锁新技巧
收藏文章⭐️ 方便随时查阅
📢 特别提醒:
转载请注明原文链接,商业合作请私信联系
感谢你的阅读!我们下篇文章再见~ 💕

在这里插入图片描述

相关文章:

  • 抓wifi无线空口包之Ubuntu抓包(二)
  • Linux-CentOS-7—— 安装MySQL 8
  • Kafka 中的幂等机制
  • SQLI打靶
  • 【嵌入式学习6】多任务版TCP服务器
  • 玄机-第六章-哥斯拉4.0流量分析的测试报告
  • 盛水最多的容器
  • Kafka负载均衡挑战解决
  • Jupyter Notebook不能自动打开默认浏览器怎么办?
  • IDEA快速入门
  • Airflow集成Lark机器人
  • 深入理解PCA降维:原理、实现与应用
  • 【Introduction to Reinforcement Learning】翻译解读2
  • Spring Boot 3.x 集成 MongoDB 的 默认配置项及默认值,以及 常用需要修改的配置项 的详细说明
  • nacos集群启动问题
  • CAS号:288574-78-7,Zinpyr-1可用作PET传感器
  • 【数据分享】2014-2025年全国监测站点的逐时空气质量数据(15个指标\Excel\Shp格式)
  • (PROFINET 转 EtherCAT)EtherCAT/Ethernet/IP/Profinet/ModbusTCP协议互转工业串口网关
  • Linux终止进程(kill process)的一些玩法
  • Jetpack Compose 基础组件学习2.0
  • 免费 网站源码/免费二级域名注册申请
  • 开发公司企业管理制度/排名优化公司