一个元素要同时做多个动画

403 阅读4分钟

Q.如何在前端实现多个动画同时进行而不会相互覆盖?

主要有以下几种解决方案:

  1. 使用逗号分隔多个动画
  2. 使用组合动画
  3. 使用 transform 组合变换
  4. 使用 Web Animations API

具体示例:

/* 1. 基础动画定义 */
@keyframes moveLeft {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-100px);
  }
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes rotate {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes scale {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.5);
  }
  100% {
    transform: scale(1);
  }
}

/* 2. 方案一:使用逗号分隔多个动画 */
.multi-animation {
  animation: 
    moveLeft 2s ease infinite,
    fadeIn 1s ease-in,
    rotate 3s linear infinite;
}

/* 3. 方案二:组合动画 */
@keyframes combinedAnimation {
  0% {
    transform: translateX(0) rotate(0deg) scale(1);
    opacity: 0;
  }
  25% {
    transform: translateX(-50px) rotate(90deg) scale(1.2);
    opacity: 0.5;
  }
  50% {
    transform: translateX(-100px) rotate(180deg) scale(1.5);
    opacity: 1;
  }
  75% {
    transform: translateX(-50px) rotate(270deg) scale(1.2);
    opacity: 0.5;
  }
  100% {
    transform: translateX(0) rotate(360deg) scale(1);
    opacity: 0;
  }
}

.combined-animation {
  animation: combinedAnimation 4s ease-in-out infinite;
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>多重动画示例</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <!-- 基础示例 -->
    <div class="container">
        <div class="multi-animation">多个动画</div>
        <div class="combined-animation">组合动画</div>
    </div>

    <!-- 高级示例 -->
    <div id="advanced-animation"></div>
</body>
</html>
// Web Animations API 示例
document.addEventListener('DOMContentLoaded', () => {
  const element = document.getElementById('advanced-animation');
  
  // 定义多个动画
  const animations = [
    // 位移动画
    element.animate([
      { transform: 'translateX(0)' },
      { transform: 'translateX(200px)' },
      { transform: 'translateX(0)' }
    ], {
      duration: 2000,
      iterations: Infinity
    }),
    
    // 旋转动画
    element.animate([
      { transform: 'rotate(0deg)' },
      { transform: 'rotate(360deg)' }
    ], {
      duration: 3000,
      iterations: Infinity
    }),
    
    // 缩放动画
    element.animate([
      { transform: 'scale(1)' },
      { transform: 'scale(1.5)' },
      { transform: 'scale(1)' }
    ], {
      duration: 1500,
      iterations: Infinity
    }),
    
    // 透明度动画
    element.animate([
      { opacity: 1 },
      { opacity: 0.5 },
      { opacity: 1 }
    ], {
      duration: 1000,
      iterations: Infinity
    })
  ];
});
/* 更复杂的动画组合示例 */
.animated-card {
  position: relative;
  width: 200px;
  height: 300px;
  background: linear-gradient(45deg, #ff6b6b, #4ecdc4);
  border-radius: 10px;
  overflow: hidden;
}

/* 悬浮效果组合 */
.animated-card:hover {
  animation: 
    cardFloat 2s ease-in-out infinite,
    cardGlow 1.5s ease infinite,
    cardRotate 3s linear infinite;
}

@keyframes cardFloat {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-20px);
  }
}

@keyframes cardGlow {
  0%, 100% {
    box-shadow: 0 0 5px rgba(78, 205, 196, 0.5);
  }
  50% {
    box-shadow: 0 0 20px rgba(255, 107, 107, 0.8);
  }
}

@keyframes cardRotate {
  0% {
    transform: rotateY(0deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}

/* 内容动画 */
.card-content {
  position: absolute;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: white;
}

.card-title {
  animation: 
    titleSlide 1s ease-out,
    titlePulse 2s ease infinite;
}

@keyframes titleSlide {
  from {
    transform: translateX(-100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes titlePulse {
  0%, 100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
}

/* 粒子效果 */
.particles {
  position: absolute;
  width: 100%;
  height: 100%;
}

.particle {
  position: absolute;
  width: 5px;
  height: 5px;
  background: rgba(255, 255, 255, 0.5);
  border-radius: 50%;
}

/* 为每个粒子设置不同的动画 */
.particle:nth-child(1) {
  animation: 
    particle1 2s linear infinite,
    fadeInOut 2s ease infinite;
}

.particle:nth-child(2) {
  animation: 
    particle2 3s linear infinite,
    fadeInOut 3s ease infinite;
}

@keyframes particle1 {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(100px, -100px);
  }
}

@keyframes particle2 {
  0% {
    transform: translate(0, 0);
  }
  100% {
    transform: translate(-100px, 100px);
  }
}

@keyframes fadeInOut {
  0%, 100% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
}
class ParticleSystem {
  constructor(container, count = 50) {
    this.container = container;
    this.count = count;
    this.particles = [];
    this.init();
  }

  init() {
    for (let i = 0; i < this.count; i++) {
      this.createParticle();
    }
  }

  createParticle() {
    const particle = document.createElement('div');
    particle.className = 'particle';
    
    // 随机位置
    particle.style.left = `${Math.random() * 100}%`;
    particle.style.top = `${Math.random() * 100}%`;
    
    // 随机动画延迟
    particle.style.animationDelay = `${Math.random() * 2}s`;
    
    this.container.appendChild(particle);
    this.particles.push(particle);
  }

  animate() {
    this.particles.forEach(particle => {
      // 添加自定义动画
      const animations = [
        particle.animate([
          { transform: 'translate(0, 0)' },
          { transform: `translate(${Math.random() * 200 - 100}px, ${Math.random() * 200 - 100}px)` }
        ], {
          duration: 2000 + Math.random() * 1000,
          iterations: Infinity,
          direction: 'alternate',
          easing: 'ease-in-out'
        }),
        
        particle.animate([
          { opacity: 0 },
          { opacity: 1 },
          { opacity: 0 }
        ], {
          duration: 1500 + Math.random() * 500,
          iterations: Infinity
        })
      ];
    });
  }
}

主要解决方案说明:

  1. 使用逗号分隔多个动画
  • 这是最简单的方法
  • 每个动画可以有自己的时间和缓动函数
  • 动画之间不会相互覆盖
  1. 使用组合动画
  • 将多个动画效果组合到一个@keyframes中
  • 适合需要精确控制多个属性同步变化的情况
  • 可以减少代码量
  1. 使用Web Animations API
  • 提供了更灵活的JavaScript动画控制
  • 可以精确控制每个动画的时间线
  • 支持更复杂的动画序列
  1. 使用transform组合变换
  • 在同一个transform属性中组合多个变换
  • 性能较好,浏览器优化更好

使用建议:

  1. 对于简单的多重动画,使用逗号分隔的方式最直观
  2. 需要精确控制的复杂动画,使用组合动画
  3. 需要JavaScript控制的动画,使用Web Animations API
  4. 注意性能优化,优先使用transform和opacity属性