如何在HTML Canvas中实现类似SVG viewBox的相对坐标系?
实现Canvas类似SVG viewBox的自适应坐标系
核心思路
通过Canvas 2D上下文的translate(平移)和scale(缩放)方法,手动将Canvas的像素坐标系映射到自定义坐标系(比如0-100),实现和SVG viewBox完全一致的自适应效果。
具体步骤
1. 同步Canvas的显示与绘图尺寸
Canvas有两个关键尺寸:CSS尺寸控制页面显示大小,自身width/height属性控制绘图像素精度。只设置CSS尺寸会导致绘图模糊,因此需要同步两者,窗口大小变化时也要更新:
const canvas = document.getElementById('myCanvas'); const container = canvas.parentElement; function updateCanvasSize() { const dpr = window.devicePixelRatio || 1; // 适配高清屏 const w = container.clientWidth; const h = container.clientHeight; // 设置绘图像素尺寸(乘以设备像素比) canvas.width = w * dpr; canvas.height = h * dpr; // 设置页面显示尺寸 canvas.style.width = `${w}px`; canvas.style.height = `${h}px`; }
2. 模拟viewBox的坐标系变换
假设要实现viewBox="0 0 100 100"的效果,先定义自定义坐标系范围,再计算缩放比例和偏移量(默认模拟SVG的xMidYMid meet模式:居中且完整显示):
function setupViewBox(ctx, vbX, vbY, vbWidth, vbHeight) { const dpr = window.devicePixelRatio || 1; const canvasWidth = canvas.width / dpr; // 转回实际显示宽度 const canvasHeight = canvas.height / dpr; // 计算缩放比例 const scaleX = canvasWidth / vbWidth; const scaleY = canvasHeight / vbHeight; const scale = Math.min(scaleX, scaleY); // 取较小比例保证完整显示 // 计算居中偏移量 const offsetX = (canvasWidth - vbWidth * scale) / 2; const offsetY = (canvasHeight - vbHeight * scale) / 2; ctx.save(); // 保存当前上下文状态,避免影响后续绘图 // 应用变换:先平移到偏移位置,再缩放 ctx.translate(offsetX * dpr, offsetY * dpr); ctx.scale(scale * dpr, scale * dpr); // 若viewBox起始坐标不是(0,0),额外平移对应距离 ctx.translate(-vbX, -vbY); }
3. 使用自定义坐标系绘图
设置好变换后,就可以用自定义坐标系绘图,无需考虑Canvas实际尺寸:
function draw() { updateCanvasSize(); const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); // 启用viewBox:起始坐标(0,0),宽高100x100 setupViewBox(ctx, 0, 0, 100, 100); // 在自定义坐标系中心绘制矩形(始终处于Canvas视觉中心) ctx.fillStyle = 'blue'; ctx.fillRect(40, 40, 20, 20); ctx.restore(); // 恢复上下文状态 } // 初始化绘图+监听窗口 resize draw(); window.addEventListener('resize', draw);
不同适配模式调整
- 拉伸填充(类似SVG的
preserveAspectRatio="none"):直接用scaleX和scaleY分别缩放,无需取最小值和计算偏移:ctx.translate(0, 0); ctx.scale(scaleX * dpr, scaleY * dpr); - 裁剪显示(类似SVG的
xMidYMid slice模式):取Math.max(scaleX, scaleY)作为缩放比例,偏移量计算不变,会裁剪超出Canvas的部分以填满容器。
内容的提问来源于stack exchange,提问作者JackieChilesTheSecond
相关产品推荐
相关产品推荐

