用JavaScript实现浏览器截图功能的全过程

 更新时间:2025年07月22日 10:33:56   作者:ZCKKKing  
在Web开发中实现网页截图功能可以帮助我们保存网页内容、生成海报、制作截图分享等,这篇文章主要介绍了用JavaScript实现浏览器截图功能的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

最近项目中要实现一个需求:
 

用户希望在上传文件时能够直接截图上传,并且要求能够截图浏览器外的内容。

多方查找之下找到了类似的解决方案,但个人感觉操作上有些抽象,仅供参考。

HTML部分

  • 截图开始的按钮
    <button id="start-screenshot" class="button">开始截图</button>
    
  • 这里是canvas部分
    <div id="screenshot-container">
           <canvas id="screenshot-canvas"></canvas>
           <div class="selection-area" id="selection-area" style="display: none;"></div>
           <div class="toolbar" id="screenshot-toolbar" style="display: none;">
               <button id="confirm-screenshot">确认</button>
               <button id="cancel-screenshot">取消</button>
           </div>
    </div>
    

CSS部分

  • 根据需求自行调整就好
     #screenshot-container {
         display: none;
         position: fixed;
         top: 0;
         left: 0;
         width: 100%;
         height: 100%;
         z-index: 9999;
     }
    
     #screenshot-canvas {
         position: absolute;
         top: 0;
         left: 0;
         cursor: crosshair;
     }
    
     .selection-area {
         position: absolute;
         border: 2px dashed #12B7F5;
         background-color: rgba(18, 183, 245, 0.1);
         pointer-events: none;
     }
    
     .toolbar {
         position: absolute;
         background-color: white;
         border-radius: 4px;
         box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
         padding: 5px;
         display: flex;
         gap: 5px;
     }
    
     .toolbar button {
         background-color: #12B7F5;
         color: white;
         border: none;
         padding: 5px 10px;
         border-radius: 4px;
         cursor: pointer;
         font-size: 14px;
     }
    
     .toolbar button:hover {
         background-color: #0E9AD7;
     }
    

