feat: 在 App.vue 和 ParticleBackground 组件中优化动画控制和性能,添加页面可见性处理,确保动画在页面隐藏时暂停,提升用户体验和资源利用效率。
This commit is contained in:
parent
298b5410bb
commit
a1e5bfd74d
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>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user