使用context.drawImage转video为canvas时object-fit:cover效果失效问题
解决Canvas绘制Video时
object-fit: cover效果不生效的问题 我之前也踩过这个坑!object-fit: cover是CSS层面的布局样式,Canvas的drawImage()方法根本不会识别这个属性——它只会按照视频原始尺寸或者你传入的固定尺寸直接绘制,完全忽略CSS对video元素的裁剪/缩放处理。要让Canvas复现video的cover显示效果,我们得手动模拟这个逻辑:保持视频宽高比,缩放至完全覆盖Canvas容器,裁剪超出部分。
核心思路
object-fit: cover的本质是:
- 保持视频自身的宽高比不变
- 缩放视频至刚好能完全覆盖目标容器(Canvas)
- 将缩放后的视频居中,裁剪掉超出容器的部分
所以我们需要计算三个关键值:缩放后的视频宽高、视频在Canvas中的偏移量,然后用这些值调用drawImage()。
实现代码
先写一个通用的绘制函数,把计算逻辑封装进去:
function drawVideoWithCover(video, canvas) { const ctx = canvas.getContext('2d'); // 获取视频的原始宽高(不是video元素的CSS宽高!) const videoWidth = video.videoWidth; const videoHeight = video.videoHeight; const canvasWidth = canvas.width; const canvasHeight = canvas.height; // 计算视频和Canvas的宽高比 const videoRatio = videoWidth / videoHeight; const canvasRatio = canvasWidth / canvasHeight; let drawWidth, drawHeight, offsetX, offsetY; // 根据比例差异,确定缩放策略 if (canvasRatio > videoRatio) { // Canvas比视频宽:按Canvas高度缩放视频,裁剪左右两侧 drawHeight = canvasHeight; drawWidth = videoRatio * drawHeight; offsetX = (canvasWidth - drawWidth) / 2; offsetY = 0; } else { // Canvas比视频高:按Canvas宽度缩放视频,裁剪上下两侧 drawWidth = canvasWidth; drawHeight = drawWidth / videoRatio; offsetY = (canvasHeight - drawHeight) / 2; offsetX = 0; } // 清空画布,绘制视频 ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.drawImage(video, offsetX, offsetY, drawWidth, drawHeight); }
怎么使用这个函数
你可以在视频播放时,通过requestAnimationFrame实时绘制画面:
const video = document.getElementById('your-video-element'); const canvas = document.getElementById('your-canvas-element'); // 确保Canvas的像素尺寸和显示尺寸一致(避免拉伸) canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; // 视频开始播放后,实时绘制 video.addEventListener('play', () => { function renderFrame() { drawVideoWithCover(video, canvas); requestAnimationFrame(renderFrame); } renderFrame(); });
关键注意事项
- 必须用
video.videoWidth和video.videoHeight获取视频原始尺寸,而不是video.offsetWidth/offsetHeight——后者是CSS设置的元素尺寸,不是视频本身的比例。 - 要保证Canvas的
width和height属性值等于它的显示尺寸(比如通过offsetWidth/offsetHeight设置),否则绘制的内容会被拉伸变形。
这样处理后,Canvas里的视频画面就和video元素设置object-fit: cover的显示效果完全一致了!
内容的提问来源于stack exchange,提问作者Tyler Hudson
相关产品推荐
相关产品推荐