JS部分(原生JS为例)

  • 获取dom元素

    const startButton = document.getElementById('start-screenshot'); // 开始截图按钮
    const screenshotContainer = document.getElementById('screenshot-container'); // 获取的图片展示区
    const canvas = document.getElementById('screenshot-canvas'); // canvas
    const ctx = canvas.getContext('2d');
    const selectionArea = document.getElementById('selection-area'); // 截图区域
    const toolbar = document.getElementById('screenshot-toolbar'); // 截图时右下角的小弹框
    const confirmButton = document.getElementById('confirm-screenshot'); // 确认按钮
    const cancelButton = document.getElementById('cancel-screenshot'); // 取消按钮
    
  • 变量定义

    let isCapturing = false;
    let isSelecting = false;
    let startX = 0;
    let startY = 0;
    let endX = 0;
    let endY = 0;
    let screenCapture = null;
    
  • 设置画布大小

    function setCanvasSize() {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
    }
    
  • 开始截图方法

    async function startScreenshot() {
       try {
           // 请求屏幕捕获
           screenCapture = await navigator.mediaDevices.getDisplayMedia({
               video: {
                   cursor: 'always'
               }
           });
    
           // 获取视频轨道
           const videoTrack = screenCapture.getVideoTracks()[0];
           
           // 创建视频元素以捕获屏幕
           const videoElem = document.createElement('video');
           videoElem.srcObject = screenCapture;
           
           // 当视频加载完成后,绘制到画布上
           videoElem.onloadedmetadata = () => {
               videoElem.play();
               
               // 设置画布大小
               setCanvasSize();
               
               // 绘制视频帧到画布
               ctx.drawImage(videoElem, 0, 0, canvas.width, canvas.height);
               
               // 停止视频轨道
               videoTrack.stop();
               
               // 显示截图容器
               screenshotContainer.style.display = 'block';
               isCapturing = true;
           };
       } catch (err) {
           console.error('截图失败:', err);
           alert('截图失败,请确保您已授予屏幕捕获权限。');
       }
    }
    

    这里会请求屏幕捕获权限并获取屏幕内容,这里可以选择 浏览器标签页windows打开的窗口整个屏幕,确实可以获取到浏览器之外的内容。

  • 更新选择区域

    function updateSelectionArea() {
          const width = Math.abs(endX - startX);
          const height = Math.abs(endY - startY);
          const left = Math.min(startX, endX);
          const top = Math.min(startY, endY);
    
          selectionArea.style.display = 'block';
          selectionArea.style.left = left + 'px';
          selectionArea.style.top = top + 'px';
          selectionArea.style.width = width + 'px';
          selectionArea.style.height = height + 'px';
    
          // 更新工具栏位置
          toolbar.style.display = 'flex';
          toolbar.style.left = (left + width + 5) + 'px';
          toolbar.style.top = (top + height + 5) + 'px';
    }
    
  • 确认截图(在这里获取截图结果)

    function confirmScreenshot() {
        if (!isCapturing) return;
    
        const width = Math.abs(endX - startX);
        const height = Math.abs(endY - startY);
        const left = Math.min(startX, endX);
        const top = Math.min(startY, endY);
    
        // 创建新画布以保存选定区域
        const resultCanvas = document.createElement('canvas');
        resultCanvas.width = width;
        resultCanvas.height = height;
        const resultCtx = resultCanvas.getContext('2d');
    
        // 将选定区域绘制到新画布
        resultCtx.drawImage(
            canvas,
            left, top, width, height,
            0, 0, width, height
        );
    
        // 在这里获取截图结果
        // 如果想生成成一个Base64url
        const base64Url = resultCanvas.toDataURL();
    	
    	// 如果想生成成一个File对象
    	const resultFile = dataURLtoFile(resultCanvas.toDataURL(), "截图.png")
    	
        // 重置截图状态
        resetScreenshot();
    }
    
  • 将Base64数据转换为File对象(不需要转换结果为文件对象可以不写这段)

    function dataURLtoFile(dataurl, filename) {
        // 将Base64数据拆分为MIME类型和实际数据
        const arr = dataurl.split(',');
        const mime = arr[0].match(/:(.*?);/)[1]; // 获取MIME类型
        const bstr = atob(arr[1]); // 解码Base64数据
        let n = bstr.length;
        const u8arr = new Uint8Array(n);
    
        // 将解码后的数据转换为Uint8Array
        while (n--) {
            u8arr[n] = bstr.charCodeAt(n);
        }
    
        // 创建并返回File对象
        return new File([u8arr], filename, { type: mime });
    }
    
  • 取消截图

    function cancelScreenshot() {
        resetScreenshot();
    }
    
  • 重置截图状态

    function resetScreenshot() {
       isCapturing = false;
       isSelecting = false;
       selectionArea.style.display = 'none';
       toolbar.style.display = 'none';
       screenshotContainer.style.display = 'none';
       ctx.clearRect(0, 0, canvas.width, canvas.height);
    }
    
  • 各类监听事件

    // 事件监听器
    startButton.addEventListener('click', startScreenshot);
    confirmButton.addEventListener('click', confirmScreenshot);
    cancelButton.addEventListener('click', cancelScreenshot);
    
    // 鼠标事件处理
    canvas.addEventListener('mousedown', function(e) {
        if (!isCapturing) return;
    
        isSelecting = true;
        startX = e.clientX;
        startY = e.clientY;
        endX = e.clientX;
        endY = e.clientY;
        updateSelectionArea();
    });
    
    canvas.addEventListener('mousemove', function(e) {
        if (!isSelecting) return;
    
        endX = e.clientX;
        endY = e.clientY;
        updateSelectionArea();
    });
    
    canvas.addEventListener('mouseup', function() {
        isSelecting = false;
    });
    
    // 窗口大小改变时重新设置画布大小
    window.addEventListener('resize', function() {
        if (isCapturing) {
            setCanvasSize();
        }
    });
    
  • 键盘快捷键(不需要可以不用)

    // 键盘快捷键
    document.addEventListener('keydown', function(e) {
        // Alt + A 开始截图
        if (e.altKey && e.key === 'a') {
            e.preventDefault();
            startScreenshot();
        }
        
        // Enter 确认截图
        if (e.key === 'Enter' && isCapturing) {
            confirmScreenshot();
        }
        
        // Esc 取消截图
        if (e.key === 'Escape' && isCapturing) {
            cancelScreenshot();
        }
    });
    

完整代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>截图</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            font-family: 'Microsoft YaHei', sans-serif;
            background-color: #f5f5f5;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
            overflow: hidden;
        }

        .container {
            text-align: center;
            background-color: white;
            padding: 30px;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            max-width: 800px;
            width: 100%;
        }

        .button {
            background-color: #12B7F5;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            transition: background-color 0.3s;
            margin-top: 10px;
        }

        .button:hover {
            background-color: #0E9AD7;
        }

        #screenshot-container {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: 9999;
        }

        #screenshot-canvas {
            position: absolute;
            top: 0;
            left: 0;
            cursor: crosshair;
        }

        .selection-area {
            position: absolute;
            border: 2px dashed #12B7F5;
            background-color: rgba(18, 183, 245, 0.1);
            pointer-events: none;
        }

        .toolbar {
            position: absolute;
            background-color: white;
            border-radius: 4px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
            padding: 5px;
            display: flex;
            gap: 5px;
        }

        .toolbar button {
            background-color: #12B7F5;
            color: white;
            border: none;
            padding: 5px 10px;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
        }

        .toolbar button:hover {
            background-color: #0E9AD7;
        }
    </style>
