top of page
Search

Ionic Music Player

  • Writer: Fire Focus
    Fire Focus
  • Oct 10, 2020
  • 6 min read

Introduction


In this lesson you will learn about how you can create a music player app using Ionic Framework.First of all, we have to create a songs array with title, subtitle and thumnail image in songs.page.ts.


songs.page.ts

songs = [
{
title: "Believer",
subtitle: "Imagine Dragons"
,img: "/assets/believer.jpg",
path: "/assets/songs/believer.mp3"
},
{
title: "Fifa 2010",
subtitle: "FIFA 2010 Theme Song",
img: "/assets/fifa.png",
path: "/assets/songs/fifa_2010.mp3"
},
{title: "Thunder",
subtitle: "Imagine Dragons",
img: "/assets/thunder.jpg",
path: "/assets/songs/thunder.mp3"
}
];

Display Songs


Next, we display songs from the songs array.


songs.page.html

<ion-item lines="none" *ngFor="let song of songs" (click)="playSong(song.title,song.subtitle,song.img,song.path)">
 <ion-thumbnail slot="start">
    <img src="{{song.img}}" />
 </ion-thumbnail>
 <ion-label> 
    <h2>{{song.title}}</h2>
    <p>{{song.subtitle}}</p>
 </ion-label>
</ion-item>

Full Player UI


Next, we create a UI for the music player.Here we have variables like

  • currImage - to keep track of current song thumbnail

  • currTitle - to keep track of current song title

  • currSubtitle - to keep track of current song subtitle


Song Progress


Then, we can create a song progress bar using ion-range (we don't use ion-progress-bar here because we should be able to drag the progress bar forward & backward).

  • maxRangeValue - maximum value for ion-range

  • currRangeTime - current range value.It changes as song plays on

Here, we have two more variables

  • currSecsText - to display song current time in seconds

  • durationText - to display song total duration in seconds


Buttons


Here, we have four ion-buttons

  • Previous Song Button - to play previous song

  • Play Button - to play the song (*ngIf="!isPlaying" is used.If song is not playing the show this button)

  • Pause Button - to pause the song (*ngIf="isPlaying" is used.If song is playing the show this button)

  • Next Song Button - to play next song


Up Next Song


Here, we have some variables that hold the data about next song

  • upNextImg - to keep track of next song thumbnail

  • upNextTitle - to keep track of next songtitle

  • upNextSubtitle - to keep track of nextsong subtitle


Full Player HTML


Below we have the full player html markup.


songs.page.html

<div class="ion-text-center" id="fullPlayer">
  <!-- Minimize Icon -->
  <ion-toolbar>
    <ion-buttons slot="end">
      <ion-button (click)="minimize()">
         <ion-icon name="chevron-down-outline" slot="icon-only"></ion-icon>
      </ion-button>
    </ion-buttons>
  </ion-toolbar>

  <img src="{{currImage}}" alt="" id="currImg">

  <h2>
    {{currTitle}}
  </h2>
  <p>
    {{currSubtitle}}
  </p>

  <ion-item lines="none">
    <ion-range #range (touchmove)="touchMove()" (touchend)="touchEnd()" (touchstart)="touchStart()"
      max="{{maxRangeValue}}" [(ngModel)]="currRangeTime">

      <ion-label slot="start">
        <ion-text>
          <b>
            {{currSecsText}}
          </b>
        </ion-text>
      </ion-label>

      <ion-label slot="end">
        <ion-text>
          <b>
            {{durationText}}
          </b>
        </ion-text>
      </ion-label>
    </ion-range>
  </ion-item>


  <!-- Play Previous Button -->
  <ion-button fill="clear" mode="ios" (click)="playPrev()">
    <ion-icon name="play-skip-back-outline" style="font-size: 30px;"></ion-icon>
  </ion-button>

  <!-- Play Button -->
  <ion-button fill="clear" mode="ios" *ngIf="!isPlaying" (click)="play()">
    <ion-icon name="play" style="font-size: 40px;"></ion-icon>
  </ion-button>

  <!-- Pause Button -->
  <ion-button fill="clear" mode="ios" *ngIf="isPlaying" (click)="pause()">
    <ion-icon name="pause" style="font-size: 40px;"></ion-icon>
  </ion-button>

  <!-- Play Next Button -->
  <ion-button fill="clear" mode="ios" (click)="playNext()">
    <ion-icon name="play-skip-forward-outline" style="font-size: 30px;"></ion-icon>
  </ion-button>

  <ion-list-header>
    Up Next
  </ion-list-header>
  <!-- Up Next Song -->
  <ion-item>
    <ion-thumbnail slot="start">
      <img src="{{upNextImg}}" />
    </ion-thumbnail>
    <ion-label>
      <h2>
        {{upNextTitle}}
      </h2>
      <p>
        {{upNextSubtitle}}
      </p>
    </ion-label>
  </ion-item>
</div>

Minimize Player UI


This is the content that will be displayed while minimizing the full player view.It contains the same variables and has same buttons as in full player view except it has close button that closes the currently playing song.It uses ion-progress bar to keep the song progress and has progress variable.


songs.page.html

<ion-toolbar id="miniPlayer">
  <ion-item>
    <ion-thumbnail slot="start" (click)="maximize()">
      <img src="{{currImage}}" />
    </ion-thumbnail>
    <ion-label (click)="maximize()">
      <h2>{{currTitle}}</h2>
      <p>{{currSubtitle}}</p>
    </ion-label>

    <!-- Play Button -->
    <ion-button (click)="play()" mode="ios" fill="clear" *ngIf="!isPlaying">
      <ion-icon name="play" style="font-size: x-large;"></ion-icon>
    </ion-button>
    <!-- Pause Button -->
    <ion-button (click)="pause()" mode="ios" fill="clear" *ngIf="isPlaying">
      <ion-icon name="pause" style="font-size: x-large;"></ion-icon>
    </ion-button>
    <!-- Cancel Song Button -->
    <ion-button (click)="cancel()" mode="ios" fill="clear">
      <ion-icon name="close" style="font-size: x-large;"></ion-icon>
    </ion-button>
  </ion-item>

  <!-- Song Progress Bar -->
  <ion-progress-bar value="{{progress}}"></ion-progress-bar>
</ion-toolbar>

CSS Styles


The styles for the full player view and minimize player view.


songs.page.scss

* {
    font-weight: bold;
}

ion-item {
    ion-thumbnail {
        img {
            border-radius: 10px;
        }
    }
    ion-label {
        h2,
        p {
            font-weight: bold;
        }
    }
}

#miniPlayer {
    box-shadow: 0px -4px 10px rgba(0, 0, 0, 0.1);
    border-radius: 10px 10px 0 0;
    transition: 0.5s;
    //hide by default by setting bottom value in negative
    bottom: -100px;
    ion-progress-bar {
        z-index: 10;
    }
}

