feat: 在 App.vue 和 ParticleBackground 组件中优化动画控制和性能,添加页面可见性处理,确保动画在页面隐藏时暂停,提升用户体验和资源利用效率。
This commit is contained in:
		
							
								
								
									
										80
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								src/App.vue
									
									
									
									
									
								
							| @@ -77,37 +77,37 @@ import WechatModal from './components/WechatModal.vue' | ||||
|  | ||||
| const showWechatModal = ref(false) | ||||
|  | ||||
| // 优化动画性能 | ||||
| // 优化动画控制 | ||||
| const handleVisibilityChange = () => { | ||||
|   const elements = document.querySelectorAll('.floating-element, .floating-bubble, .floating-flower, .floating-cloud') | ||||
|   const isHidden = document.hidden | ||||
|    | ||||
|   elements.forEach(el => { | ||||
|     if (isHidden) { | ||||
|       el.classList.add('animation-paused') | ||||
|     } else { | ||||
|       el.classList.remove('animation-paused') | ||||
|     } | ||||
|   }) | ||||
| } | ||||
|  | ||||
| // 优化初始化 | ||||
| onMounted(() => { | ||||
|   const elements = document.querySelectorAll('.floating-element, .floating-bubble, .floating-flower, .floating-cloud') | ||||
|   elements.forEach(el => { | ||||
|     el.style.left = `${Math.random() * 100}vw` | ||||
|     el.style.top = `${Math.random() * 100}vh` | ||||
|     // 添加硬件加速 | ||||
|     el.style.transform = 'translateZ(0)' | ||||
|     el.style.willChange = 'transform' | ||||
|   }) | ||||
|     el.style.animationPlayState = 'running' | ||||
|   }) | ||||
|    | ||||
| // 添加页面可见性检测 | ||||
| onMounted(() => { | ||||
|   document.addEventListener('visibilitychange', handleVisibilityChange) | ||||
| }) | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   document.removeEventListener('visibilitychange', handleVisibilityChange) | ||||
| }) | ||||
|  | ||||
| const handleVisibilityChange = () => { | ||||
|   const elements = document.querySelectorAll('.floating-element, .floating-bubble, .floating-flower, .floating-cloud') | ||||
|   elements.forEach(el => { | ||||
|     if (document.hidden) { | ||||
|       el.style.animationPlayState = 'paused' | ||||
|     } else { | ||||
|       el.style.animationPlayState = 'running' | ||||
|     } | ||||
|   }) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| @@ -166,17 +166,14 @@ const handleVisibilityChange = () => { | ||||
| } | ||||
|  | ||||
| @keyframes bubble { | ||||
|   0%, 100% { | ||||
|     transform: translate( | ||||
|       calc(random(100) * 1vw), | ||||
|       calc(100vh + 50px) | ||||
|     ); | ||||
|   0% { | ||||
|     transform: translate(0, 100vh); | ||||
|   } | ||||
|   50% { | ||||
|     transform: translate( | ||||
|       calc(random(100) * 1vw), | ||||
|       -50px | ||||
|     ); | ||||
|     transform: translate(calc(100vw * 0.5), -50px); | ||||
|   } | ||||
|   100% { | ||||
|     transform: translate(100vw, 100vh); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -285,26 +282,23 @@ const handleVisibilityChange = () => { | ||||
|  | ||||
| /* 动画关键帧 */ | ||||
| @keyframes floatFlower { | ||||
|   0%, 100% { | ||||
|     transform: translate( | ||||
|       calc(random(100) * 1vw), | ||||
|       -50px | ||||
|     ) rotate(0deg); | ||||
|   0% { | ||||
|     transform: translate(0, -50px) rotate(0deg); | ||||
|   } | ||||
|   50% { | ||||
|     transform: translate( | ||||
|       calc(random(100) * 1vw), | ||||
|       calc(100vh + 50px) | ||||
|     ) rotate(360deg); | ||||
|     transform: translate(calc(100vw * 0.5), calc(100vh + 50px)) rotate(360deg); | ||||
|   } | ||||
|   100% { | ||||
|     transform: translate(100vw, -50px) rotate(720deg); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @keyframes floatCloud { | ||||
|   from { | ||||
|     transform: translateX(-150px) translateY(calc(random(50) * 1vh)); | ||||
|   0% { | ||||
|     transform: translateX(-150px) translateY(0); | ||||
|   } | ||||
|   to { | ||||
|     transform: translateX(calc(100vw + 150px)) translateY(calc(random(50) * 1vh)); | ||||
|   100% { | ||||
|     transform: translateX(calc(100vw + 150px)) translateY(0); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -430,11 +424,12 @@ const handleVisibilityChange = () => { | ||||
|   .floating-bubble, | ||||
|   .floating-flower, | ||||
|   .floating-cloud { | ||||
|     display: none; | ||||
|     animation-duration: 15s; | ||||
|     animation-timing-function: linear; | ||||
|   } | ||||
|    | ||||
|   .rainbow-glow { | ||||
|     opacity: 0.05; | ||||
|     animation-duration: 20s; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -446,15 +441,16 @@ const handleVisibilityChange = () => { | ||||
|   transform: translateZ(0); | ||||
|   backface-visibility: hidden; | ||||
|   perspective: 1000px; | ||||
|   animation-play-state: running; | ||||
| } | ||||
|  | ||||
| /* 优化动画性能 */ | ||||
| /* 添加动画性能优化 */ | ||||
| @media (prefers-reduced-motion: no-preference) { | ||||
|   .floating-element, | ||||
|   .floating-bubble, | ||||
|   .floating-flower, | ||||
|   .floating-cloud { | ||||
|     animation-play-state: running; | ||||
|     animation-play-state: running !important; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -33,6 +33,8 @@ const config = { | ||||
| let scene, camera, renderer, particles, lines, mouse = { x: 0, y: 0 } | ||||
|  | ||||
| const initThreeJS = () => { | ||||
|   if (!canvasRef.value) return | ||||
|    | ||||
|   // 1. 初始化场景 | ||||
|   scene = new THREE.Scene() | ||||
|   scene.background = new THREE.Color(0x000000) | ||||
| @@ -51,7 +53,8 @@ const initThreeJS = () => { | ||||
|   renderer = new THREE.WebGLRenderer({ | ||||
|     canvas: canvasRef.value, | ||||
|     antialias: true, | ||||
|     alpha: true | ||||
|     alpha: true, | ||||
|     powerPreference: 'high-performance' | ||||
|   }) | ||||
|   renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) | ||||
|   renderer.setSize(window.innerWidth, window.innerHeight) | ||||
| @@ -59,7 +62,7 @@ const initThreeJS = () => { | ||||
|   // 4. 创建粒子系统 | ||||
|   createParticles() | ||||
|    | ||||
|   // 5. 动画循环 | ||||
|   // 5. 启动动画循环 | ||||
|   animate() | ||||
| } | ||||
|  | ||||
| @@ -202,20 +205,27 @@ const animate = () => { | ||||
|     // 更新粒子位置 | ||||
|     updateParticles(time) | ||||
|      | ||||
|     // 确保渲染器存在且场景已初始化 | ||||
|     if (renderer && scene) { | ||||
|       renderer.render(scene, camera) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| const updateParticles = (time) => { | ||||
|   if (!particles || !particles.geometry) return | ||||
|    | ||||
|   const positions = particles.geometry.attributes.position.array | ||||
|    | ||||
|   for (let i = 0; i < config.particleCount; i++) { | ||||
|     const i3 = i * 3 | ||||
|      | ||||
|     // 优化粒子运动 | ||||
|     positions[i3] += Math.sin(time + i * 0.05) * 0.01 | ||||
|     positions[i3 + 1] += Math.cos(time + i * 0.03) * 0.01 | ||||
|     positions[i3 + 2] += Math.sin(time * 0.3 + i * 0.04) * 0.01 | ||||
|      | ||||
|     // 鼠标交互 | ||||
|     if (mouse.x !== null && mouse.y !== null) { | ||||
|       const dx = positions[i3] - mouse.x * 20 | ||||
|       const dy = positions[i3 + 1] - mouse.y * 20 | ||||
| @@ -258,16 +268,34 @@ const addPostProcessing = () => { | ||||
|   composer.addPass(bloomPass) | ||||
| } | ||||
|  | ||||
| // 添加页面可见性处理 | ||||
| const handleVisibilityChange = () => { | ||||
|   if (document.hidden) { | ||||
|     if (renderer) { | ||||
|       renderer.setAnimationLoop(null) | ||||
|     } | ||||
|   } else { | ||||
|     if (renderer) { | ||||
|       renderer.setAnimationLoop(animate) | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| onMounted(() => { | ||||
|   initThreeJS() | ||||
|   window.addEventListener('resize', handleResize) | ||||
|   window.addEventListener('mousemove', handleMouseMove) | ||||
|   window.addEventListener('visibilitychange', handleVisibilityChange) | ||||
| }) | ||||
|  | ||||
| onUnmounted(() => { | ||||
|   window.removeEventListener('resize', handleResize) | ||||
|   window.removeEventListener('mousemove', handleMouseMove) | ||||
|   if (renderer) renderer.dispose() | ||||
|   window.removeEventListener('visibilitychange', handleVisibilityChange) | ||||
|   if (renderer) { | ||||
|     renderer.dispose() | ||||
|     renderer = null | ||||
|   } | ||||
| }) | ||||
| </script> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Cat Tom
					Cat Tom