</head>
<body>
    <div class="container">
        <button id="start-screenshot" class="button">开始截图</button>
    </div>

    <div id="screenshot-container">
        <canvas id="screenshot-canvas"></canvas>
        <div class="selection-area" id="selection-area" style="display: none;"></div>
        <div class="toolbar" id="screenshot-toolbar" style="display: none;">
            <button id="confirm-screenshot">确认</button>
            <button id="cancel-screenshot">取消</button>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 元素引用
            const startButton = document.getElementById('start-screenshot');
            const screenshotContainer = document.getElementById('screenshot-container');
            const canvas = document.getElementById('screenshot-canvas');
            const ctx = canvas.getContext('2d');
            const selectionArea = document.getElementById('selection-area');
            const toolbar = document.getElementById('screenshot-toolbar');
            const confirmButton = document.getElementById('confirm-screenshot');
            const cancelButton = document.getElementById('cancel-screenshot');

            // 截图状态
            let isCapturing = false;
            let isSelecting = false;
            let startX = 0;
            let startY = 0;
            let endX = 0;
            let endY = 0;
            let screenCapture = null;

            // 设置画布大小
            function setCanvasSize() {
                canvas.width = window.innerWidth;
                canvas.height = window.innerHeight;
            }

            // 开始截图
            async function startScreenshot() {
                try {
                    // 请求屏幕捕获
                    screenCapture = await navigator.mediaDevices.getDisplayMedia({
                        video: {
                            cursor: 'always'
                        }
                    });

                    // 获取视频轨道
                    const videoTrack = screenCapture.getVideoTracks()[0];
                    
                    // 创建视频元素以捕获屏幕
                    const videoElem = document.createElement('video');
                    videoElem.srcObject = screenCapture;
                    
                    // 当视频加载完成后,绘制到画布上
                    videoElem.onloadedmetadata = () => {
                        videoElem.play();
                        
                        // 设置画布大小
                        setCanvasSize();
                        
                        // 绘制视频帧到画布
                        ctx.drawImage(videoElem, 0, 0, canvas.width, canvas.height);
                        
                        // 停止视频轨道
                        videoTrack.stop();
                        
                        // 显示截图容器
                        screenshotContainer.style.display = 'block';
                        isCapturing = true;
                    };
                } catch (err) {
                    console.error('截图失败:', err);
                    alert('截图失败,请确保您已授予屏幕捕获权限。');
                }
            }

            // 更新选择区域
            function updateSelectionArea() {
                const width = Math.abs(endX - startX);
                const height = Math.abs(endY - startY);
                const left = Math.min(startX, endX);
                const top = Math.min(startY, endY);

                selectionArea.style.display = 'block';
                selectionArea.style.left = left + 'px';
                selectionArea.style.top = top + 'px';
                selectionArea.style.width = width + 'px';
                selectionArea.style.height = height + 'px';

                // 更新工具栏位置
                toolbar.style.display = 'flex';
                toolbar.style.left = (left + width + 5) + 'px';
                toolbar.style.top = (top + height + 5) + 'px';
            }

            // 将Base64数据转换为File对象
            function dataURLtoFile(dataurl, filename) {
                // 将Base64数据拆分为MIME类型和实际数据
                const arr = dataurl.split(',');
                const mime = arr[0].match(/:(.*?);/)[1]; // 获取MIME类型
                const bstr = atob(arr[1]); // 解码Base64数据
                let n = bstr.length;
                const u8arr = new Uint8Array(n);

                // 将解码后的数据转换为Uint8Array
                while (n--) {
                    u8arr[n] = bstr.charCodeAt(n);
                }

                // 创建并返回File对象
                return new File([u8arr], filename, { type: mime });
            }

            // 确认截图
            function confirmScreenshot() {
                if (!isCapturing) return;

                const width = Math.abs(endX - startX);
                const height = Math.abs(endY - startY);
                const left = Math.min(startX, endX);
                const top = Math.min(startY, endY);

                // 创建新画布以保存选定区域
                const resultCanvas = document.createElement('canvas');
                resultCanvas.width = width;
                resultCanvas.height = height;
                const resultCtx = resultCanvas.getContext('2d');

                // 将选定区域绘制到新画布
                resultCtx.drawImage(
                    canvas,
                    left, top, width, height,
                    0, 0, width, height
                );

                // 在这里获取截图结果
                // 如果想生成成一个Base64url
                const base64Url = resultCanvas.toDataURL();
                
                // 如果想生成成一个File对象
                const resultFile = dataURLtoFile(resultCanvas.toDataURL(), "截图.png")

                // 重置截图状态
                resetScreenshot();
            }

            // 取消截图
            function cancelScreenshot() {
                resetScreenshot();
            }

            // 重置截图状态
            function resetScreenshot() {
                isCapturing = false;
                isSelecting = false;
                selectionArea.style.display = 'none';
                toolbar.style.display = 'none';
                screenshotContainer.style.display = 'none';
                ctx.clearRect(0, 0, canvas.width, canvas.height);
            }

            // 事件监听器
            startButton.addEventListener('click', startScreenshot);
            confirmButton.addEventListener('click', confirmScreenshot);
            cancelButton.addEventListener('click', cancelScreenshot);

            // 鼠标事件处理
            canvas.addEventListener('mousedown', function(e) {
                if (!isCapturing) return;

                isSelecting = true;
                startX = e.clientX;
                startY = e.clientY;
                endX = e.clientX;
                endY = e.clientY;
                updateSelectionArea();
            });

            canvas.addEventListener('mousemove', function(e) {
                if (!isSelecting) return;

                endX = e.clientX;
                endY = e.clientY;
                updateSelectionArea();
            });

            canvas.addEventListener('mouseup', function() {
                isSelecting = false;
            });

            // 键盘快捷键
            document.addEventListener('keydown', function(e) {
                // Alt + A 开始截图
                if (e.altKey && e.key === 'a') {
                    e.preventDefault();
                    startScreenshot();
                }
                
                // Enter 确认截图
                if (e.key === 'Enter' && isCapturing) {
                    confirmScreenshot();
                }
                
                // Esc 取消截图
                if (e.key === 'Escape' && isCapturing) {
                    cancelScreenshot();
                }
            });

            // 窗口大小改变时重新设置画布大小
            window.addEventListener('resize', function() {
                if (isCapturing) {
                    setCanvasSize();
                }
            });
        });
    </script>
