Canvas幸运大转盘如何精准计算扇区图片的宽高与绘制位置
幸运大转盘扇区图片精准绘制方案
原有实现的核心问题有两点:一是采用逐像素循环迭代的方式调整尺寸,边界判断逻辑容易出现误差;二是计算扇区可用宽度时使用了近似弧长公式,没有基于几何关系直接推导精准值,且没有利用「扇区越靠近圆心越窄,仅需校验图片最靠近圆心的左边缘是否溢出侧边」的特性,导致计算冗余且结果不准。
前置坐标系说明
现有代码里的坐标系变换逻辑是正确的:
- 先将画布原点平移到转盘圆心
- 再将坐标系旋转到当前扇区的中心角方向
此时当前扇区沿x轴正方向对称分布,单个扇区的夹角为arc = 2 * Math.PI / sectors.length,扇区两条侧边的方程为y = ±x * Math.tan(arc/2)。
核心约束规则
图片要完全落在扇区内,只需要满足两个约束:
- 径向约束:图片最右端不超过转盘外边缘(预留留白),最左端不小于设定的内圈半径
- 侧边约束:扇区越靠近圆心越窄,只要图片最靠左的边缘位置(离圆心最近的边)的半高不超过该位置扇区的半弦长(加留白),整张图就不会溢出扇区侧边
基于两个约束可以直接通过方程解出图片的最大可用宽高,不需要循环迭代。
修正后的代码
首先提前抽离可配置参数,方便后续调整视觉效果:
// 绘制配置,可根据实际需求调整数值 const DRAW_CONFIG = { innerRadius: 55, // 扇区内圈半径,图片不越过该位置靠近圆心 outerPadding: 15, // 图片距离转盘外边缘的留白 sidePadding: 4 // 图片距离扇区两侧边的留白 };
替换原有drawSector函数中图片绘制分支的全部逻辑:
else { const div = document.createElement('div'); div.innerHTML = sector.label; const img = div.querySelector('img'); // 图片未加载完成时等待加载后重绘,避免拿到0宽高导致绘制失败 if (!img.complete) { img.onload = () => drawSector(sector, i); return; } // 用图片原始宽高计算比例,避免CSS渲染尺寸干扰比例准确性 const imgRatio = img.naturalWidth / img.naturalHeight; const halfSectorAngle = arc / 2; const tanHalfAngle = Math.tan(halfSectorAngle); const outerEdgeX = rad - DRAW_CONFIG.outerPadding; // 图片允许的最右x坐标 // 分别计算两个约束下的最大允许宽度 // 1. 径向约束:图片总宽度不能超过内外圈之间的可用长度 const maxWByRadial = outerEdgeX - DRAW_CONFIG.innerRadius; // 2. 侧边约束:基于左边缘不溢出侧边的规则解方程得到最大宽度 const maxWBySide = (outerEdgeX * tanHalfAngle - DRAW_CONFIG.sidePadding) / (tanHalfAngle + 1 / (2 * imgRatio)); // 取两个约束下的更小值作为最终绘制宽度,保证不越界 const finalWidth = Math.min(maxWByRadial, maxWBySide); const finalHeight = finalWidth / imgRatio; // 计算绘制坐标:右边缘对齐外沿留白位置,垂直方向居中 const drawX = outerEdgeX - finalWidth; const drawY = -finalHeight / 2; ctx.drawImage(img, drawX, drawY, finalWidth, finalHeight); }
效果对比
原有问题效果

预期实现效果

内容的提问来源于stack exchange,提问作者Duc Manh Nguyen
相关产品推荐
相关产品推荐

