翼度科技»论坛 编程开发 html5 查看内容

记录--使用 JS 实现基本的截图功能

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助



思路分析

在开始动手之前,分析一下整个功能的实现过程:

  • 根据图片大小创建 canvas1 画布,并将原图片直接定位在 canvas1 上;
  • 在画布上添加一个蒙层,以区分当前 canvas 图像是被裁剪的原图像;
  • 在蒙层上方,对裁剪区域(鼠标移动形成的矩形范围)再次进行图像绘制;
  • 获取裁剪区域的数据,并将该数据定位到另一个 canvas 画布上。
实现过程

准备工作

首先,编写所需的 HTML 结构并获取对应元素。
  1. <body>
  2.   
  3.   <input type="file" id="imageFile" accept="image/*">
  4.   
  5.   
  6.     <canvas id="canvas1"></canvas>
  7.   
  8.   
  9.   
  10.     <canvas id="canvas2"></canvas>
  11.   
  12. </body>
复制代码
绘制原图像

我们需要监听 input 元素的 change 事件,以获取上传图片的相关参数,这里主要是为了获取图片的宽度和高度。
我们创建一个 FileReader() 对象并监听其 load 事件。load 事件在读取操作成功后立刻执行,在这个方法中我们就可以获取图片的宽高。
  1. function init() {
  2.   imageFile.addEventListener('change', handleFileChange, false); // 监听图片上传事件。
  3. }
  4. function handleFileChange(e) {
  5.   const imgFile = e.target.files[0]; // 获取上传的图片对象。
  6.   const reader = new FileReader();
  7.   reader.onload = function(e) {
  8.     const imgSrc = e.target.result; // 图片文件的 base64 编码格式。
  9.     imageBox.src = imgSrc; // 把图片放入 img 容器。
  10.     // 等图片加载完成后,获取图片的宽高。
  11.     imageBox.onload = function () {
  12.       const imgWidth = this.width, imgHeight = this.height;
  13.       console.log(imgWidth, imgHeight);
  14.     }
  15.   }
  16.   if (imgFile) {
  17.     reader.readAsDataURL(imgFile); // 读取图片文件,读取完成才能获取 result 属性。
  18.   }
  19. }
  20. init();
复制代码

此时还没有图片,我们创建一个自适应图片大小的 canvas1 画布,并使用 drawImage() 方法将上传的图片直接定位到 canvas1 当中。
  1. function handleFileChange(e) {
  2.   const imgFile = e.target.files[0]; // 获取上传的图片对象。
  3.   const reader = new FileReader();
  4.   reader.onload = function (e) {
  5.     const imgSrc = e.target.result; // 图片的 base64 编码。
  6.     imageBox.src = imgSrc; // 把上传的图像放入 img 容器。
  7.     // 图片加载完毕后执行
  8.     imageBox.onload = function () {
  9.       // 获取图片的宽高。
  10.       const imgWidth = this.width, imgHeight = this.height;
  11.       console.log(imgWidth, imgHeight);
  12.       
  13.       // 创建 canvas 画布并绘制图片。
  14.       generateCanvas(canvasContainer1, canvas1, imgWidth, imgHeight);
  15.       ctx.drawImage(imageBox, 0, 0, imgWidth, imgHeight);
  16.     }
  17.   }
  18.   if (imgFile) {
  19.     reader.readAsDataURL(imgFile); // 将当前file读取成DataURL
  20.   }
  21. }
  22. // 根据 width 和 height 创建 canvas 画布。
  23. function generateCanvas(container, canvas, width, height) {
  24.   container.width = width + 'px';
  25.   container.height = height + 'px';
  26.   canvas.width = width;
  27.   canvas.height = height;
  28.   container.style.display = 'block'; // 显示 canvas 区域。
  29. }
复制代码

可以看到原图像已经成功被绘制,接下来就可以开始动态绘制截图区域了。
绘制截图区域

在这个过程中,我们需要分别监听 imageBox 容器(原图像)上的 mousedown、mousemove 和 mouseup 事件,这些事件的作用如下:

  • mousedown 事件:记录开始截图的位置,并开始监听 mousemove 和 mouseup 事件。
  • mousemove 事件:监听鼠标的偏移量,以计算裁剪区域的宽度和高度。
  • mouseup 事件:截图结束,注销监听 mousedown 和 mousemove 事件,并绘制裁剪区域。
  1. let startPosition = []; // 记录鼠标点击(开始截图)的位置。
  2. let screenshotData = []; // 保存截取部分的相关信息。
  3. function init() {
  4.   // 监听鼠标点击事件。
  5.   canvas1.addEventListener('mousedown', handleMouseDown, false);
  6. }
  7. // 记录鼠标点击(开始截图)的位置,并监听相关事件。
  8. function handleMouseDown(e) {
  9.   startPosition = [e.offsetX, e.offsetY];
  10.   canvas1.addEventListener('mousemove', handleMouseMove, false);
  11.   canvas1.addEventListener('mouseup', handleMouseUp, false);
  12. }
  13. // 监听鼠标的偏移量,以计算裁剪区域的宽度和高度。
  14. function handleMouseMove(e) {
  15.   // 获取裁剪区域的宽度和高度。
  16.   const { offsetX, offsetY } = e;
  17.   const [startX, startY] = startPosition;
  18.   const [rectWidth, rectHeight] = [offsetX - startX, offsetY - startY];
  19.   console.log('rect', rectWidth, rectHeight);
  20.   
  21.   // 保存裁剪区域的相关信息。
  22.   screenshotData = [startX, startY, rectWidth, rectHeight];
  23. }
  24. // 注销监听事件等后续操作。
  25. function handleMouseUp() {
  26.   canvas1.removeEventListener('mousemove', handleMouseMove, false);
  27.   canvas1.removeEventListener('mouseup', handleMouseUp, false);
  28. }