</body>
</html>

总结

姑且算是实现了这个需求,但是实现效果并不是很理想,只能说仅供参考吧

到此这篇关于用JavaScript实现浏览器截图功能的文章就介绍到这了,更多相关JS浏览器截图功能内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Bootstrap轮播插件中图片变形的终极解决方案 使用jqthumb.js

    Bootstrap轮播插件中图片变形的终极解决方案 使用jqthumb.js

    这篇文章主要介绍了Bootstrap轮播插件中图片变形的终极解决方案,使用jqthumb.js,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • 深入理解JavaScript系列(29):设计模式之装饰者模式详解

    深入理解JavaScript系列(29):设计模式之装饰者模式详解

    这篇文章主要介绍了深入理解JavaScript系列(29):设计模式之装饰者模式详解,装饰者用用于包装同接口的对象,不仅允许你向方法添加行为,而且还可以将方法设置成原始对象调用(例如装饰者的构造函数),需要的朋友可以参考下
    2015-03-03
  • js实现简单的可切换选项卡效果

    js实现简单的可切换选项卡效果

    这篇文章主要介绍了js实现简单的可切换选项卡效果的方法,涉及javascript操作css样式实现切换选项卡的技巧,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • 不使用XMLHttpRequest实现异步加载 Iframe和script

    不使用XMLHttpRequest实现异步加载 Iframe和script

    运用Iframe和script可以实现简单的异步加载,没有使用XMLHttpRequest,需要的朋友可以参考下
    2012-10-10
  • 如何确保JavaScript的执行顺序 之实战篇

    如何确保JavaScript的执行顺序 之实战篇

    我曾在文章《如何在多个页面使用同一个HTML片段 - 续》的最后提到JavaScript顺序执行的特性。虽然现代浏览器可以并行的下载JavaScript(部分浏览器),但考虑到JavaScript的依赖关系,他们的执行依然是按照引入顺序进行的。
    2011-03-03
  • HTML+JavaScript实现扫雷小游戏

    HTML+JavaScript实现扫雷小游戏

    这篇文章主要为大家详细介绍了HTML+JavaScript实现扫雷小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • 原生js实现日期联动

    原生js实现日期联动

    日期联动算是一个比较常见的功能了,随便度娘一下,你就能找到N多代码,今天给大家介绍的是个人比较常用,代码很简洁,高效,这里推挤给大家。
    2015-01-01
  • javascript实现手动点赞效果

    javascript实现手动点赞效果

    这篇文章主要为大家详细介绍了javascript实现手动点赞效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • 判定对象是否为window的js代码

    判定对象是否为window的js代码

    这是一个非常有趣的题目。我们先从Object.prototype.toString入手,看能否解决它。
    2010-02-02
  • JS跨域总结

    JS跨域总结

    JS跨域总结,主要是解决js中跨域访问的我问题
    2012-08-08

最新评论