canvas实现截图功能

news/2024/11/10 21:29:06/文章来源:https://www.cnblogs.com/IwishIcould/p/18328952

开篇

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

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

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

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

<div><input  type="file" id="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.readAsDataURLreader.readAsDataURL(file);  } </script>

将图片的宽高赋值给canvas

我们在获取图片的宽和高之后然后赋值给canvas。
并且将canvas给显示出来就行。
这一步比较简单
<style>.canvas-box{border: 1px solid red;display: none;}
</style>
<canvas id="canvas-node" class="canvas-box"></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 = widthcanvasNode.height = heightcanvasNode.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.readAsDataURLreader.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.offsetXlet rectEndY = e.offsetY// 得到矩形的宽度和高度let rectWidth = rectEndX - currentPoint.xlet rectHeight = rectEndY - currentPoint.ylet {width, height} = canvasNodectx.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" id="file" accept="image/*" /></div><canvas id="canvas-node" class="canvas-box"></canvas><!-- 截图区域的图像显示在下面这个新的画布上 --><div class="canvas2-box"><canvas id="canvas2"></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 id="downBtn">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" id="file" accept="image/*" /></div><canvas id="canvas-node" class="canvas-box"></canvas><button id="downBtn">down</button><div class="canvas2-box"><canvas id="canvas2"></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.offsetXlet rectEndY = e.offsetY// 得到矩形的宽度和高度let rectWidth = rectEndX - currentPoint.xlet rectHeight = rectEndY - currentPoint.ylet {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.typegetImageWH(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.readAsDataURLreader.readAsDataURL(file);  } function canvasSetWH(canvasNode,width, height){canvasNode.width = widthcanvasNode.height = heightcanvasNode.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>

尾声

终于写完了,这周过得很充实。。。
如果觉得写的不错,求未来的老板们点个赞,感谢!

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BI 工具助力企业解锁数字化工厂,开启工业智能新视界

背景 在 2022 年公布的《“十四五”数字经济发展规划》中,政府不断增加对制造业数字化转型的政策支持力度,积极倡导制造企业采用最新技术,提升自动化、数字化和智能化水平。这一举措旨在强化国际竞争力,推动制造业由制造大国向制造强国转变。 然而,在制造业数字化和智能化…

接口依赖处理(Jemeter添加JSON提取器)

操作 1、在对应请求下添加JSON提取器 2、设置想要提取的参数的参数名以及路径 3、将提取到的参数值关联到其他接口 语法格式 1、提取路径格式:$(代表最外层的花括号).K.K 2、关联到其他接口:${K}

内网穿透公众号开本地nginx发环境配置(花生壳和量子互联)

昨天搞了一天,这儿总结一下,免得以后忘了 nginx收入80端口 所以穿透的工具局域网设80 其它端口根据软件应用配置 花生壳的通道要选https 量子互联的话tcp 配置启动https 就可以 nginx 我用1.24没有能成功 1.20成功了,不知道啥 原因 nginx配置时注意全文,都 …

测试采样率变化对音频的影响

SRC(Sample Rate Convert,采样率转换)是hifi发烧友老生常谈的一个问题。为了让多个来源的音频可以同时输出给设备,操作系统会将全部的音频转换为同一采样率,比如Android设备会将音频采样率统一转换为48kHz。这篇文章测试了不同安卓设备和版本之间的音频重采样:2021年Andr…

推荐一款基于人工智能驱动的无代码自动化测试平台!

今天给大家推荐一款基于人工智能驱动的无代码自动化测试平台:testRigor! 1、testRigor介绍 简单来说,testRigor是一款基于人工智能驱动的无代码自动化测试平台,它能够通过分析应用的行为模式,智能地生成测试用例,并自动执行这些测试,无需人工编写测试脚本。可以用于Web、…

获取安卓中加密数据库的密钥

带大家过一遍如何分析常见java层的数据库加密密钥确定数据库是否加密,找到数据库路径,对于有度这个样本,其数据库位于/data/data/im.xinda.youdu/files/youdu/db/路径下,该路径会存在1个或多个文件夹,用于存储不同用户的数据,其格式为buin_用户uin_user_用户gid,其中uin…

C#/.NET/.NET Core优秀项目和框架2024年7月简报

前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项…

Linux 端口排查小结

检查网络端口排查思路:首先进行ping测试,如果ping测试不同检查ICMP协议是否开通和防火墙设置。 如果ping测试通的话 3.telnet ip 端口 ,检查是否可以测通,如果测不通,首先进入服务器检查监听端口和对应开放的IP: netstat -tunlp |grep 224、如果还不通 检查防火墙 和 i…

破案了

看来以后发犇犇要小心点了,不然就被学弟翻到了😰

2024.8.1 鲜花

QQQwwwQQQRe:End of a dream鞅的停时定理 感觉学起来还挺简单的,就是有太灵活逆天的式子。 这里不放鞅的定义了,可以看 百度百科 这里指的是连续鞅。 停时定理: 若满足一下三个条件之一: \[P\{ T < \infty \}=1\\ \]\[E[|M_T|]<\infty\\ \]\[\lim_{n\to \infty} E[|M…

Golang语言文件操作快速入门篇

作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任。 目录一.文件的读取操作1.什么是文件2.IO流类型概述3.文件的基本操作3.1 打开和关闭文件3.2 带缓冲大小读取文件内容3.3 循环读取4.ioutil包一次性读取小文件4.1 ioutil包概述4.2 ioutil读取整个文件5.bufio包…

GO语言文件操作快速入门篇

作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任。 目录一.文件的读取操作1.什么是文件2.IO流类型概述3.文件的基本操作3.1 打开和关闭文件3.2 带缓冲大小读取文件内容3.3 循环读取4.ioutil包一次性读取小文件4.1 ioutil包概述4.2 ioutil读取整个文件5.bufio包…