Progress Ring
介绍
纯 CSS 环形进度:两个半环用 rotate 拼出任意角度,中间数字同步递增。点击随机设定新目标。
纯 HTML、CSS 与少量原生 JS,没有框架依赖。复制提示词交给 Claude Code、Cursor、Trae 或 Codex,模型会把 Progress Ring 加进你的项目并换成你的配色。
适用场景
数据亮点、增长指标、计数器和统计卡片。
安装
订阅 Builder 后,用 8pt CLI 装进项目,或在已接入 8pt MCP 的编辑器里直接调用。
$ npm i -g @8pt-dev/cli && 8pt add progress-ring
mcp › 使用 8pt MCP 调用 get_component("progress-ring")
代码
Progress Ring 的源码,动效只用 transform 与 opacity,并尊重减少动态效果设置。
<div class="x-ring">
<button type="button" class="x-ring__dial" aria-label="设置新进度">
<span class="x-ring__half x-ring__half--r"><i></i></span>
<span class="x-ring__half x-ring__half--l"><i></i></span>
<span class="x-ring__face"><b>0</b><small>%</small></span>
</button>
<div class="x-ring__meta"><b>存储空间</b><span>已用 <em>0</em> / 100 GB</span></div>
</div>.x-ring{display:flex;align-items:center;gap:20px;color:var(--d-fg,#fff)}
.x-ring__dial{--sz:132px;--th:12px;position:relative;width:var(--sz);height:var(--sz);padding:0;border:0;border-radius:50%;background:var(--d-line,rgba(255,255,255,.12));cursor:pointer;flex:none}
.x-ring__half{position:absolute;top:0;width:50%;height:100%;overflow:hidden}
.x-ring__half--r{left:50%}
.x-ring__half--l{left:0}
.x-ring__half i{position:absolute;top:0;width:200%;height:100%;border-radius:50%;transform:rotate(0);transition:transform .55s linear}
.x-ring__half--r i{left:-100%;background:linear-gradient(90deg,var(--d-accent,#4D74FF) 50%,transparent 50%)}
.x-ring__half--l i{left:0;background:linear-gradient(90deg,transparent 50%,color-mix(in srgb,var(--d-accent,#4D74FF) 70%,#9ef0ff) 50%)}
.x-ring__face{position:absolute;inset:var(--th);display:flex;align-items:center;justify-content:center;border-radius:50%;background:var(--d-bg,#09090d)}
.x-ring__face b{font:700 34px/1 var(--d-font,system-ui,sans-serif);letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.x-ring__face small{margin-left:2px;font:600 14px/1 var(--d-font,system-ui,sans-serif);color:var(--d-muted,#9b9ba8);transform:translateY(6px)}
.x-ring__meta{display:grid;gap:6px}
…const init = (root) => {
const dial = root.querySelector('.x-ring__dial'), r = dial.querySelector('.x-ring__half--r i'), l = dial.querySelector('.x-ring__half--l i');
const num = dial.querySelector('b'), used = root.querySelector('.x-ring__meta em');
let cur = 0, raf = 0, tl;
const set = (p) => {
const deg = p * 3.6, from = cur, fdeg = from * 3.6; cur = p;
// right half sweeps 0-180deg, left half 180-360deg; when crossing 180 the halves run one after the other
const rd = Math.min(deg, 180), ld = Math.max(deg - 180, 0);
clearTimeout(tl); r.style.transitionDelay = l.style.transitionDelay = '0s';
if (deg > 180 && fdeg < 180) { r.style.transform = `rotate(${rd}deg)`; l.style.transitionDelay = '.55s'; l.style.transform = `rotate(${ld}deg)`; }
else if (deg < 180 && fdeg > 180) { l.style.transform = `rotate(${ld}deg)`; r.style.transitionDelay = '.55s'; r.style.transform = `rotate(${rd}deg)`; }
else { r.style.transform = `rotate(${rd}deg)`; l.style.transform = `rotate(${ld}deg)`; }
…订阅 Builder 查看完整源码。查看价格