mask-image 不适合做星级评分展示,因其本质是图像遮罩工具,缺乏交互响应、无障碍支持差,且 Safari 对 SVG mask 支持不稳定;仅静态显示固定分数时可用,但需满足线性渐变遮罩、纯色单层SVG图标、固定容器宽度三个前提。

为什么 mask-image 不是星级评分的推荐方案
直接说结论:mask-image 在大多数实际项目中并不适合做星级评分展示。它本质是图像遮罩工具,依赖位图或渐变作为遮罩源,无法响应鼠标交互(如 hover 半星)、不支持无障碍访问(aria-valuenow 难以绑定)、且 Safari 对 SVG <mask> 的 mask-image 支持不稳定——常见表现为遮罩偏移或完全失效。
如果你只是想静态显示一个固定分数(比如“4.2 星”),且已有一张完整五星图,那 mask-image 确实能用,但代价是牺牲可维护性和语义化。
用 mask-image 实现静态星级的三个硬性前提
真要用,必须满足以下全部条件,否则立刻出问题:
- 遮罩源必须是线性渐变(
linear-gradient),不能用 PNG —— 否则缩放时边缘模糊,且无法精确控制遮罩比例 - 原始星级图标需为纯色单层 SVG(无描边、无透明度、无 group 嵌套),宽高严格一致,否则
mask-image会裁切错位 - 容器宽度必须固定(如
width: 120px),且与图标原始宽度成整数比,否则 4.3 星这种小数会因像素对齐偏差显示成 4 或 5 星
示例代码(仅作验证):
立即学习“前端免费学习笔记(深入)”;
.star-rating {
width: 120px;
height: 24px;
background: url('stars.svg') no-repeat;
mask-image: linear-gradient(90deg, #000 4.3/5, transparent 0);
mask-size: 120px 24px;
mask-repeat: no-repeat;
}
替代方案:用 CSS clip-path + flex 更可靠
如果坚持纯 CSS、拒绝 JS,clip-path 配合五个独立 <span> 是更可控的选择。它天然支持每个星独立 hover、可加 aria-hidden 和 role="img"、在所有现代浏览器中行为一致。
关键点:
- 每个星用
inline-block并设固定宽高,避免 flex 压缩导致 clip 错位 -
clip-path: inset(0 20% 0 0)这类写法比 polygon 更易算半星(20% = 1/5 星) - 不要用
clip-path: url(#svg-clip)—— SVG clipPath 在 Firefox 中有重绘延迟,拖动时会闪
真正该用 JS 的地方:用户交互和状态同步
任何需要点击选星、hover 预览、键盘操作(←→ 方向键)、或与后端分数联动的场景,CSS 遮罩或裁剪都无解。此时应直接用 <input type="range"> 或五个 <button>,通过 JS 绑定 click 和 keydown,再用 class 控制视觉状态。
容易被忽略的一点:iOS Safari 对 mask-image 的 mask-position 解析有 bug,当父容器有 transform: scale() 时,遮罩位置会偏移 2–3 像素,且无法通过 mask-origin 修正。这种细节在响应式布局里几乎必然触发。


















