你将要创建的内容
使用vue.js构建应用程序 简单、有趣且令人愉快。您可以毫不费力地构建一个工作应用程序。为了证明这一点,今天我将向您展示构建自己的全功能音乐播放器是多么容易。为了让事情变得更简单,我们将使用Vuetify.js,一个 Vue.js 驱动的 UI 库,它将加快 UI 构建。我几乎能感觉到你的不耐烦,所以让我们开始吧。
您可以在GitHub 存储库中找到完整的源代码。这是工作演示。要学习本教程,您应该熟悉 Vue 组件、 Vue 单文件组件和 ES2015 语法。
规划应用程序
每个创作都始于一个想法,至少是一些基本的计划。所以首先我们需要决定我们想要构建什么以及我们想要实现什么功能。都说一张图抵得上千言万语,那么我们先从音乐播放器的简单草图说起。
我制作了这个wiref ram e,这样你就可以大致了解我们想要构建的UI。下一步是描述我们打算实现的功能。
正如约翰约翰逊所说:
首先,解决问题。然后,编写代码。
我们将使用它作为 wis dom的来源,并在开始编写应用程序之前对其进行规划。
应用组件
Vue.js 是一个基于组件的框架。因此,我们首先需要将应用程序拆分为单独的组件(在我们的例子中为五个,如上图所示),并概述每个组件的特性和功能。
标题栏
该组件将包含以下部分:
左侧的菜单
中心的应用程序名称
右侧三个静态图标
信息面板
该组件将显示有关当前播放曲目的基本信息:
曲目的艺术家和标题在左侧
当前曲目在右侧的位置和持续时间
控制条
该组件将包含两个栏,其中将包含操作播放器播放列表中的音轨所需的所有控件。
左侧带有图标的音量滑块(其外观将根据音量和静音时发生变化),右侧为音量百分比
用于播放、暂停、停止和跳过曲目的按钮。
最右边的两个按钮:一个用于重复当前曲目,一个用于随机播放曲目的顺序
一个显示当前播放曲目位置的搜索栏,可以通过在栏上单击鼠标来更改它
播放列表面板
该组件将包含具有以下功能的曲目播放列表:
显示具有正确编号、艺术家、标题和持续时间属性的曲目
单击即可选择曲目
双击播放曲目
搜索栏
当我们想要查找和播放特定曲目时,该组件将提供搜索功能。
当然,上面的大纲不能涵盖所有的细节和细微差别,这完全没问题。现在,我们对最终产品有一个整体的了解就足够了。我们将在构建过程中处理所有细节和最终挑战。
因此,让我们进入有趣的部分并编写一些代码!
入门
Vuetify 的快速入门页面 提供了很多帮助您入门的选项。我们将使用一个名为webpack Simple的预制 Vue CLI 模板。在要用于此项目的目录中运行以下命令:
首先,安装 Vue CLI:
$ npm install -g vue-cli
然后,创建应用程序:
$ vue init vuetifyjs/ webpack -simple vue-music-player
接下来,转到应用程序的目录并安装所有依赖项:
$ cd vue-music player $ npm install
我们将使用 Howler.js (一个javascript音频库)来处理音乐播放器的音频部分。所以我们也需要将它包含在项目中。运行以下命令:
$ npm install --save howler
最后,运行应用程序:
$ npm run dev
该应用程序将localhost:8080
在您的默认浏览器中打开。您应该会看到一个简单的 Vuetify 应用程序框架。
调整模板
为了适应我们的需要,我们需要清理模板并稍微调整一下。将App.vue文件重命名为Player.vue,打开它,删除里面的所有内容,然后添加以下内容:
<template> <v-app dark> <v-content> <v-container> <!-- The player components go here --> </v-container> </v-content> </v-app> </template> <script> export default { data () { return { } } } </script>
我们将音乐播放器应用程序包装在v-app
组件中,这是应用程序正常工作所必需的。我们还传递了dark
道具,以应用 Vuetify 深色主题。
现在,打开main.js文件,删除原始内容,并添加以下内容:
import Vue from 'vue' import Vuetify from 'vuetify' import 'vuetify/dist/vuetify.css' import Player from './Player.vue' import {Howl, Howler} from 'howler' Vue.use(Vuetify) new Vue({ el: '#app', render: h => h(Player) })
另外,打开index.html文件并将<title>
标签的内容更改为Vue Music Player。
现在,在您的浏览器中,您应该会看到一个空白的深色页面。瞧。您已准备好开始创建。
在开始编码之前,很高兴知道 Vuetify 为主要代码编辑器提供代码片段和自动完成功能:VS Code、Atom 和 Sublime。要获取片段,请在您喜欢的编辑器中搜索扩展程序(vuetify-vscode
、 或vuetify-atom
、 或vuetify-sublime
)。
构建标题栏组件
在src目录中,创建一个新的组件文件夹。然后,在该文件夹中,创建具有以下内容 的PlayerTitleBar.vue文件:
<template> <v-system-bar window> <v-menu offset-y transition="slide-y-transition"> <v-btn flat small right slot="activator"> <v-icon>headset</v-icon> MENU </v-btn> <v-list> <v-list-tile @click="dialog = true"> <v-list-tile-title>About</v-list-tile-title> </v-list-tile> <v-dialog v-model="dialog" max-width="300"> <v-card> <v-card-title><h2>Vue Music Player</h2></v-card-title> <v-card-text>Version 1.0.0</v-card-text> <v-card-actions> <v-spacer></v-spacer> <v-btn flat @click="dialog = false">OK</v-btn> </v-card-actions> </v-card> </v-dialog> </v-list> </v-menu> <v-spacer></v-spacer> VUE MUSIC PLAYER <v-spacer></v-spacer> <v-icon>remove</v-icon> <v-icon>check_box_outline_blank</v-icon> <v-icon>close</v-icon> </v-system-bar> </template> <script> export default { data () { return { dialog: false } }, } </script>
在这里,我们使用以下 Vuetify 组件:工具栏、菜单、按钮、图标、列表、对话框和卡片。
我们将菜单、名称和图标与<v-spacer>
组件分开。为了显示或隐藏对话框,我们创建了 dialog: false
data 属性。当我们单击About菜单项时,它的值将切换。
现在,在Player.vue 文件中,导入标题栏组件,将其注册到 components 对象中,并将其添加到模板中。
<template> <v-app dark> <v-content> <v-container> <player-title-bar></player-title-bar> // ADD the component in the template </v-container> </v-content> </v-app> </template> <script> import PlayerTitleBar from './components/PlayerTitleBar.vue' // IMPORT the component export default { components: { PlayerTitleBar // REGISTER the component }, data () { return { } } } </script>
现在,在浏览器中检查结果。您应该看到以下内容:
我们将对其他四个组件重复这三个步骤。因此,在接下来的部分中,当我告诉您在模板中导入、注册和添加组件时,您应该遵循此处描述的相同过程。
构建播放列表组件
在根目录中,新建一个播放列表文件夹并添加您要播放的音频文件。文件名必须在单词之间加上下划线并在末尾加上.mp3 扩展名,例如,Remember_the_Way.mp3。现在,在Player.vue的数据对象 中创建一个音轨数组 :
playlist: [ {title: "Streets of Sant'Ivo", artist: "Ask Again", howl: null, display: true}, {title: "Remember the Way", artist: "Ask Again", howl: null, display: true}, ... ]
每个轨道都有 title
和artist
属性,一个howl
对象设置为null
,一个 display
属性设置为true
。
display
当我们实现搜索功能时将使用该属性。现在它被设置true
为所有轨道,所以它们都是可见的。
Howler 将音频文件包装在一个howl
对象中。我们设置howl
为null
因为我们将在创建 Vue 实例时动态填充它。为此,我们使用了 Vue 的created
生命周期钩子。
created: function () { this.playlist.forEach( (track) => { let file = track.title.replace(/\s/g, "_") track.howl = new Howl({ src: [`./playlist/${file}.mp3`] }) }) }
Howl
这将为播放列表中的每个曲目设置一个新对象。
现在,创建PlayerPlaylistPanel.vue 组件并将其添加到内部:
<template> <v-card height="330"> <v-list> <v-list-tile v-for="(track, index) in playlist" :key="track.title" v-show="track.display"> <v-list-tile-content> <v-list-tile-title>{{ index }} {{ track.artist }} - {{ track.title }}</v-list-tile-title> </v-list-tile-content> <v-spacer></v-spacer> {{ track.howl.duration() }} </v-list-tile> </v-list> </v-card> </template> <script> export default { props: { playlist: Array } } </script>
playlist
首先,我们从Player.vue 文件中传递道具。接下来,在模板中,我们使用v-for
指令遍历每个曲目并显示曲目的索引,然后是曲目的艺术家和标题,以及最右侧的曲目持续时间。我们还使用v-show
绑定到display
属性。display
只有当是 时,轨道才会可见 true
。
现在,在Player.vue 文件中,我们在模板中导入、注册和添加播放列表组件。然后,我们将playlist
prop 绑定到playlist
data 属性,如下所示 <player-playlist-panel :playlist="playlist"></player-playlist-panel>
:
让我们在浏览器中检查结果:
这里有两个问题。首先,曲目的数量不正确,其次,曲目的持续时间以毫秒为单位显示,但我们希望它以分钟为单位。我们将通过创建格式过滤器来解决每个问题。
在main.js文件中,创建一个 numbers
过滤器 和一个 minutes
过滤器,它们将可全局访问。接下来,在 PlayerPlaylistPanel.vue中,我们像这样使用它们: {{ index | numbers }}
和{{ track.howl.duration() | minutes }}
.
现在,如果您检查应用程序,一切都应该正确显示。
使曲目可选
在Player.vue 文件中,添加selectedTrack: null
data 属性并将其绑定到播放列表组件 ( :selectedTrack="selectedTrack"
)。然后,我们在 PlayerPlaylistPanel.vue 文件 ( selectedTrack: Object
) 中传递道具。
我们还添加了一个点击事件监听器 <v-list-tile-content @click="selectTrack(track)">
,然后创建selectTrack()
方法:
methods: { selectTrack (track) { this.$emit('selecttrack', track) } }
现在,回到 Player.vue
,将selecttrack
事件添加到播放列表组件 ( @selecttrack="selectTrack"
) 并创建 selectTrack()
方法:
selectTrack (track) { this.selectedTrack = track }
现在,如果您转到播放列表并单击一个曲目,它将被选中。我们看不到它,但我们可以在 Vue DevTools 中证明它。在以下屏幕截图中,选择了第二个轨道:
行和选择样式
下一步是使选择可见。为此,我们将绑定一个类,该类将所选轨道着色为橙色,另一个类将使行更暗以使轨道更易于区分。在v-show
指令后添加以下内容:
:class="[{selected: track === selectedTrack}, {even: index % 2 == 0}]"
我们还将添加另一个类,当列表变得太大时,它将显示一个滚动条。
<v-card height="330" :class="{playlist}">
我们在文件末尾添加必要的类。
<style scoped> .selected { background-color: orange !important; } .even { background-color: #505050 } .playlist { overflow: auto } </style>
就是这样。现在,所选曲目以橙色突出显示。
我们将在下一节末尾添加双击播放功能。
构建播放器控件组件
现在让我们创建播放器控件。我们将从播放、暂停和停止按钮开始。
添加播放、暂停和停止按钮
创建PlayerControlsBars.vue 组件并将其添加到里面:
<template> <div> <v-toolbar flat height=90> <v-spacer></v-spacer> <v-btn outline fab small color="light-blue" @click="stopTrack"> <v-icon>stop</v-icon> </v-btn> <v-btn outline fab color="light-blue" @click="playTrack()"> <v-icon large>play_arrow</v-icon> </v-btn> <v-btn outline fab small color="light-blue" @click="pauseTrack"> <v-icon>pause</v-icon> </v-btn> <v-spacer></v-spacer> </v-toolbar> </div> </template>
在这里,我们使用 Vuetify工具栏 组件。
三个按钮都注册了点击事件监听器。让我们为它们创建方法:
methods: { playTrack(index) { this.$emit('playtrack', index) }, pauseTrack() { this.$emit('pausetrack') }, stopTrack() { this.$emit('stoptrack') } }
现在,在 Player.vue 文件中,导入、注册并在模板中添加组件。然后,注册事件***器 ( @playtrack="play"
, @pausetrack="pause"
, @stoptrack="stop"
)。
接下来,创建 index: 0
数据属性,它将保存当前轨道的索引。然后,创建一个计算currentTrack()
:
computed: { currentTrack () { return this.playlist[this.index] } }
现在我们可以开始创建play
、pause
和stop
方法了。我们将从play()
方法开始,但在此之前我们需要创建 playing: false
data 属性,它将指示曲目是否正在播放。为方法添加以下代码play()
:
play (index) { let selectedTrackIndex = this.playlist.findIndex(track => track === this.selectedTrack) if (typeof index === 'number') { index = index } else if (this.selectedTrack) { if (this.selectedTrack != this.currentTrack) { this.stop() } index = selectedTrackIndex } else { index = this.index } let track = this.playlist[index].howl if (track.playing()) { return } else { track.play() } this.selectedTrack = this.playlist[index] this.playing = true this.index = index }
该方法以索引为参数,指定要播放的曲目。首先,我们获取所选曲目的索引。然后,我们进行一些检查以确定index
. 如果索引作为参数提供并且它是一个数字,那么我们使用它。如果选择了曲目,我们将使用所选曲目的索引。如果所选曲目与当前曲目不同,我们使用该stop()
方法停止当前曲目。最后,如果既没有传递索引参数也没有选择轨道,我们使用index
data 属性的值。
接下来,我们获取音轨的啸叫(基于索引值)并检查它是否正在播放。如果是,我们什么也不返回;如果不是,我们就玩它。
最后,我们更新selectedTrack
,playing
和index
data 属性。
现在让我们创建pause()
andstop()
方法。
pause () { this.currentTrack.howl.pause() this.playing = false }, stop () { this.currentTrack.howl.stop() this.playing = false }
在这里,我们只是暂停或停止当前轨道并更新playing
数据属性。
让我们也让一首曲目在双击时开始播放。
@dblclick="playTrack()"
在PlayerPlaylistPanel.vue<v-list-tile-content>
中 添加 并创建方法:playTrack()
playTrack(index) { this.$emit('playtrack', index) }
@playtrack="play"
在Player.vue文件中注册监听 器,瞧。
添加上一个和下一个按钮
现在让我们添加上一个和下一个按钮。
<v-btn outline fab small color="light-blue" @click="skipTrack('prev')"> <v-icon>skip_previous</v-icon> </v-btn> <!-- stop, play, and pause buttons are here --> <v-btn outline fab small color="light-blue" @click="skipTrack('next')"> <v-icon>skip_next</v-icon> </v-btn>
创建 skipTrack()
方法:
skipTrack (direction) { this.$emit('skiptrack', direction) }
在Player.vue中注册事件监听器 ( @skiptrack="skip"
) 。
并创建skip()
方法:
skip (direction) { let index = 0 if (direction === "next") { index = this.index + 1 if (index >= this.playlist.length) { index = 0 } } else { index = this.index - 1 if (index < 0) { index = this.playlist.length - 1 } } this.skipTo(index) }, skipTo (index) { if (this.currentTrack) { this.currentTrack.howl.stop() } this.play(index) }
我们首先检查方向是否为next
。如果是这样,我们将索引增加 1。如果索引大于数组中的最后一个索引,那么我们从零重新开始。当方向为prev
时,我们将索引减 1。如果索引小于零,则使用最后一个索引。最后,我们将index
用作该skipTo()
方法的参数。它停止当前曲目并播放下一个或上一个。
以下是玩家使用按钮时的外观:
添加音量滑块
在所有按钮之前添加以下内容:
<v-slider v-model="volume" @input="updateVolume(volume)" max="1" step="0.1"></v-slider>
在这里,我们使用 Vuetify滑块 组件。
添加 volume: 0.5
数据属性,然后创建updateVolume()
方法:
updateVolume (volume) { Howler.volume(volume) }
在这里,我们使用全局 Howler 对象来全局设置所有嚎叫的音量。
此外,我们需要将初始咆哮音量(默认设置为 1)同步到volume
属性。如果您不这样做,音量将显示 0.5,但最初为 1。为此,我们将created
再次使用钩子:
created: function () { Howler.volume(this.volume) }
我们希望在音量滑块右侧以百分比形式查看音量级别,因此我们将其添加到模板中: {{this.volume * 100 + '%'}}
添加静音按钮
现在,我们在滑块前添加一个音量图标。
<v-btn flat icon @click="toggleMute"> <template v-if="!this.muted"> <v-icon v-if="this.volume >= 0.5">volume_up</v-icon> <v-icon v-else-if="this.volume > 0">volume_down</v-icon> <v-icon v-else>volume_mute</v-icon> </template> <v-icon v-show="this.muted">volume_off</v-icon> </v-btn>
图标将根据volume
和muted
属性的值而变化。
添加 muted: false
数据属性并创建toggleMute()
方法:
toggleMute () { Howler.mute(!this.muted) this.muted = !this.muted }
我们再次使用全局 Howler 对象来全局设置静音,然后我们切换muted
值。
在下面的屏幕截图中,您可以看到音量滑块的外观:
添加重复按钮
在所有按钮之后添加以下内容:
<v-btn flat icon @click="toggleLoop"> <v-icon color="light-blue" v-if="this.loop">repeat_one</v-icon> <v-icon color="blue-grey" v-else>repeat_one</v-icon></v-btn>
在Player.vue中添加 loop: false
属性 ,绑定它并在PlayerControlsBars.vue中 传递 prop( ) 。 :loop="loop"
loop: Boolean
现在,让我们创建toggleLoop()
方法:
toggleLoop () { this.$emit('toggleloop', !this.loop) }
现在,回到 Player.vue,注册事件监听器 ( @toggleloop="toggleLoop"
) 并创建toggleLoop()
方法:
toggleLoop (value) { this.loop = value }
在这一点上,我们面临一个小问题。当轨道寻找终点时,它就停止了。播放器不会移动到下一个曲目,也不会重复当前曲目。为了解决这个问题,我们需要created
在属性之后添加以下内容src
:
onend: () => { if (this.loop) { this.play(this.index) } else { this.skip('next') } }
现在,当loop
打开时,将重复当前曲目。如果它关闭,播放器将移动到下一首曲目。
添加随机播放按钮
在重复按钮后添加以下内容:
<v-btn flat icon @click="toggleShuffle"> <v-icon color="light-blue" v-if="this.shuffle">shuffle</v-icon> <v-icon color="blue-grey" v-else>shuffle</v-icon> </v-btn>
在中添加shuffle: false
属性 Player.vue
,绑定它(:shuffle="shuffle"
),并在中传递道具(shuffle: Boolean
) PlayerControlsBars.vue
。
现在,让我们创建toggleShuffle()
方法;
toggleShuffle () { this.$emit('toggleshuffle', !this.shuffle) }
现在,回到 Player.vue,注册事件监听器 ( @toggleshuffle="toggleShuffle"
) 并创建toggleShuffle()
方法:
toggleShuffle (value) { this.shuffle = value }
现在,将以下内容添加到skip()
方法之后 index = 0
:
lastIndex = this.playlist.length - 1 if (this.shuffle) { index = Math.round(Math.random() * lastIndex) while (index === this.index) { index = Math.round(Math.random() * lastIndex) } } else if (direction === "next") { ...
这是您的应用程序现在的外观:
添加搜索栏
首先,在 Player.vue中,创建 seek: 0
属性。然后我们需要观察 playing
属性以更新搜索。
watch: { playing(playing) { this.seek = this.currentTrack.howl.seek() let updateSeek if (playing) { updateSeek = setInterval(() => { this.seek = this.currentTrack.howl.seek() }, 250) } else { clearInterval(updateSeek) } }, }
这将每秒更新四次搜索值。
现在,创建一个计算progress()
:
progress () { if (this.currentTrack.howl.duration() === 0) return 0 return this.seek / this.currentTrack.howl.duration() }
:progress="progress"
在模板中 绑定它 ( )。
现在,在 PlayerControlsBars.vue中,传递progress
prop ( progress: Number
) 并在我们已经创建的工具栏下方添加另一个工具栏:
<v-toolbar flat height="40"> <v-progress-linear height="40" v-model="trackProgress" @click="updateSeek($event)"></v-progress-linear> </v-toolbar>
在这里,我们使用 Vuetify 进度 组件。
创建一个 computed trackProgress()
,它将以百分比形式获取轨道的进度。
computed: { trackProgress () { return this.progress * 100 }, }
现在,创建updateSeek()
方法:
updateSeek (event) { let el = document.queryselector(".progress-linear__bar"), mousePos = event.offsetX, elWidth = el.clientWidth, percents = (mousePos / elWidth) * 100 this.$emit('updateseek', percents) }
在这里,我们得到了使用.progress-linear__bar
类的进度条元素。我在浏览器开发工具中找到了这个。接下来,我们获取鼠标位置和栏的宽度。然后,我们以百分比形式获得鼠标点击位置。
回到 Player.vue,添加并注册事件监听器 ( @updateseek="setSeek"
) 并创建setSeek()
方法:
setSeek (percents) { let track = this.currentTrack.howl if (track.playing()) { track.seek((track.duration() / 100) * percents) } }
和繁荣!您可以使用鼠标更改播放曲目的位置。
构建信息面板组件
创建 具有以下内容的PlayerInfoPanel.vue文件:
<template> <v-card height="60"> <v-card-title> <h2>{{ trackInfo.artist }} - {{ trackInfo.title }}</h2> <v-spacer></v-spacer> <h3>{{trackInfo.seek | minutes}}/{{trackInfo.duration | minutes}}</h3> </v-card-title> </v-card> </template> <script> export default { props: { trackInfo: Object }, } </script>
在这里,我们传递了一个 prop trackInfo
,我们用它来填充我们组件中的轨道信息。
现在,回到 Player.vue,在模板中导入、注册和添加组件。
然后,创建一个计算getTrackInfo()
:
getTrackInfo () { let artist = this.currentTrack.artist, title = this.currentTrack.title, seek = this.seek, duration = this.currentTrack.howl.duration() return { artist, title, seek, duration, } }
接下来,我们将它绑定到模板 ( :trackInfo="getTrackInfo"
) 中,瞧。我们获得了当前播放曲目的一些基本信息,如下面的屏幕截图所示。
构建搜索栏组件
创建 具有以下内容 的PlayerSearchBar.vue文件:
<template> <v-toolbar flat> <v-text-field clearable prepend-icon="search" placeholder="Quick search" v-model="searchString" @input="searchPlaylist"> </v-text-field> <v-spacer></v-spacer> </v-toolbar> </template> <script> export default { props: { playlist: Array }, data () { return { searchString: "", } }, methods: { searchPlaylist () { this.playlist.forEach((track) => { if (this.searchString) { if (!track.title.toLowerCase().includes(this.searchString.toLowerCase()) && !track.artist.toLowerCase().includes(this.searchString.toLowerCase())) { track.display = false } else { track.display = true } } else if (this.searchString === "" || this.searchString === null) { track.display = true } }) } }, } </script>
我们创建一个文本字段 并添加 clearable
道具以在我们输入内容时显示清除图标。
通过使用v-model
,我们将它绑定到searchString
,它最初是一个空字符串。我们添加了一个输入事件监听器。
我们还传递了我们在 方法playlist
中使用的道具。searchPlaylist()
在此方法中,我们使用该display
属性并off
为标题或艺术家与搜索字符串不匹配的每个曲目打开它,我们保留它或on
为所有匹配项打开它。最后,如果搜索字符串为空或等于null
,当我们使用清除按钮清除字段时发生这种情况,我们on
为display
所有曲目转动 。
现在,回到Player.vue,在模板中导入、注册和添加组件。
绑定播放列表属性 ( :playlist="playlist"
) 并检查功能。以下是它的实际效果:
一些改进想法
如您所见,有了明确的目标和适当的计划,构建 Vue/Vuetify 应用程序可以非常轻松和愉快。您现在拥有一个可以在放松或编码时间使用的音乐播放器。当然,总有进一步改进和添加的空间,因此您可以尝试以下一些想法,以使播放器功能更加丰富:
多个播放列表支持
从播放列表中添加或删除曲目的能力
拖放支持
对曲目进行排序的能力
音频可视化
结论
在本教程中,我们看到了使用 Vue.js,尤其是 Vuetify.js 构建应用程序是多么容易和愉快。我希望你和我一样喜欢构建这个播放器。我很高兴看到您自己改进的播放器版本。因此,如果您创建了一个,只需在评论中放一个演示链接!
- 应用组件
- 标题栏
- 信息面板
- 控制条
- 播放列表面板
- 搜索栏
- 调整模板
- 使曲目可选
- 行和选择样式
- 添加播放、暂停和停止按钮
- 添加上一个和下一个按钮
- 添加音量滑块
- 添加静音按钮
- 添加重复按钮
- 添加随机播放按钮
- 添加搜索栏