首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

canvas实现截图功能

编程知识
2024年08月01日 08:09

开篇

最近在做一个图片截图的功能。
因为工作时间很紧张,
当时是使用的是一个截图插件。
周末两天无所事事,来写一个简单版本的截图功能。
因为写的比较简单,如果写的不好,求大佬轻一点喷

读取图片并获取图片的宽度和高度思路

首先读取文件我们使用input中类型是file。
我们需要对读取的对象进行限制,必须是图片类型。
这个可以使用这个属性:accept="image/*" 来实现
但是这个属性不可靠,最好还是通过正则来判断。
我们要获取图片的宽和高,需要创建FileReader对象。
使用reader.readAsDataURL(file)异步读取文件内容,
并将其编码为一个Data URL(数据URL)
当文件读取完成之后,会触发reader.onload事件。
这个时候我们还要创建一个图片对象。
等待这个图片读取完成后,通过 img.width, img.height返回图片的宽和高。
下面我们就来简单实现一下

读取图片并获取图片的宽度

<div>
  <input  type="file" accept="image/*" />
</div>
<script>
  // 获取文件节点
  let fileNode = document.getElementById("file")
  // 给文件节点注册事件
  fileNode.addEventListener("change", readFile)
  // 读取文件,然后返回宽度和高度
  function readFile(e){
    let file = e.target.files[0]
    getImageWH(file, function(width, height) {  
      console.log('Width:', width, 'Height:', height);  
    }); 
  }
  // 返回文件(图片的宽和高)
  function getImageWH(file, callback) {  
    // 创建一个FileReader实例  
    const reader = new FileReader();  
    // 当文件读取完成时触发  
    reader.onload = function(e) {  
      // e 这个对象中包含这个图片相关的属性
      console.log('e这个对象', e)
      // 创建一个新的Image对象  
      const img = new Image();  
      // 设置Image的src为读取到的文件内容  
      img.src = e.target.result;  
      // 当图片加载时触发  
      img.onload = function() {  
        // 调用回调函数,并传入图片的宽高  
        callback(img.width, img.height);  
      };
    };
    // 开始读取文件内容,以DataURL的形式 
    // reader.onload 方法的执行需要调用下面这个 reader.readAsDataURL
    reader.readAsDataURL(file);  
  } 
  </script>

将图片的宽高赋值给canvas

我们在获取图片的宽和高之后然后赋值给canvas。
并且将canvas给显示出来就行。
这一步比较简单
<style>
  .canvas-box{
    border: 1px solid red;
    display: none;
  }
</style>
<canvas></canvas>
// 获取canvas节点
let canvasNode = document.getElementById("canvas-node")

// 读取文件
function readFile(e){
  let file = e.target.files[0]
  getImageWH(file, function(width, height) {  
    // 将宽度和高度传给canvasSetWH函数,显示在页面上
    canvasSetWH(canvasNode,width, height)
  }); 
}

function canvasSetWH(canvasNode,width, height){
  canvasNode.width = width
  canvasNode.height = height
  canvasNode.style.display = "block"
}

将图片内容在canvas中显示出来

想要将图片绘制出来,此时我们需要借助drawImage这个API。
这个API有三种形式的传参

第1种:drawImage(image, x, y)
image: 绘制的图像源
x, y:  图像在画布上的起始坐标(x,y), 图像将以原始尺寸绘制

第2种:drawImage(image, x, y, width, height)
image: 绘制的图像源
x, y:  图像在画布上的起始坐标(x,y)
width, height(可选):绘制到画布上的图像的宽度和高度

第3种: drawImage(image, sx, sy, swidth, sheight, dx, dy, dwidth, dheight)
image: 绘制的图像源
sx, sy: 图像源中矩形区域的起始坐标
swidth, sheight:图像源中矩形区域的宽度和高度,即要绘制的图像部分
dx, dy:绘制到画布上的起始坐标
dwidth, dheight:绘制到画布上的矩形区域的宽度和高度,允许对绘制的图像进行缩放

