You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现带圆形进度条、居中播放按钮且支持依赖注入播放的音频播放器?

Got it, let's build this audio player with a circular progress bar, centered play button, and implement dependency injection for the playback trigger. Here's a complete, clean implementation:

1. HTML Structure

We'll start with a container that holds the hidden audio element, the circular progress bar wrapper, and the play button:

<div class="audio-player-container">
  <!-- Hidden audio element -->
  <audio id="audio-player" src="your-audio-file.mp3"></audio>
  
  <!-- Circular progress bar wrapper -->
  <div class="circular-progress">
    <div class="progress-ring">
      <svg width="200" height="200">
        <circle class="progress-bg" cx="100" cy="100" r="90"></circle>
        <circle class="progress-fill" cx="100" cy="100" r="90"></circle>
      </svg>
      <!-- Centered play button -->
      <button class="play-btn" id="play-btn">▶️</button>
    </div>
  </div>
</div>
2. CSS Styling

This will handle the circular progress bar visual and center the play button perfectly:

.audio-player-container {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  background: #f0f0f0;
}

.circular-progress .progress-ring {
  position: relative;
}

.progress-bg {
  fill: none;
  stroke: #e0e0e0;
  stroke-width: 8;
}

.progress-fill {
  fill: none;
  stroke: #4CAF50;
  stroke-width: 8;
  stroke-linecap: round;
  transform: rotate(-90deg);
  transform-origin: center;
  stroke-dasharray: 565.48; /* Circumference of the circle: 2*π*90 ≈ 565.48 */
  stroke-dashoffset: 565.48; /* Start with full empty progress */
  transition: stroke-dashoffset 0.1s linear;
}

.play-btn {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 60px;
  height: 60px;
  border-radius: 50%;
  border: none;
  background: white;
  font-size: 24px;
  cursor: pointer;
  box-shadow: 0 2px 8px rgba(0,0,0,0.2);
  transition: background 0.2s;
}

.play-btn:hover {
  background: #f5f5f5;
}

.play-btn.paused {
  font-size: 20px;
}
3. Dependency Injection for Playback

We'll decouple the audio playback logic into a service, then inject it into our player controller. This makes the code modular and easy to test/replace:

Step 3.1: Create the Audio Service (Dependency)

class AudioPlaybackService {
  constructor(audioElement) {
    this.audio = audioElement;
    this.isPlaying = false;
  }

  togglePlay() {
    if (this.isPlaying) {
      this.audio.pause();
    } else {
      this.audio.play();
    }
    this.isPlaying = !this.isPlaying;
    return this.isPlaying;
  }

  updateProgress() {
    const duration = this.audio.duration;
    const currentTime = this.audio.currentTime;
    const progress = (currentTime / duration) * 565.48;
    return 565.48 - progress;
  }
}

Step 3.2: Inject the Service into the Player Controller

class AudioPlayerController {
  constructor(playbackService, playBtn, progressFill) {
    this.playbackService = playbackService;
    this.playBtn = playBtn;
    this.progressFill = progressFill;

    this.init();
  }

  init() {
    // Bind button click event
    this.playBtn.addEventListener('click', () => this.handlePlayClick());
    
    // Update progress bar as audio plays
    this.playbackService.audio.addEventListener('timeupdate', () => this.updateProgressBar());
  }

  handlePlayClick() {
    const isPlaying = this.playbackService.togglePlay();
    // Update button icon
    this.playBtn.textContent = isPlaying ? '⏸️' : '▶️';
    this.playBtn.classList.toggle('paused', isPlaying);
  }

  updateProgressBar() {
    const dashOffset = this.playbackService.updateProgress();
    this.progressFill.style.strokeDashoffset = dashOffset;
  }
}

// Initialize the player with dependency injection
const audioElement = document.getElementById('audio-player');
const playbackService = new AudioPlaybackService(audioElement);
const playBtn = document.getElementById('play-btn');
const progressFill = document.querySelector('.progress-fill');

new AudioPlayerController(playbackService, playBtn, progressFill);

Quick breakdown:

  • Dependency Injection: We pass the AudioPlaybackService directly into the AudioPlayerController instead of letting the controller create it. This lets you easily swap out the service (like using a mock for testing) without touching the controller code.
  • Circular Progress Bar: Uses SVG circles with stroke-dasharray and stroke-dashoffset to animate the progress in real-time as the audio plays.
  • Centered Button: Positioned perfectly in the middle of the circle using absolute positioning and transform: translate(-50%, -50%).

内容的提问来源于stack exchange,提问作者Judson Abraham

火山引擎 最新活动