关于 elite-performance
面向 Web 开发者的性能优化 Skill,聚焦动画流畅度与 Core Web Vitals。内容涵盖动画实现、资源加载、内存清理和性能排查。
主要功能
- 01Core Web Vitals 与动画目标
- 02适合 GPU 加速的动画属性
- 03GSAP 清理与批处理
- 04加载策略与资源优化
- 05性能调试检查清单
适用场景
- 01检查动画代码中的布局触发属性
- 02规划动画库的加载方式
- 03排查掉帧或内存泄漏
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rshvr/ elite-web-design --skill elite-performance安装后在 Claude Code 中输入/elite-performance确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/animation-performance.mdasset-optimization.mddebugging.mdvite-setup.md详细介绍
极致性能
在达到 Core Web Vitals 目标的同时,保持动画流畅运行在 60fps。
快速参考
| 主题 | 参考文件 |
|---|---|
| Vite 配置 | vite-setup.md |
| 动画性能 | animation-performance.md |
| 资源优化 | asset-optimization.md |
| 调试 | debugging.md |
性能预算(2026)
Core Web Vitals 目标
| 指标 | 良好 | 需改进 | 较差 |
|---|---|---|---|
| LCP(最大内容绘制) | ≤ 2.5s | ≤ 4.0s | > 4.0s |
| INP(交互到下一次绘制) | ≤ 200ms | ≤ 500ms | > 500ms |
| CLS(累积布局偏移) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
动画目标
| 指标 | 目标 |
|---|---|
| 帧率 | 60fps(16.67ms/帧) |
| 帧预算 | JS/布局 < 10ms |
| 动画启动 | 响应时间 < 100ms |
| 滚动卡顿 | 丢帧数为 0 |
Bundle 目标
| 资源 | 目标 |
|---|---|
| 初始 JS | < 100KB(gzip 压缩后) |
| 初始 CSS | < 50KB(gzip 压缩后) |
| GSAP 核心 | 约 25KB(gzip 压缩后) |
| 初始资源总计 | < 200KB(gzip 压缩后) |
GPU 加速属性
仅为这些属性添加动画
/* GPU composited - FAST */
transform: translateX() translateY() translateZ() scale() rotate() skew();
opacity: 0 to 1;
filter: blur() brightness() contrast();
/* Will trigger compositor layer */
will-change: transform, opacity;
绝不要为这些属性添加动画
/* Triggers layout - SLOW */
width, height
top, right, bottom, left
margin, padding
font-size
border-width
/* Triggers paint - SLOW */
background-color, color
border-color
box-shadow
text-shadow
transform 与 position
/* BAD - Triggers layout every frame */
.element {
animation: moveLeft 1s;
}
@keyframes moveLeft {
to {
left: 100px;
}
}
/* GOOD - GPU composited */
.element {
animation: moveLeft 1s;
}
@keyframes moveLeft {
to {
transform: translateX(100px);
}
}
快速提升性能的方法
1. 延迟加载首屏以下的内容
<img src="hero.jpg" alt="Hero" loading="eager" />
<img src="feature.jpg" alt="Feature" loading="lazy" />
<div class="lazy-section" data-component="heavy-animation">
</div>
2. 使用 content-visibility
/* Skip rendering off-screen sections */
.section {
content-visibility: auto;
contain-intrinsic-size: 0 500px; /* Estimated height */
}
3. 限制开销大的效果
/* Isolate animated sections */
.animated-section {
contain: layout style paint;
}
/* Full containment for cards */
.card {
contain: strict;
}
4. 在适当情况下减少动画
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
5. 优化滚动处理程序
// BAD - Fires on every scroll event
window.addEventListener("scroll", handleScroll);
// GOOD - Passive listener, no preventDefault
window.addEventListener("scroll", handleScroll, { passive: true });
// BETTER - Use ScrollTrigger (already optimized)
gsap.to(".element", {
scrollTrigger: {/* ... */},
});
GSAP 性能
使用 gsap.context() 清理资源
// Prevents memory leaks
const ctx = gsap.context(() => {
gsap.to(".element", { x: 100 });
ScrollTrigger.create({/* ... */});
});
// On unmount
ctx.revert();
批量更新 ScrollTrigger
// Process multiple items efficiently
ScrollTrigger.batch(".item", {
onEnter: (batch) =>
gsap.to(batch, {
opacity: 1,
y: 0,
stagger: 0.1,
}),
});
使用 refreshPriority
// Control refresh order
ScrollTrigger.create({
trigger: ".section",
refreshPriority: -1, // Refresh after others
});
延迟创建 ScrollTrigger
// Don't create all at once
const createTrigger = (element) => {
ScrollTrigger.create({
trigger: element,
start: "top 80%",
onEnter: () => {
// Create animation only when needed
gsap.from(element, { opacity: 0, y: 50 });
},
once: true,
});
};
// Create triggers as needed
gsap.utils.toArray(".section").forEach(createTrigger);
CSS 动画性能
高效的关键帧
/* GOOD - Only compositor properties */
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* BAD - Triggers layout */
@keyframes slideIn {
from {
opacity: 0;
margin-top: 30px;
}
to {
opacity: 1;
margin-top: 0;
}
}
will-change 最佳实践
/* Apply just before animation */
.card {
transition:
transform 0.3s,
opacity 0.3s;
}
.card:hover {
will-change: transform;
}
/* Remove after animation */
.card.animating {
will-change: transform, opacity;
}
/* Never do this globally */
/* * { will-change: transform; } NEVER! */
合成图层
/* Force new layer when needed */
.animated-element {
transform: translateZ(0); /* or translate3d(0,0,0) */
}
/* Better: Use will-change temporarily */
.animating {
will-change: transform;
}
加载策略
关键路径
<head>
<style>
/* Above-fold styles */
</style>
<link rel="preload" href="hero.webp" as="image" />
<link rel="preload" href="font.woff2" as="font" crossorigin />
<link
rel="stylesheet"
href="full.css"
media="print"
onload="this.media='all'"
/>
</head>
<body>
<script src="gsap.min.js" defer></script>
<script src="app.js" defer></script>
</body>
动态导入
// Load GSAP plugins only when needed
const loadScrollTrigger = async () => {
const { ScrollTrigger } = await import("gsap/ScrollTrigger");
gsap.registerPlugin(ScrollTrigger);
return ScrollTrigger;
};
// Load on interaction or visibility
const section = document.querySelector(".scroll-section");
const observer = new IntersectionObserver(async ([entry]) => {
if (entry.isIntersecting) {
await loadScrollTrigger();
initScrollAnimations();
observer.disconnect();
}
});
observer.observe(section);
渐进增强
// Check for animation support
const supportsAnimation = "animate" in document.documentElement;
const prefersReducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
if (supportsAnimation && !prefersReducedMotion) {
// Load animation library
import("./animations.js");
} else {
// Show final states immediately
document.querySelectorAll(".animated").forEach((el) => {
el.classList.add("animation-complete");
});
}
内存管理
清理动画
// Store references for cleanup
const animations = [];
const scrollTriggers = [];
function initAnimations() {
animations.push(gsap.to(".element", { x: 100 }));
scrollTriggers.push(ScrollTrigger.create({ trigger: ".section" }));
}
function cleanup() {
animations.forEach((anim) => anim.kill());
scrollTriggers.forEach((st) => st.kill());
animations.length = 0;
scrollTriggers.length = 0;
}
// Or use gsap.context()
const ctx = gsap.context(() => {
// All animations here
});
// Cleanup
ctx.revert();
SplitText 清理
const splits = [];
function initTextAnimations() {
document.querySelectorAll(".split-text").forEach((el) => {
const split = new SplitText(el, { type: "chars" });
splits.push(split);
gsap.from(split.chars, {
opacity: 0,
y: 20,
stagger: 0.02,
});
});
}
function cleanup() {
splits.forEach((split) => split.revert());
splits.length = 0;
}
清理事件监听器
// Use AbortController for easy cleanup
const controller = new AbortController();
window.addEventListener("resize", handleResize, {
signal: controller.signal,
});
window.addEventListener("scroll", handleScroll, {
passive: true,
signal: controller.signal,
});
// Cleanup all at once
function cleanup() {
controller.abort();
}
调试清单
性能问题
-
丢帧?
- 检查 DevTools Performance 面板
- 查找长任务(> 50ms)
- 确认动画仅涉及合成器属性
-
初始加载缓慢?
- 检查 Network 瀑布图
- 确认关键路径已优化
- 检查 Bundle 大小
-
内存泄漏?
- 随时间检查 Memory 面板
- 确认导航时会执行清理
- 留意已分离的 DOM 节点
-
布局抖动?
- 在 Performance 面板中查找强制重排
- 批量执行 DOM 读取/写入
- 使用 transform 代替 position
快速检查
// Log animation frame rate
let lastTime = performance.now();
let frameCount = 0;
function measureFPS() {
frameCount++;
const now = performance.now();
if (now - lastTime >= 1000) {
console.log("FPS:", frameCount);
frameCount = 0;
lastTime = now;
}
requestAnimationFrame(measureFPS);
}
measureFPS();
// Detect layout thrashing
const originalGetComputedStyle = window.getComputedStyle;
window.getComputedStyle = function (...args) {
console.trace("getComputedStyle called");
return originalGetComputedStyle.apply(this, args);
};
如需了解全面的调试技巧,请参阅 debugging.md。
资源
评论
支持 Markdown