最近项目中要实现一个需求:
用户希望在上传文件时能够直接截图上传,并且要求能够截图浏览器外的内容。
多方查找之下找到了类似的解决方案,但个人感觉操作上有些抽象,仅供参考。
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>