也就是说:我们这里绘制可以使用第1种方法和第2种方法。
图像源在getImageWH 这个方法中返回来。
// 返回文件(图片的宽和高和图像源)
function getImageWH(file, callback) {  
  // ....其他代码.....
  // 当文件读取完成时触发  
  reader.onload = function(e) {  
    // ....其他代码.....
    // 当图片加载时触发  
    img.onload = function() {  
      // 调用回调函数,返回图像源,图片的宽度,高度
      callback(img,img.width, img.height);  
    };
  };
  // 开始读取文件内容,以DataURL的形式 
  // reader.onload 方法的执行需要调用下面这个 reader.readAsDataURL
  reader.readAsDataURL(file);  
} 

// 获取canvas节点
let canvasNode = document.getElementById("canvas-node")
// 创建上下文
let ctx = canvasNode.getContext("2d")

function readFile(e){
  let file = e.target.files[0]
  getImageWH(file, function(img, width, height) {  
    // 将宽度和高度传给canvasSetWH函数,显示在页面上
    canvasSetWH(canvasNode,width, height)
    // 将图片绘制出来
    ctx.drawImage(img, 0, 0,width, height );
  }); 
}

绘制蒙层

绘制蒙层这一步相对比较简单。
我们只需要在图片上绘制一个跟图片大小一样的蒙层即可。
可以借助fillStyle来填充颜色。fillRect绘制矩形。
下面我们简单实现一下
// 调用绘制蒙层的方法(在绘制图片完成后调用这个函数)
drawMask(0,0,width, height);

//绘制蒙层
function drawMask(x, y, width, height, opactity) {
  ctx.fillStyle = "rgba(0,0,0,0.5)";
  ctx.fillRect(x, y, width, height);
}

绘制截图区域

我们需要给canvas绑定鼠标按下事件。
在鼠标按下的时候记录上当前鼠标的坐标信息(x,y)
在鼠标按下的时候还要注册移动事件和抬起事件。
在鼠标移动的时候计算出蒙层的位置信息(rectEndX,rectEndY)
然后计算出截图区域的位置信息
最后还需要鼠标抬起的时候要移除移动事件和抬起事件
下面我们来简单实现一下
.... 其他代码.....
// 图像源
let img = new Image();
// 注册事件用于得到鼠标按下时的偏移量
canvasNode.addEventListener("mousedown", mousedownInCanvasHandler)
let currentPoint = {}
// 鼠标按下
function mousedownInCanvasHandler(e){
  currentPoint= { x: e.offsetX, y: e.offsetY }
  // 按下鼠标的时候我们需要注册移动事件和抬起事件
  canvasNode.addEventListener('mousemove', mousemoveInCanvasHandler)
  canvasNode.addEventListener('mouseup', mouseupInCanvasHandler)
}

// 绘制矩形
function mousemoveInCanvasHandler(e){
  let rectEndX = e.offsetX
  let rectEndY = e.offsetY
  // 得到矩形的宽度和高度
  let rectWidth = rectEndX - currentPoint.x
  let rectHeight = rectEndY - currentPoint.y
  let {width, height} = canvasNode
  ctx.clearRect(0, 0, width, height)
  // 绘制蒙层
  drawMask(0,0,width, height);
  drawScreenShot(width, height,rectWidth, rectHeight)
}
// 绘制截图
function drawScreenShot( canvasWidth, canvasHeight,rectWidth,rectHeight){
  // 在原图形之外画出一个矩形
  ctx.globalCompositeOperation = "destination-out";
  ctx.fillStyle='#000'
  ctx.fillRect(currentPoint.x, currentPoint.y,rectWidth,rectHeight)
  ctx.globalCompositeOperation ='destination-over'
  // 绘制截图区域的矩形
  ctx.drawImage(img, 0, 0,canvasWidth, canvasHeight,0,0,canvasWidth, canvasHeight );
}
// 鼠标抬起的时候要移除移动事件和抬起事件
function mouseupInCanvasHandler(e){
  canvasNode.removeEventListener('mousemove', mousemoveInCanvasHandler)
  canvasNode.removeEventListener('mouseup', mouseupInCanvasHandler)
}
.... 其他代码.....

把截图的区域显示出来

我们只需要在截图完成后(鼠标抬起时)
得到截图区域的信息ctx.getImageData()
然后把截图区域的信息写入一个新的画布即可。
在绘制前先清空画布
<style>
  .canvas-box,.canvas2-box{
    display: none;
  }
