feat: 优化 App.vue 和 ParticleBackground 组件的动画效果,调整粒子配置和运动参数,提升性能和视觉表现,同时增加节流控制以优化动画刷新率。
This commit is contained in:
parent
318f2155bc
commit
35e8fda1b8
44
src/App.vue
44
src/App.vue
@ -112,9 +112,11 @@ onMounted(() => {
|
|||||||
height: 60px;
|
height: 60px;
|
||||||
background: linear-gradient(45deg, rgba(255,255,255,0.4), rgba(255,255,255,0.1));
|
background: linear-gradient(45deg, rgba(255,255,255,0.4), rgba(255,255,255,0.1));
|
||||||
border-radius: 20% 60% 40% 80%;
|
border-radius: 20% 60% 40% 80%;
|
||||||
animation: float 20s linear infinite;
|
will-change: transform;
|
||||||
|
transform: translateZ(0);
|
||||||
|
animation: float 25s linear infinite;
|
||||||
animation-delay: var(--delay);
|
animation-delay: var(--delay);
|
||||||
opacity: 0.6;
|
opacity: 0.4;
|
||||||
backdrop-filter: blur(2px);
|
backdrop-filter: blur(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,9 +126,11 @@ onMounted(() => {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.8), rgba(255,255,255,0.1));
|
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.8), rgba(255,255,255,0.1));
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
animation: bubble 15s ease-in-out infinite;
|
will-change: transform;
|
||||||
|
transform: translateZ(0);
|
||||||
|
animation: bubble 20s ease-in-out infinite;
|
||||||
animation-delay: var(--delay);
|
animation-delay: var(--delay);
|
||||||
opacity: 0.4;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes float {
|
@keyframes float {
|
||||||
@ -165,8 +169,11 @@ onMounted(() => {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
width: 30px;
|
width: 30px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
animation: floatFlower 20s ease-in-out infinite;
|
will-change: transform;
|
||||||
|
transform: translateZ(0);
|
||||||
|
animation: floatFlower 25s ease-in-out infinite;
|
||||||
animation-delay: var(--delay);
|
animation-delay: var(--delay);
|
||||||
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flower-center {
|
.flower-center {
|
||||||
@ -204,9 +211,12 @@ onMounted(() => {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
background: rgba(255, 255, 255, 0.8);
|
background: rgba(255, 255, 255, 0.8);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
animation: floatCloud 25s linear infinite;
|
will-change: transform;
|
||||||
|
transform: translateZ(0);
|
||||||
|
animation: floatCloud 30s linear infinite;
|
||||||
animation-delay: var(--delay);
|
animation-delay: var(--delay);
|
||||||
filter: blur(4px);
|
filter: blur(4px);
|
||||||
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-cloud::before,
|
.floating-cloud::before,
|
||||||
@ -246,7 +256,8 @@ onMounted(() => {
|
|||||||
rgba(176, 224, 230, 0.05) 80%,
|
rgba(176, 224, 230, 0.05) 80%,
|
||||||
transparent 100%
|
transparent 100%
|
||||||
);
|
);
|
||||||
animation: rotateGlow 30s linear infinite;
|
animation: rotateGlow 40s linear infinite;
|
||||||
|
opacity: 0.08;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 动画关键帧 */
|
/* 动画关键帧 */
|
||||||
@ -314,15 +325,11 @@ onMounted(() => {
|
|||||||
/* 性能优化 */
|
/* 性能优化 */
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.floating-element,
|
.floating-element,
|
||||||
.floating-bubble {
|
.floating-bubble,
|
||||||
animation: none;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-flower,
|
.floating-flower,
|
||||||
.floating-cloud,
|
.floating-cloud,
|
||||||
.rainbow-glow {
|
.rainbow-glow {
|
||||||
animation: none;
|
animation-play-state: paused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,5 +402,16 @@ onMounted(() => {
|
|||||||
width: 120px;
|
width: 120px;
|
||||||
height: 120px;
|
height: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.floating-element,
|
||||||
|
.floating-bubble,
|
||||||
|
.floating-flower,
|
||||||
|
.floating-cloud {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-glow {
|
||||||
|
opacity: 0.05;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -12,21 +12,21 @@ const breakpoints = useBreakpoints({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const isMobile = breakpoints.smaller('tablet')
|
const isMobile = breakpoints.smaller('tablet')
|
||||||
const particleCount = isMobile.value ? 1000 : 1800
|
const particleCount = isMobile.value ? 800 : 1500
|
||||||
|
|
||||||
// 优化粒子配置
|
// 优化粒子配置
|
||||||
const config = {
|
const config = {
|
||||||
particleSize: 2.5,
|
particleSize: isMobile.value ? 2.0 : 2.5,
|
||||||
systemRadius: 20,
|
systemRadius: isMobile.value ? 15 : 20,
|
||||||
baseSpeed: 0.15,
|
baseSpeed: 0.1,
|
||||||
hoverRadius: 4,
|
hoverRadius: isMobile.value ? 3 : 4,
|
||||||
color: 0xb4e0f7, // 主色调:浅蓝色
|
color: 0xb4e0f7, // 主色调:浅蓝色
|
||||||
colorVariation: [0xffb7d0, 0xa8d8ea, 0xf3e5f5], // 粒子颜色变化
|
colorVariation: [0xffb7d0, 0xa8d8ea, 0xf3e5f5], // 粒子颜色变化
|
||||||
lineColor: 0xa8d8ea,
|
lineColor: 0xa8d8ea,
|
||||||
lineDistance: 3.5,
|
lineDistance: isMobile.value ? 2.5 : 3.5,
|
||||||
particleCount: isMobile.value ? 1200 : 2000,
|
particleCount: isMobile.value ? 800 : 1500,
|
||||||
particleOpacity: 0.8,
|
particleOpacity: 0.6,
|
||||||
lineOpacity: 0.2,
|
lineOpacity: 0.15,
|
||||||
glowSize: 3
|
glowSize: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,37 +193,44 @@ const createConnections = (positions, colors) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const animate = () => {
|
const animate = () => {
|
||||||
|
if (!document.hidden) {
|
||||||
requestAnimationFrame(animate)
|
requestAnimationFrame(animate)
|
||||||
|
|
||||||
const time = Date.now() * 0.0001
|
const time = Date.now() * 0.00005
|
||||||
particles.material.uniforms.time.value = time
|
particles.material.uniforms.time.value = time
|
||||||
|
|
||||||
|
if (time % 2 === 0) {
|
||||||
|
updateParticles()
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer.render(scene, camera)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateParticles = () => {
|
||||||
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.1) * 0.02
|
positions[i3 + 1] += Math.cos(time + i * 0.03) * 0.01
|
||||||
positions[i3 + 1] += Math.cos(time + i * 0.05) * 0.02
|
positions[i3 + 2] += Math.sin(time * 0.3 + i * 0.04) * 0.01
|
||||||
positions[i3 + 2] += Math.sin(time * 0.5 + i * 0.07) * 0.02
|
|
||||||
|
|
||||||
// 鼠标交互优化
|
|
||||||
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
|
||||||
const distance = Math.sqrt(dx * dx + dy * dy)
|
const distance = Math.sqrt(dx * dx + dy * dy)
|
||||||
|
|
||||||
if (distance < config.hoverRadius) {
|
if (distance < config.hoverRadius) {
|
||||||
const force = (config.hoverRadius - distance) / config.hoverRadius
|
const force = (config.hoverRadius - distance) / config.hoverRadius * 0.8
|
||||||
positions[i3] += dx * force * 0.03
|
positions[i3] += dx * force * 0.02
|
||||||
positions[i3 + 1] += dy * force * 0.03
|
positions[i3 + 1] += dy * force * 0.02
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
particles.geometry.attributes.position.needsUpdate = true
|
particles.geometry.attributes.position.needsUpdate = true
|
||||||
renderer.render(scene, camera)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
|
@ -132,16 +132,33 @@ export function useParticles(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动画循环
|
// 添加节流控制
|
||||||
const animate = () => {
|
let animationFrame = null
|
||||||
|
let lastTime = 0
|
||||||
|
|
||||||
|
const animate = (timestamp) => {
|
||||||
|
if (!lastTime) lastTime = timestamp
|
||||||
|
const delta = timestamp - lastTime
|
||||||
|
|
||||||
|
// 限制刷新率,约60fps
|
||||||
|
if (delta > 16) {
|
||||||
ctx.clearRect(0, 0, canvasSize.value.width, canvasSize.value.height)
|
ctx.clearRect(0, 0, canvasSize.value.width, canvasSize.value.height)
|
||||||
|
|
||||||
particles.forEach(particle => {
|
// 批量更新粒子
|
||||||
particle.update()
|
const batchSize = 50
|
||||||
particle.draw()
|
for (let i = 0; i < particles.length; i += batchSize) {
|
||||||
})
|
const end = Math.min(i + batchSize, particles.length)
|
||||||
|
for (let j = i; j < end; j++) {
|
||||||
|
particles[j].update()
|
||||||
|
particles[j].draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drawLines()
|
drawLines()
|
||||||
|
lastTime = timestamp
|
||||||
|
}
|
||||||
|
|
||||||
|
animationFrame = requestAnimationFrame(animate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理鼠标移动
|
// 处理鼠标移动
|
||||||
@ -184,6 +201,10 @@ export function useParticles(options) {
|
|||||||
|
|
||||||
// 清理
|
// 清理
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
if (animationFrame) {
|
||||||
|
cancelAnimationFrame(animationFrame)
|
||||||
|
}
|
||||||
|
particles.length = 0
|
||||||
pause()
|
pause()
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize)
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user