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