</style>
<body>
  <!-- 文件读取 -->
   <div>
     <input  type="file" accept="image/*" />
   </div>
   <canvas></canvas>
   <!-- 截图区域的图像显示在下面这个新的画布上 -->
   <div>
    <canvas></canvas>
   </div>
</body>
// 将截图区域的数据保存下来
screenshotData= [currentPoint.x, currentPoint.y, rectWidth, rectHeight]

// 鼠标抬起的时候要移除移动事件和抬起事件
function mouseupInCanvasHandler(e){
  canvasNode.removeEventListener('mousemove', mousemoveInCanvasHandler)
  canvasNode.removeEventListener('mouseup', mouseupInCanvasHandler)
  drawScreenShotImg(screenshotData)
}

// 绘制一个截图区域的信息在另外一个画布上,并且将他显示出来
function drawScreenShotImg(screenshotData){
  // screenshotData是截图的开始和结束坐标
  let drawData = ctx.getImageData(...screenshotData)
  canvasSetWH(canvas2Box,screenshotData[2],screenshotData[3])
  // 先清空画布,注意清空的大小,否者会造成叠加(清除不干净)
  ctx2.clearRect(0,0, currentPoint.x, currentPoint.y)
  // 将截图区域绘制到canvas2上
  ctx2.putImageData(drawData,0,0)
}

将截图区域的部分下载下来

将canvas下载下来时,需要借助
语法:canvas.toDataURL(picType, encoderOptions)
参数:
picType:表示的是图片的格式。默认为 image/png。
encoderOptions:从 0 到 1 的区间内选择图片的质量。
  如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

获取图片的类型我们可以通过(file.type)来知道
let file = e.target.files[0]
// 得到图片的类型,等会下载的时候需要
fileType = file.type
<button>down</button>
// 注册下载事件
downBtn.addEventListener('click',()=>{
  let {width, height} = canvas2
  // toDataURL的第一个参数:图片格式,默认为 image/png,
  // 第2个参数:可以从 0 到 1 的区间内选择图片的质量。
  let imgURL = canvas2.toDataURL( fileType, 1);
  let link = document.createElement('a');
  link.download = "截图图片";
  link.href = imgURL;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
})

截图功能全部代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>canvas实现截图功能</title>
  <style>
    .canvas-box,.canvas2-box{
      display: none;
    }
  </style>
</head>
<body>
  <!-- 文件读取 -->
   <div>
     <input  type="file" accept="image/*" />
   </div>
   <canvas></canvas>
   <button>down</button>
   <div>
    <canvas></canvas>
   </div>