复制代码
在 handleMouseMove 函数中,我们已经获取了裁剪区域的宽高,也就是生成截图的宽高。
接下来,我们需要在原图像上展示出我们所裁剪的区域,也就是这个效果:

可以看到,原图像的上方、裁剪区域下方会覆盖一层半透明黑色蒙层,它的作用是区分原图层和裁剪部分图层。所以我们需要在绘制截图区域之前,添加一层蒙层。
注意,在已有内容的 canvas 画布上进行再次绘制之前,需要先清除整个画布的内容。 这里通过 clearRect() 方法清除 canvas1 画布上的所有内容,并添加蒙层。
我们继续来补充 handleMouseMove 和 handleMouseUp 函数中的逻辑:
  1. const MASKER_OPACITY = 0.4;
  2. function handleMouseMove(e) {
  3.   // 获取裁剪区域的宽度和高度。
  4.   const { offsetX, offsetY } = e;
  5.   const [startX, startY] = startPosition;
  6.   const [rectWidth, rectHeight] = [offsetX - startX, offsetY - startY];
  7.   console.log('rect', rectWidth, rectHeight);
  8.   // 保存裁剪区域的相关信息。
  9.   screenshotData = [startX, startY, rectWidth, rectHeight];
  10.   // 再次绘制前,清理 canvas1 画布上的内容。
  11.   const { width, height } = canvas1;
  12.   ctx.clearRect(0, 0, width, height);
  13.   // 在 canvas1 画布上绘制蒙层。
  14.   drawImageMasker(0, 0, width, height, MASKER_OPACITY);
  15.   // 绘制截图区域。
  16.   drawScreenShot(width, height, rectWidth, rectHeight);
  17. }
  18. // ...
  19. // 绘制图片蒙层,填充范围和颜色,以便区分原图层和裁剪部分图层。
  20. function drawImageMasker(x, y, width, height, opacity) {
  21.   ctx.fillStyle = `rgba(0, 0, 0, ${opacity})`;
  22.   ctx.fillRect(0, 0, width, height);
  23. }
  24. // 绘制裁剪的矩形区域。
  25. function drawScreenShot(canWidth, canHeight, rectWidth, rectHeight) {
  26.   // 在源图像外绘制新图像,只有源图像外的目标图像部分会被显示,源图像是透明的。
  27.   ctx.globalCompositeOperation = 'destination-out';
  28.   ctx.fillStyle = '#2c2c2c';
  29.   ctx.fillRect(...startPosition, rectWidth, rectHeight);
  30.   // 在现有画布上绘制新的图形。
  31.   ctx.globalCompositeOperation = 'destination-over';
  32.   ctx.drawImage(imageBox, 0, 0, canWidth, canHeight, 0, 0, canWidth, canHeight);
  33. }
复制代码
然后,当我们放开鼠标(结束截图动作)时,除了注销对 mousedown 和 mousemove 事件的监听,还需要将所得的裁剪区域的图像放入另一个 canvas 中。
在绘制新图像的过程中,我们需要使用以下方法:

  • getImageData():读取 canvas 上的内容,返回一个 ImageData 对象,包含了每个像素的信息。
  • putImageData():将 ImagaData 对象的数据放入 canvas 中,覆盖 canvas 中的已有图像。
  1. function handleMouseUp() {
  2.   canvas1.removeEventListener('mousemove', handleMouseMove, false);
  3.   canvas1.removeEventListener('mouseup', handleMouseUp, false);
  4.   // 开始绘制截图区域图片。
  5.   drawScreenshotImage(screenshotData);
  6.   // 如果裁剪得到新图像后,不希望保留原图像,可以设置以下属性。
  7.   // canvasContainer1.style.display = 'none';
  8. }
  9. // 在新容器 canvas2 上绘制新图像。
  10. function drawScreenshotImage(screenshotData) {
  11.   // 获取 canvas1 的数据。
  12.   const data = ctx.getImageData(...screenshotData);
  13.   // 创建 canvas2 画布。
  14.   generateCanvas(canvasContainer2, canvas2, screenshotData[2], screenshotData[3]);
  15.   // 每次绘制前,都先进行清除操作。
  16.   ctx2.clearRect(...screenshotData);
  17.   // 将 canvas1 的数据放入 canvas2 中。
  18.   ctx2.putImageData(data, 0, 0);
  19. }
复制代码
经过以上步骤,就可以实现我们所需的效果

本文转载于:

https://juejin.cn/post/7264920437242036284

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 


来源:https://www.cnblogs.com/smileZAZ/p/17617611.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具