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

8.9 KiB
Raw Blame History

GPU 加速管理使用指南

概述

ArgonRenderOptimizer 类提供了 GPU 加速管理功能,包括:

  • 启用/禁用 GPU 加速will-change 属性)
  • 限制同时运行的动画数量(最多 3 个)
  • 自动管理动画队列
  • 动画完成后自动清理资源

功能说明

1. GPU 加速管理

enableGPU(element)

为元素启用 GPU 加速,设置 will-change: transform, opacity 属性。

参数:

  • element (Element): 要启用 GPU 加速的 DOM 元素

示例:

const optimizer = new ArgonRenderOptimizer();
const element = document.querySelector('.animated-element');

// 启用 GPU 加速
optimizer.enableGPU(element);

disableGPU(element)

为元素禁用 GPU 加速,移除 will-change 属性。

参数:

  • element (Element): 要禁用 GPU 加速的 DOM 元素

示例:

// 禁用 GPU 加速
optimizer.disableGPU(element);

2. 动画数量限制

系统自动限制同时运行的动画数量不超过 3 个,超出的动画会进入等待队列。

startAnimation(element, animationFn)

启动一个动画。如果当前活动动画数量未达到上限3 个),立即启动;否则加入等待队列。

参数:

  • element (Element): 要动画的 DOM 元素
  • animationFn (Function): 动画函数,接收 element 作为参数

返回值:

  • boolean: 如果动画立即启动返回 true否则返回 false

示例:

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 元素

示例:

// 动画完成后调用
optimizer.endAnimation(element);

3. 查询方法

getActiveAnimationCount()

获取当前活动的动画数量。

返回值:

  • number: 当前活动的动画数量

示例:

const activeCount = optimizer.getActiveAnimationCount();
console.log(`当前活动动画: ${activeCount} 个`);

getQueuedAnimationCount()

获取等待队列中的动画数量。

返回值:

  • number: 等待队列中的动画数量

示例:

const queuedCount = optimizer.getQueuedAnimationCount();
console.log(`等待队列: ${queuedCount} 个`);

4. 清理方法

clearAllAnimations()

清除所有动画,包括活动动画和等待队列。

示例:

// 清除所有动画
optimizer.clearAllAnimations();

完整使用示例

示例 1: 基本动画管理

// 初始化优化器
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: 监听动画完成事件

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: 批量动画管理

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: 页面切换时清理

const optimizer = new ArgonRenderOptimizer();

// PJAX 页面切换前清理所有动画
$(document).on('pjax:beforeReplace', function() {
	optimizer.clearAllAnimations();
});

最佳实践

1. 始终调用 endAnimation()

确保在动画完成后调用 endAnimation(),以便:

  • 释放 GPU 资源(移除 will-change
  • 允许队列中的下一个动画启动
  • 避免内存泄漏
// ✓ 正确
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 动画时监听事件

optimizer.startAnimation(element, (el) => {
	el.classList.add('animate');
	
	// 监听动画完成
	el.addEventListener('animationend', function handler() {
		optimizer.endAnimation(el);
		el.removeEventListener('animationend', handler);
	}, { once: true });
});

3. 错误处理

optimizer.startAnimation(element, (el) => {
	try {
		// 动画逻辑
		el.style.transform = 'translateX(100px)';
		
		setTimeout(() => {
			optimizer.endAnimation(el);
		}, 500);
	} catch (error) {
		console.error('动画错误:', error);
		// 确保即使出错也要清理
		optimizer.endAnimation(el);
	}
});

4. 页面卸载时清理

// 页面卸载或组件销毁时
window.addEventListener('beforeunload', () => {
	optimizer.clearAllAnimations();
});

// 或在 PJAX 中
$(document).on('pjax:beforeReplace', () => {
	optimizer.clearAllAnimations();
});

性能优化建议

1. 仅对需要的元素启用 GPU 加速

不要过度使用 GPU 加速,只对真正需要动画的元素使用:

// ✓ 好 - 仅在动画时启用
optimizer.startAnimation(element, (el) => {
	// GPU 加速自动启用
	el.style.transform = 'translateX(100px)';
	setTimeout(() => {
		optimizer.endAnimation(el);  // GPU 加速自动禁用
	}, 500);
});

// ✗ 不好 - 长期启用 GPU 加速
optimizer.enableGPU(element);
// 元素一直保持 GPU 加速状态,浪费资源

2. 利用动画队列

系统会自动管理动画队列,不需要手动控制:

// 启动多个动画,系统自动排队
elements.forEach(el => {
	optimizer.startAnimation(el, (element) => {
		// 动画逻辑
		setTimeout(() => {
			optimizer.endAnimation(element);
		}, 500);
	});
});

3. 监控动画状态

在开发时监控动画状态,确保没有泄漏:

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 个:

optimizer.maxAnimations = 5;  // 不推荐

Q: 动画队列是 FIFO 还是 LIFO

A: FIFO先进先出。先加入队列的动画会先执行。

Q: 如果忘记调用 endAnimation() 会怎样?

A: 会导致:

  • GPU 资源不会释放will-change 一直存在)
  • 队列中的动画永远不会启动
  • 活动动画计数不准确

建议始终在动画完成时调用 endAnimation()