</body>
<script>
  // 获取canvas节点
  let canvasNode = document.getElementById("canvas-node")
  // 创建上下文
  let ctx = canvasNode.getContext("2d")
  let downBtn =  document.getElementById("downBtn")
  
  let canvas2Box = document.querySelector(".canvas2-box")
  let canvas2 = document.getElementById("canvas2")
  let ctx2 = canvas2.getContext("2d")
  // 获取文件节点
  let fileNode = document.getElementById("file")
  // 给文件节点注册事件
  fileNode.addEventListener("change", readFile)
  // 图像源
  let img = new Image();
  // 截图区域的数据
  let screenshotData = []
  let fileType = "" // 文件的类型,下载的时候需要
  // 注册事件用于得到鼠标按下时的偏移量
  canvasNode.addEventListener("mousedown", mousedownInCanvasHandler)
  let currentPoint = {}

  // 注册下载事件
  downBtn.addEventListener('click',()=>{
    let {width, height} = canvas2
    // format:表示的是图片的类型  "image/png"
    // toDataURL的第一个参数:图片格式,默认为 image/png,
    // 第2个参数:可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
    let imgURL = canvas2.toDataURL( fileType, 1);
    let link = document.createElement('a');
    link.download = "截图图片";
    link.href = imgURL;
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  })
  // 鼠标按下
  function mousedownInCanvasHandler(e){
    currentPoint= { x: e.offsetX, y: e.offsetY }
    // 按下鼠标的时候我们需要注册移动事件和抬起事件
    canvasNode.addEventListener('mousemove', mousemoveInCanvasHandler)
    canvasNode.addEventListener('mouseup', mouseupInCanvasHandler)
  }
  
  // 绘制矩形
  function mousemoveInCanvasHandler(e){
    let rectEndX = e.offsetX
    let rectEndY = e.offsetY
    // 得到矩形的宽度和高度
    let rectWidth = rectEndX - currentPoint.x
    let rectHeight = rectEndY - currentPoint.y
    let {width, height} = canvasNode
    // 将截图区域的数据保存下来
    screenshotData= [currentPoint.x, currentPoint.y, rectWidth, rectHeight]
    ctx.clearRect(0, 0, width, height)
    // 绘制蒙层
    drawMask(0,0,width, height);
    drawScreenShot(width, height,rectWidth, rectHeight)
  }
  // 绘制截图
  function drawScreenShot( canvasWidth, canvasHeight,rectWidth,rectHeight){
    // 在原图形之外画出一个矩形
    ctx.globalCompositeOperation = "destination-out";
    ctx.fillStyle='#000'
    ctx.fillRect(currentPoint.x, currentPoint.y,rectWidth,rectHeight)
    ctx.globalCompositeOperation ='destination-over'
    // 绘制截图区域的矩形
    ctx.drawImage(img, 0, 0,canvasWidth, canvasHeight,0,0,canvasWidth, canvasHeight );
  }

  
  // 鼠标抬起的时候要移除移动事件和抬起事件
  function mouseupInCanvasHandler(e){
    canvasNode.removeEventListener('mousemove', mousemoveInCanvasHandler)
    canvasNode.removeEventListener('mouseup', mouseupInCanvasHandler)
    drawScreenShotImg(screenshotData)
  }

  // 绘制一个截图区域的信息在另外一个画布上,并且将他显示出来
  function drawScreenShotImg(screenshotData){
    // screenshotData是截图的开始和结束坐标
    let drawData = ctx.getImageData(...screenshotData)
    canvasSetWH(canvas2Box,screenshotData[2],screenshotData[3])
    // 先清空画布,注意清空的大小,否者会造成叠加(清除不干净)
    ctx2.clearRect(0,0, currentPoint.x, currentPoint.y)
    // 将截图区域绘制到canvas2上
    ctx2.putImageData(drawData,0,0)
  }


  // 读取文件
  function readFile(e){
    let file = e.target.files[0]
    // 得到图片的类型,等会下载的时候需要
    console.log('file.type', file.type)
    fileType = file.type
    getImageWH(file, function(width, height) {  
      // 将宽度和高度传给canvasSetWH函数,显示在页面上
      canvasSetWH(canvasNode,width, height)
      ctx.drawImage(img, 0, 0,width, height );
      // 调用绘制蒙层的方法
      drawMask(0,0,width, height);
    });
  }


  // 返回文件(图片的宽和高)
  function getImageWH(file, callback) {  
    // 创建一个FileReader实例  
    const reader = new FileReader();  
    // 当文件读取完成时触发  
    reader.onload = function(e) {  
      // e 这个对象中包含这个图片相关的属性
      console.log('e这个对象', e)
      // 创建一个新的Image对象  
        
      // 设置Image的src为读取到的文件内容  
      img.src = e.target.result;  
      // 当图片加载时触发  
      img.onload = function() {  
        // 调用回调函数,返回图像源,图片的宽度,高度
        callback(img.width, img.height);  
      };
    };
    // 开始读取文件内容,以DataURL的形式 
    // reader.onload 方法的执行需要调用下面这个 reader.readAsDataURL
    reader.readAsDataURL(file);  
  } 

  function canvasSetWH(canvasNode,width, height){
    canvasNode.width = width
    canvasNode.height = height
    canvasNode.style.display = "block"
  }

  // 绘制蒙层
  function drawMask(x, y, width, height, opactity) {
    ctx.fillStyle = "rgba(0,0,0,0.5)";
    ctx.fillRect(x, y, width, height);
  }
</script>
</html>

尾声

