如何使用HTML5的drawImage()将视频分割为分块播放?
嘿,我帮你搞定这个3×4视频网格的问题!你说drawImage出问题,大概率是踩了这个函数参数顺序或者区域计算的坑——我先给你理清楚核心问题,再给你修正后的完整代码。
你可能踩的drawImage坑
drawImage()的参数顺序特别容易搞混,尤其是源区域和目标区域的位置/尺寸,很多人会把两者的参数写反。正确的参数顺序是:
ctx.drawImage(video, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
sx/sy:视频上要截取的起始坐标sWidth/sHeight:视频上截取的区域宽高dx/dy:canvas上绘制的起始坐标dWidth/dHeight:canvas上绘制的区域宽高
另外,你可能没等视频加载完成就计算宽高,用了默认值导致区域划分错误,或者CSS拉伸了canvas导致显示变形。
修正后的完整代码
这是可以直接运行的版本,实现3×4网格每个区域播放对应视频片段:
<!DOCTYPE html> <html> <head> <title>Lab 5: HTML5</title> <style> .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); /* 3列均分 */ grid-template-rows: repeat(4, 1fr); /* 4行均分 */ gap: 2px; width: 660px; /* 和你原来的box宽度一致 */ margin: 0 auto; } canvas { border: 1px solid #333; width: 100%; height: auto; } video { display: none; /* 隐藏原始视频,只用canvas播放 */ } </style> </head> <body> <video id="sourceVideo" controls> <source src="your-video.mp4" type="video/mp4"> Your browser does not support HTML5 video. </video> <div class="grid-container" id="gridContainer"></div> <script> const video = document.getElementById('sourceVideo'); const gridContainer = document.getElementById('gridContainer'); const cols = 3; const rows = 4; let canvases = []; let ctxs = []; let videoWidth, videoHeight; let cellSourceWidth, cellSourceHeight; // 必须等视频加载元数据后,才能获取真实宽高 video.addEventListener('loadedmetadata', () => { videoWidth = video.videoWidth; videoHeight = video.videoHeight; // 计算每个网格对应的视频源区域尺寸 cellSourceWidth = videoWidth / cols; cellSourceHeight = videoHeight / rows; // 创建3×4的canvas网格 for (let row = 0; row < rows; row++) { for (let col = 0; col < cols; col++) { const canvas = document.createElement('canvas'); // 设置canvas的实际绘图尺寸(和视频源区域一致,避免拉伸) canvas.width = cellSourceWidth; canvas.height = cellSourceHeight; gridContainer.appendChild(canvas); canvases.push(canvas); ctxs.push(canvas.getContext('2d')); } } }); // 视频播放时循环绘制每一帧 video.addEventListener('play', () => { function drawFrames() { if (!video.paused && !video.ended) { // 遍历每个canvas,绘制对应的视频片段 canvases.forEach((canvas, index) => { const row = Math.floor(index / cols); const col = index % cols; const ctx = ctxs[index]; // 关键:正确对应源区域和目标区域 ctx.drawImage( video, col * cellSourceWidth, // 视频上的起始x坐标 row * cellSourceHeight, // 视频上的起始y坐标 cellSourceWidth, // 截取的视频宽度 cellSourceHeight, // 截取的视频高度 0, 0, // canvas上的起始坐标(左上角) canvas.width, canvas.height // canvas上的绘制尺寸 ); }); requestAnimationFrame(drawFrames); } } drawFrames(); }); </script> </body> </html>
关键细节解释
- 等待视频加载:用
loadedmetadata事件确保获取视频真实宽高,避免用默认的300×150计算导致区域错位 - 网格区域计算:每个网格的视频源区域是
列索引×源宽、行索引×源高,确保每个canvas对应视频的正确子区域 - canvas尺寸适配:canvas的
width/height属性设置为源区域尺寸(保证绘图清晰),CSS的width:100%用来适配网格容器大小,避免拉伸变形 - 循环绘制:用
requestAnimationFrame实现流畅的帧绘制,和视频播放同步
额外排查小技巧
- 可以在控制台打印
cellSourceWidth、col * cellSourceWidth这些值,看是否符合预期(比如视频宽600的话,cellSourceWidth应该是200) - 如果用单个大canvas绘制所有网格,只需要修改目标区域的
dx/dy为每个网格在大canvas上的坐标即可
内容的提问来源于stack exchange,提问作者potatoguy
相关产品推荐
相关产品推荐

