loading...

Music And Video Player With VueJS | Day 2 & 3 [30 Days Of Code]

jordanirabor profile image Jordan Irabor ・8 min read

Hello guys, I've been gone for a bit after writing the first technical article for the first project in my 30 days of code challenge.

I'm back now and I'm writing about the two projects I covered on the second and third days.

These projects may seem a bit rushed because I was quite occupied at the time but I'm going to write about them anyway 🔥

The source code for the music player is available here on Github

The source code for the video player is available here on Github

Building A Basic Music Player With VueJS

VueJS is an awesome JS library, it takes away the stress and simply makes everything cooler to do.

Here's a visual representation of the final results of the music player:

Enough talk, let's start building!

Setup VueJS

For the sake of this tutorial, we will be using the Vue CLI for installation and setup, an alternative would be to reference Vue from a CDN

 https://unpkg.com/vue 

We can install the Vue CLI with this command:

$ npm install --global vue-cli

Next, let's create a new project:

$ vue init webpack-simple vueMusicPlayer

After responding to a few terminal prompts, we would have set everything up.

Let's navigate into the working directory, install dependencies and start our application:

$ cd vueMusicPlayer
$ npm install
$ npm run dev

Defining The Vue Components

For this application, we will be defining two components, the first will display the song's thumbnail and music frame at the left side of the screen while the other component will hold the playlist at the right side of the screen.

First, let's create a components folder in the src directory [ it's not necessary to do this but I like keeping a clean working directory ].

Next, we create the two components in the components directory. We will call them:

musicFrame.vue

playList.vue

The logic behind the components of this music player is:

The root component [ App.vue ] holds both children components [ musicFrame and playList ] and listens for the events they emit from time to time. Whenever a song in the playlist [ to the right ] is clicked on, the root component catches a new event [ also with some data ] and updates the variables that represent the current song playing.

Great!

Building The MusicFrame Component

Let's take a quick look at the template structure for the musicFrame component:

<template>

<div id="musicframe">

<div id="border">

<img v-bind:src="currentImage" width="100%" height="100%"/>

<div id="controls" @click="audioFunction">

<audio ref="audio" id="audio">

<source v-bind:src="currentSong">

Your browser does not support the audio element.

</audio>
<button>Play</button>
<button>Pause</button>

</div>

</div>
</div>

</template>

The musicFrame has a wrapping div tag with the class "musicFrame", within this tag, there is a border div tag that shelters other internal tags.