终于写完了,这周过得很充实。。。
如果觉得写的不错,求未来的老板们点个赞,感谢!
From:https://www.cnblogs.com/IwishIcould/p/18328952
本文地址: http://www.shuzixingkong.net/article/655
0评论
提交 加载更多评论
其他文章 PHP转Go系列 | Carbon 时间处理工具的使用姿势
在日常的开发过程中经常会遇到对时间的处理,比如将时间戳进行格式化、获取昨天或上周或上个月的时间、基于当前时间进行加减等场景的使用
PHP转Go系列 | Carbon 时间处理工具的使用姿势
低代码如何借助 K8s 实现高并发支持?
引言 在当今这个数字化时代,互联网的普及和技术的飞速发展使得应用程序面临着前所未有的挑战,其中最为显著的就是高并发访问的需求。随着用户数量的激增和业务规模的扩大,如何确保应用在高并发场景下依然能够稳定运行、快速响应,成为了所有开发者和技术团队必须面对的重要课题。 Kubernetes(K8s),作为
低代码如何借助 K8s 实现高并发支持? 低代码如何借助 K8s 实现高并发支持? 低代码如何借助 K8s 实现高并发支持?
吃透 JVM 诊断方法与工具使用
JVM(Java虚拟机)是Java程序运行的基础环境,它提供了内存管理、线程管理和性能监控等功能。吃透JVM诊断方法,可以帮助开发者更有效地解决Java应用在运行时遇到的问题。以下是一些常见的JVM诊断方法: 使用JConsole: JConsole是一个可视化监控工具,可以连接到本地或远程的JVM
推荐一款基于人工智能驱动的无代码自动化测试平台!
今天给大家推荐一款基于人工智能驱动的无代码自动化测试平台:testRigor! 1、testRigor介绍 简单来说,testRigor是一款基于人工智能驱动的无代码自动化测试平台,它能够通过分析应用的行为模式,智能地生成测试用例,并自动执行这些测试,无需人工编写测试脚本。可以用于Web、移动、AP
推荐一款基于人工智能驱动的无代码自动化测试平台! 推荐一款基于人工智能驱动的无代码自动化测试平台! 推荐一款基于人工智能驱动的无代码自动化测试平台!
SemanticKernel/C#:检索增强生成(RAG)简易实践
检索增强生成(RAG)是什么? RAG是“Reference-based Generative model with Attention”的缩写,也可以被称为“Retrieval-Augmented Generation”,是一种结合了检索技术和生成模型的方法,主要用于自然语言处理任务,如文本生成、
SemanticKernel/C#:检索增强生成(RAG)简易实践 SemanticKernel/C#:检索增强生成(RAG)简易实践 SemanticKernel/C#:检索增强生成(RAG)简易实践
02.计算器存储器的原理
02.计算器存储器的原理 目录介绍 01.什么是存储器 1.1 了解存储器是什么 1.2 存储器类型 02.存储器系统设计 2.1 存储器分层设计 2.2 存储器层次结构 2.3 高速缓存设计思想 2.4 虚拟内存访问内存 03.存储器类型 3.1 按照材质划分 3.2 按芯片类型划分 3.3 内存
02.计算器存储器的原理
.NET 结果与错误处理利器 FluentResults
前言 在项目开发中,方法返回的结果(成功或失败)对我们开发来说很重要。传统方法,如通过异常来指示错误或使用特定的返回类型(如布尔值加输出参数),虽然有效,但可能缺乏直观性和灵活性。 FluentResults库应运而生,它以一种既流畅又富有表达力的方式,极大地优化了这一过程。通过使用FluentRe
.NET 结果与错误处理利器 FluentResults .NET 结果与错误处理利器 FluentResults .NET 结果与错误处理利器 FluentResults
自从用了这些监控工具,我连续几天没睡好觉!
大家好,我是程序员鱼皮,今天分享一些很实用的系统监控告警工具。 为什么要用监控告警? 说到监控告警,没有企业开发经验的同学非常容易忽视它,甚至会有同学觉得没有必要,大不了出了 Bug 再修就是了。 这种想法大错特错! 我们把系统想象成人的身体。有的时候,一个人表面看起来可能很健康,但可能只是没有机会
自从用了这些监控工具,我连续几天没睡好觉! 自从用了这些监控工具,我连续几天没睡好觉! 自从用了这些监控工具,我连续几天没睡好觉!