#fullPlayer {
    transition: 0.5s;
    position: fixed;
    width: 100%;
    height: 100%;
    left: 0;
    background: #fff;
    z-index: 20;
    //Hide by default by setting bottom value in negative
    bottom: -1000px;
    //Current Song Image 
    #currImg {
        margin-top: 30px;
        width: 200px;
        height: 200px;
        border-radius: 10px;
    }
    p {
        color: #8c8c8c;
        margin: 0;
    }   
    ion-text {
        font-size: medium;
    }
}

Scripts


Let's come to the most interesting part Scripting.Import IonRange that will be used to keep track of ion-range value.


songs.page.ts

import { IonRange } from "@ionic/angular";

Declare Variables


Declare all necessary variables.


songs.page.ts

@ViewChild("range", { static: false }) range: IonRange;

 //Current song details
  currTitle:string;
  currSubtitle:string;
  currImage:string;

  //progress bar value
  progress:number = 0;

  //toggle for play/pause button
  isPlaying:booleab = false;

  //track of ion-range touch
  isTouched:boolean = false;

  //ion range texts
  currSecsText:string;
  durationText:string;

  //ion range value
  currRangeTime:number;
  maxRangeValue:number;

  //Current song
  currSong: HTMLAudioElement;

  //Upnext song details
  upNextImg:string;
  upNextTitle:string;
  upNextSubtitle:string;

Format Number to Seconds


Before continue,use the below method to convert a number to seconds ie., convert 59 => 00:59.


songs.page.ts

  sToTime(t) {
    return this.padZero(parseInt(String((t / (60)) % 60))) + ":" +
      this.padZero(parseInt(String((t) % 60)));
  }
  padZero(v) {
    return (v < 10) ? "0" + v : v;
  }

Play the Song


We move to the main part.Here we play a song that is clicked from list of songs in songs.page.html.On clicking on a item, that trigger playSong() method that has song title,subtitle,thumbnail image and path for the song as arguments.


songs.page.ts

playSong(title, subTitle, img, song) {
    if (this.currSong != null) {
      this.currSong.pause();     //If a song plays,stop that
    }

    //open full player view
    document.getElementById("fullPlayer").style.bottom = "0px";
    //set current song details
    this.currTitle = title;
    this.currSubtitle = subTitle;
    this.currImage = img;

    //Current song audio
    this.currSong = new Audio(song);

    this.currSong.play().then(() => {
      //Total song duration
      this.durationText = this.sToTime(this.currSong.duration);
      //set max range value (important to show proress in ion-range)
      this.maxRangeValue = Number(this.currSong.duration.toFixed(2).toString().substring(0, 5));

      //set upnext song
      //get current song index
      var index = this.songs.findIndex(x => x.title == this.currTitle);
      //if current song is the last one then set first song info for upnext song
      if ((index + 1) == this.songs.length) {
        this.upNextImg = this.songs[0].img;
        this.upNextTitle = this.songs[0].title;
        this.upNextSubtitle = this.songs[0].subtitle;
      }

      //else set next song info for upnext song
      else {
        this.upNextImg = this.songs[index + 1].img;
        this.upNextTitle = this.songs[index + 1].title;
        this.upNextSubtitle = this.songs[index + 1].subtitle;
      }
      this.isPlaying = true;
    })

    this.currSong.addEventListener("timeupdate", () => {

      //update some infos as song plays on
      //if ion-range not touched the do update 
      if (!this.isTouched) {

        //update ion-range value
        this.currRangeTime = Number(this.currSong.currentTime.toFixed(2).toString().substring(0, 5));
        //update current seconds text
        this.currSecsText = this.sToTime(this.currSong.currentTime);
        //update progress bar (in miniize view)
        this.progress = (Math.floor(this.currSong.currentTime) / Math.floor(this.currSong.duration));


        //if song ends,play next song
        if (this.currSong.currentTime == this.currSong.duration) {
          this.playNext();
        }
      }
    });
  }

