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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 ∘ 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 🔥🔥🔥
Top comments (2)
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.