Files
argon-theme/GPU_ACCELERATION_USAGE.md
nanhaoluo f165fac420 feat: 实现 GPU 加速管理功能
- 实现 enableGPU() 方法设置 will-change 属性
- 实现 disableGPU() 方法移除 will-change 属性
- 实现动画数量限制逻辑(最多 3 个同时运行)
- 实现动画队列自动管理
- 添加 startAnimation() 和 endAnimation() 方法
- 添加 getActiveAnimationCount() 和 getQueuedAnimationCount() 查询方法
- 添加 clearAllAnimations() 清理方法
- 添加错误处理机制
- 创建交互式测试页面和自动化测试
- 创建详细的使用文档

验证需求:
- 需求 5.2: 动画时使用 will-change 提示浏览器创建合成层
- 需求 5.3: 动画完成时移除 will-change 属性释放资源
- 需求 5.5: 限制同时运行的动画数量不超过 3 个
2026-01-21 23:20:06 +08:00

380 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# GPU 加速管理使用指南
## 概述
`ArgonRenderOptimizer` 类提供了 GPU 加速管理功能,包括:
- 启用/禁用 GPU 加速will-change 属性)
- 限制同时运行的动画数量(最多 3 个)
- 自动管理动画队列
- 动画完成后自动清理资源
## 功能说明
### 1. GPU 加速管理
#### enableGPU(element)
为元素启用 GPU 加速,设置 `will-change: transform, opacity` 属性。
**参数:**
- `element` (Element): 要启用 GPU 加速的 DOM 元素
**示例:**
```javascript
const optimizer = new ArgonRenderOptimizer();
const element = document.querySelector('.animated-element');
// 启用 GPU 加速
optimizer.enableGPU(element);
```
#### disableGPU(element)
为元素禁用 GPU 加速,移除 `will-change` 属性。
**参数:**
- `element` (Element): 要禁用 GPU 加速的 DOM 元素
**示例:**
```javascript
// 禁用 GPU 加速
optimizer.disableGPU(element);
```
### 2. 动画数量限制
系统自动限制同时运行的动画数量不超过 3 个,超出的动画会进入等待队列。
#### startAnimation(element, animationFn)
启动一个动画。如果当前活动动画数量未达到上限3 个),立即启动;否则加入等待队列。
**参数:**
- `element` (Element): 要动画的 DOM 元素
- `animationFn` (Function): 动画函数,接收 element 作为参数
**返回值:**
- `boolean`: 如果动画立即启动返回 true否则返回 false
**示例:**
```javascript
const optimizer = new ArgonRenderOptimizer();
const element = document.querySelector('.box');
// 启动动画
const started = optimizer.startAnimation(element, (el) => {
// 执行动画
el.style.transform = 'translateY(-50px)';
el.style.transition = 'transform 0.5s ease';
// 动画完成后调用 endAnimation
setTimeout(() => {
el.style.transform = '';
optimizer.endAnimation(el);
}, 500);
});
if (started) {
console.log('动画立即启动');
} else {
console.log('动画加入等待队列');
}
```
#### endAnimation(element)
结束一个动画,自动禁用 GPU 加速并启动队列中的下一个动画(如果有)。
**参数:**
- `element` (Element): 动画完成的 DOM 元素
**示例:**
```javascript
// 动画完成后调用
optimizer.endAnimation(element);
```
### 3. 查询方法
#### getActiveAnimationCount()
获取当前活动的动画数量。
**返回值:**
- `number`: 当前活动的动画数量
**示例:**
```javascript
const activeCount = optimizer.getActiveAnimationCount();
console.log(`当前活动动画: ${activeCount}`);
```
#### getQueuedAnimationCount()
获取等待队列中的动画数量。
**返回值:**
- `number`: 等待队列中的动画数量
**示例:**
```javascript
const queuedCount = optimizer.getQueuedAnimationCount();
console.log(`等待队列: ${queuedCount}`);
```
### 4. 清理方法
#### clearAllAnimations()
清除所有动画,包括活动动画和等待队列。
**示例:**
```javascript
// 清除所有动画
optimizer.clearAllAnimations();
```
## 完整使用示例
### 示例 1: 基本动画管理
```javascript
// 初始化优化器
const optimizer = new ArgonRenderOptimizer();
// 获取要动画的元素
const boxes = document.querySelectorAll('.box');
// 为每个元素启动动画
boxes.forEach((box, index) => {
optimizer.startAnimation(box, (element) => {
// 应用动画
element.style.transform = 'rotate(360deg) scale(1.2)';
element.style.transition = 'transform 1s ease';
// 1秒后结束动画
setTimeout(() => {
element.style.transform = '';
optimizer.endAnimation(element);
}, 1000);
});
});
```
### 示例 2: 监听动画完成事件
```javascript
const optimizer = new ArgonRenderOptimizer();
const element = document.querySelector('.animated-box');
optimizer.startAnimation(element, (el) => {
el.classList.add('animating');
// 监听 CSS 动画完成事件
el.addEventListener('animationend', function handler() {
el.classList.remove('animating');
optimizer.endAnimation(el);
el.removeEventListener('animationend', handler);
});
});
```
### 示例 3: 批量动画管理
```javascript
const optimizer = new ArgonRenderOptimizer();
function animateElements(elements) {
let completedCount = 0;
elements.forEach((element, index) => {
optimizer.startAnimation(element, (el) => {
// 执行动画
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'all 0.5s ease';
setTimeout(() => {
el.style.opacity = '1';
el.style.transform = 'translateY(0)';
}, 50);
// 动画完成
setTimeout(() => {
optimizer.endAnimation(el);
completedCount++;
if (completedCount === elements.length) {
console.log('所有动画完成');
}
}, 600);
});
});
// 显示状态
console.log(`活动动画: ${optimizer.getActiveAnimationCount()}`);
console.log(`等待队列: ${optimizer.getQueuedAnimationCount()}`);
}
// 使用
const elements = document.querySelectorAll('.fade-in');
animateElements(elements);
```
### 示例 4: 页面切换时清理
```javascript
const optimizer = new ArgonRenderOptimizer();
// PJAX 页面切换前清理所有动画
$(document).on('pjax:beforeReplace', function() {
optimizer.clearAllAnimations();
});
```
## 最佳实践
### 1. 始终调用 endAnimation()
确保在动画完成后调用 `endAnimation()`,以便:
- 释放 GPU 资源(移除 will-change
- 允许队列中的下一个动画启动
- 避免内存泄漏
```javascript
// ✓ 正确
optimizer.startAnimation(element, (el) => {
el.style.transform = 'scale(1.2)';
setTimeout(() => {
optimizer.endAnimation(el); // 记得调用
}, 500);
});
// ✗ 错误 - 忘记调用 endAnimation
optimizer.startAnimation(element, (el) => {
el.style.transform = 'scale(1.2)';
// 没有调用 endAnimation资源不会释放
});
```
### 2. 使用 CSS 动画时监听事件
```javascript
optimizer.startAnimation(element, (el) => {
el.classList.add('animate');
// 监听动画完成
el.addEventListener('animationend', function handler() {
optimizer.endAnimation(el);
el.removeEventListener('animationend', handler);
}, { once: true });
});
```
### 3. 错误处理
```javascript
optimizer.startAnimation(element, (el) => {
try {
// 动画逻辑
el.style.transform = 'translateX(100px)';
setTimeout(() => {
optimizer.endAnimation(el);
}, 500);
} catch (error) {
console.error('动画错误:', error);
// 确保即使出错也要清理
optimizer.endAnimation(el);
}
});
```
### 4. 页面卸载时清理
```javascript
// 页面卸载或组件销毁时
window.addEventListener('beforeunload', () => {
optimizer.clearAllAnimations();
});
// 或在 PJAX 中
$(document).on('pjax:beforeReplace', () => {
optimizer.clearAllAnimations();
});
```
## 性能优化建议
### 1. 仅对需要的元素启用 GPU 加速
不要过度使用 GPU 加速,只对真正需要动画的元素使用:
```javascript
// ✓ 好 - 仅在动画时启用
optimizer.startAnimation(element, (el) => {
// GPU 加速自动启用
el.style.transform = 'translateX(100px)';
setTimeout(() => {
optimizer.endAnimation(el); // GPU 加速自动禁用
}, 500);
});
// ✗ 不好 - 长期启用 GPU 加速
optimizer.enableGPU(element);
// 元素一直保持 GPU 加速状态,浪费资源
```
### 2. 利用动画队列
系统会自动管理动画队列,不需要手动控制:
```javascript
// 启动多个动画,系统自动排队
elements.forEach(el => {
optimizer.startAnimation(el, (element) => {
// 动画逻辑
setTimeout(() => {
optimizer.endAnimation(element);
}, 500);
});
});
```
### 3. 监控动画状态
在开发时监控动画状态,确保没有泄漏:
```javascript
setInterval(() => {
console.log('活动动画:', optimizer.getActiveAnimationCount());
console.log('等待队列:', optimizer.getQueuedAnimationCount());
}, 1000);
```
## 需求验证
本实现满足以下需求:
-**需求 5.2**: 动画时使用 `will-change` 提示浏览器创建合成层
-**需求 5.3**: 动画完成时移除 `will-change` 属性释放资源
-**需求 5.5**: 限制同时运行的动画数量不超过 3 个
## 测试
运行测试文件验证功能:
1. **交互式测试**: 打开 `test-gpu-acceleration.html` 在浏览器中测试
2. **自动化测试**: 打开 `test-gpu-simple.html` 查看自动化测试结果
## 常见问题
### Q: 为什么限制为 3 个动画?
A: 同时运行过多动画会导致 CPU 占用过高和性能下降。3 个是经过测试的最佳平衡点。
### Q: 如果我需要更多同时动画怎么办?
A: 可以修改 `maxAnimations` 属性,但不建议超过 5 个:
```javascript
optimizer.maxAnimations = 5; // 不推荐
```
### Q: 动画队列是 FIFO 还是 LIFO
A: FIFO先进先出。先加入队列的动画会先执行。
### Q: 如果忘记调用 endAnimation() 会怎样?
A: 会导致:
- GPU 资源不会释放will-change 一直存在)
- 队列中的动画永远不会启动
- 活动动画计数不准确
建议始终在动画完成时调用 `endAnimation()`