Play Next Song


For playing next song, we get the index of current song from songs array.

We have two conditions

  • If current song is last song of the array, then play first song

  • Otherwise play next song in the list

songs.page.ts

playNext() {
    var index = this.songs.findIndex(x => x.title == this.currTitle);

    if ((index + 1) == this.songs.length) {
      this.playSong(this.songs[0].title, this.songs[0].subtitle, this.songs[0].img, this.songs[0].path);
    }
    else {
      var nextIndex = index + 1;
      this.playSong(this.songs[nextIndex].title, this.songs[nextIndex].subtitle, this.songs[nextIndex].img, this.songs[nextIndex].path);
    }
  }

Play Previous Song


For playing previous song, we get the index of current song from songs array.

We have two conditions

  • If current song is first song of the array, then play last song

  • Otherwise play previous song in the list

songs.page.ts

 playPrev() {
    var index = this.songs.findIndex(x => x.title == this.currTitle);

    if (index == 0) {
      var lastIndex = this.songs.length - 1;
      this.playSong(this.songs[lastIndex].title, this.songs[lastIndex].subtitle, this.songs[lastIndex].img, this.songs[lastIndex].path);
    }
    else {
      var prevIndex = index - 1;
      this.playSong(this.songs[prevIndex].title, this.songs[prevIndex].subtitle, this.songs[prevIndex].img, this.songs[prevIndex].path);
    }
  }

Drag Events


This part contains the code and details about ion-range drag events(drag the ion-range forward and backward and play song from the release point of the ion-range) It includes three methods

  • touchStart() - fires on start touching ion-range

  • touchMove() - fires on moving over ion-range

  • touchEnd() - fires on releasing the touch of ion-range

On start touching, we set isTouched value to true to prevent the updation on seconds text that are updated as song plays in playSong() method.


On dragging the ion-range, we are updating the currSecsText value.


On releasing the ion-range, we update the isTouched value to flase, set current song currentTime to current ion-range value ,currSecsText and currRangeTime value.


songs.page.ts

 touchStart() {
    this.isTouched = true;
    this.currRangeTime = Number(this.range.value);
  }

  touchMove() {
    this.currSecsText = this.sToTime(this.range.value);
  }

  touchEnd() {
    this.isTouched = false;
    this.currSong.currentTime = Number(this.range.value);
    this.currSecsText = this.sToTime(this.currSong.currentTime)
    this.currRangeTime = Number(this.currSong.currentTime.toFixed(2).toString().substring(0, 5));

    if (this.isPlaying) {
      this.currSong.play();
    }
  }

Maximize the player


The method maximize() hides the mini song player if visible and shows full song player.


songs.page.ts

 maximize() {
    document.getElementById("fullPlayer").style.bottom = "0px";
    document.getElementById("miniPlayer").style.bottom = "-100px";
  }

Minimize the player


The method minimize() hides the full song player if visible and shows mini song player.


songs.page.ts

  minimize() {
    document.getElementById("fullPlayer").style.bottom = "-1000px";
    document.getElementById("miniPlayer").style.bottom = "0px";
  }

Pause Play Close


There are three methods

  • pause() - It pauses the currently playing song

  • play() - It plays the currently paused song

  • close() - It hides the player,resets all value and stops the currently playing song

songs.page.ts

  pause() {
    this.currSong.pause();
    this.isPlaying = false;
  }

  play() {
    this.currSong.play();
    this.isPlaying = true;
  }

  cancel() {
    document.getElementById("miniPlayer").style.bottom = "-100px";
    this.currImage = "";
    this.currTitle = "";
    this.currSubtitle = "";
    this.progress = 0;
    this.currSong.pause();
    this.isPlaying = false;
  }

I hope you understand the lesson.Please leave your feedback in comments.Cheers!

Comments


Never Miss a Post. Subscribe Now!

Subscribe Fire Focus to get notified when we post something.Cheers!

Thanks for submitting!

"If you want something you've never had, you must be willing to do something you've never done"

bottom of page