An <img/> tag sits directly below the border tag, it has the value of its 'src' attribute bound to a currentImage Vue variable [ this variable updates the song's thumbnail whenever a new song is selected ].

Now, for the more obvious part, a div tag with an id of "controls" wraps the HTML5 audio element, it also listens for onclick functions and calls an audioFunction function as a handler.

Cool!

The tag takes a ref [ it will help address the tag specifically ] while the nested tag has a dynamic src attribute to allow dynamic music behavior.

That was pretty tiring to go into detail with, let's see what the innards of the script tag hold.

<script>
import { bus } from '../main';
export default {
  name: 'music-frame',
props : {

 currentSong: {
  type : String
 },
 currentImage: {
  type : String
 }
  },
  data () {
    return {
    }
  },
  methods: {
    audioFunction: function(e){
      let value = e.target.innerText;
      switch(value) {
        case "Play" : {
              console.log(this.$refs.audio.play());
              break;
        }
        case "Pause" : {
              this.$refs.audio.pause();
              break;              
        }
      }
    }
  },
  created(){
    bus.$on('reload', () => {
    this.$refs.audio.load();
    this.$refs.audio.play();
    })
  }
}
</script>

Well, for this bit, it isn't as complicated as it may appear at first. Let's go.

Firstly, we are importing a bus [ this is just an instance of a Vue object defined on the global main.js file ] because we need it to drive events and data between the children components [ musicFrame and playList ].

After exporting default [ creating a reusable module ], we reach out and receive props from the root component [ App.vue ]. In this component, we will not be registering any variable [ it's the reason the data function returns an empty object].

Next, we register the single audioFunction method, this is the method called by the controls div whenever any of its <button/> children are clicked. This method pipes the innerText on the target element through a switch statement and is able to toggle media controls e.g play, pause etc

Lastly, we attach a live cycle hook to this component, this hook fires as soon as the application is created. It's objective is to listen for a reload on the imported bus [ from the playList component ] and play a song whenever that event is caught [ this is the logic that helps to ensure that a new song is played when selected].

That's everything with the musicFrame component.

Building The PlayList Component

Like the previous component, let's begin by examining the template structure:

<template>

  <div id="playlist">

    <div id="secborder" ref="playlist" @click="changeSource">

    <div class="song" url-value="http://sami-server.info/hossein2/95/11/Music/06/Ed%20Sheeran%20-%20Shape%20Of%20You.mp3" picture-value="https://www.billboard.com/files/media/ed-sheeran-july-2017-billboard-1548.jpg"> Ed Sheeran  &nbsp;&nbsp; Shape of you </div>
    <div class="song" url-value="http://jweb.taconic.net/music/linkin_park-faint.mp3" picture-value="https://orig00.deviantart.net/65e3/f/2009/335/6/2/linkin_park__faint_by_jimeye.jpg">Linkin Park &nbsp;&nbsp; Faint</div>
    <div class="song" url-value="http://dl.asra-music.com/foreign%20full%20album/Pink/Pink%20-%20The%20Truth%20About%20Love%20(2012)/04%20-%20Just%20Give%20Me%20A%20Reason.mp3" picture-value="https://i.ytimg.com/vi/3pDj1U6Zd08/maxresdefault.jpg">Pink &nbsp;&nbsp; Just give me a reason</div>
    <div class="song" url-value="http://tegos.kz/new/mp3_full/Clean_Bandit_-_Symphony.mp3" picture-value="https://i.ytimg.com/vi/jtTI-XuW8i8/maxresdefault.jpg"> Clean Bandit &nbsp;&nbsp; Symphony</div>    <div class="song" url-value="http://zaycev-mp3.net/mp3/linkin_park_powerless_[zaycev-mp3.net].mp3" picture-value="https://wallpapercave.com/wp/ufT9VZa.jpg">Linkin Park &nbsp;&nbsp; Powerless</div>
    <div class="song" url-value="http://rm.sina.com.cn/wm/VZ2008072314565310537VK/music/MUSIC0807231507294087.mp3" picture-value="https://i.ytimg.com/vi/oY5BYjsrIHk/maxresdefault.jpg">Linkin Park &nbsp;&nbsp; Somewhere I belong</div>
    <div class="song" url-value="http://s4.faz-dl.xyz/mr-reese/single/july2017/week1/Rihanna%20-%20Wild%20Thoughts%20Solo%20Version%20-%20MP3%20320.mp3" picture-value="http://thetropixs.com/wp-content/uploads/2017/06/Rihanna-Wild.jpg"> Rihanna &nbsp;&nbsp; Wild thoughts</div>
    <div class="song" url-value="http://a.tumblr.com/tumblr_mirhiitqaC1roewk4o1_r1.mp3" picture-value="https://i.ytimg.com/vi/vAsztnDeZ1Q/maxresdefault.jpg">Demi Lovato &nbsp;&nbsp; Heart Attack</div>
    <div class="song" url-value="https://70mack.co/wp-content/upload/2017/10/Davido_FIA.mp3" picture-value="https://3.bp.blogspot.com/-J7w5lyePAfw/WgbF1knkwJI/AAAAAAAAVdI/t2ZOSjmLtssuxuT-czWbETE-hc2DC9wIACK4BGAYYCw/w1200-h630-p-k-no-nu/Davido%2BFia.png"> Davido &nbsp;&nbsp; FIA</div>    
    <div class="song" url-value="http://img0.liveinternet.ru/images/attach/b/3/3656/3656927_eminem__mockingbird.mp3" picture-value="https://wallpapercave.com/wp/WlHnNj6.jpg">Eminem &nbsp;&nbsp; Mocking Bird</div>    

    </div>


  </div>

</template>

Messy stuff. I know.

The template for this component is pretty straightforward, it's just a bunch of <div/> tags holding custom attributes: url-value and picture-value, we need these values to supply the and tags with dynamic data whenever a song [div] in the playlist is clicked.

Next, the <script/> section:

<script>
import { bus } from '../main';
export default {
  name: 'playlist',
  props : {

 currentSong: {
  type : String
 },
 currentImage: {
  type : String
 }
  },
  data () {
    return {
    }
  },

  methods: {
    changeSource : function(e) {
      let songValue = e.target.attributes[1].value;
      let songImage = e.target.attributes[2].value;
      this.$emit('updateData', [songValue, songImage]);
      bus.$emit('reload');
    }
  }
}
</script>

Remember that bus the previous component imported? Yes. That bus, this component has to import it too so there is a medium for communication between the both of them.

In the next steps, we do a few basic things like receiving props from the root component, not declaring any variables [ and it's not because we are too cheap to afford one ].

Lastly, we declare a method, the ChangeSource and its job is quite as its name says:

This method gets the new values [URL for song and image] from the selected song [div] in the playlist, emits an event to the root method [ with the two values earlier retrieved]. This method also emits a 'reload' event using the bus.

Awesome.

And Now For The Root Component

The root component is responsible for coordinating the data that runs between the two children component and making sense out of it. Let's see what this component has under the carpet.

As usual, the template section is examined first:

<template>

<div id="app">

<videoframe v-bind:currentVideo="currentVideo"></videoframe>

<playlist v-bind:currentVideo="currentVideo" @updateData="swapData($event)"></playlist>

</div>

</template>

Unlike the other two components, the root component has its template in a nice, precise and concise fashion. A root div tag with an id "app" wraps the two nested children components.

The children components have two variables bound to their dynamic attributes.On playlist, the root component listens for an "updateData" event and handles it with a "swapData" function.

Nice, precise and concise.

Time for a peek at the script:


<script>
import videoframe from './components/videoFrame'
import playlist from './components/playList'
export default {
  name: 'app',
  components: {
  'videoframe' : videoframe,
  'playlist' : playlist
},
  data () {
    return {
      currentVideo : ''
    }
  },
  methods: {
    swapData: function(data) {
     this.currentVideo = data;
    }
  }
}
</script>

First, we import musicFrame and playList [obviously because we need them], then we register them as components.

We define two key/value pairs in the data function, the first one will hold data for the current song playing, while the other will at first hold a default image banner for the musicFrame banner and alter adopt thumbnails dynamically.

Lastly, we define a single method called swapData, this method receives the data emitted in an event from a child component child and toggles values dynamically.

Awesome 🔥

That's all for the music player app, now it's time for the video player.

Here's a visual representation of the music player application we will be creating:

I would also go deep into explaining the video player application but the truth is, it is really similar to the music player application in logic, so there really isn't any need.

Conclusion

Awesome! 🔥 That is all it takes to build a music player and video player in VueJS.

I may have left a bit of the actual code out this article for brevity purposes.

To run these projects locally, type in the following commands in a terminal.

-- clone the repository --

git clone https://github.com/Jordanirabor/vueMusicPlayer

OR

git clone https://github.com/Jordanirabor/vueVideoPlayer


-- navigate into the directory --

cd vueMusicPlayer

OR

cd vueVideoPlayer

-- install dependencies --

npm install 

-- serve with hot reload at localhost:8080 --

npm run dev

-- build for production with minification --

npm run build

I'll be building and writing about another project tomorrow, stay tuned 🔥🔥🔥

Posted on by:

jordanirabor profile

Jordan Irabor

@jordanirabor

A software engineer sitting in front of two desks and a mechanical keyboard. I build software at [Stears]('https://stearsng.com') and write tutorials for Pusher and LogRocket.

Discussion

markdown guide
 

Awesome! Keep up the good work. How does it feel to go back and reassess what you wrote?

 

It feels incredible! More importantly, it helps me find new solutions to already solved problems.