Xây dựng Trình Chơi nhạc với Vuetify
Vietnamese (Tiếng Việt) translation by Thai An (you can also view the original English article)



Xây dựng ứng dụng với Vue.js thật dễ dàng, vui và thú vị. Bạn có thể xây dựng một ứng dụng hoạt động với nỗ lực tối thiểu. Để chứng minh điều đó, hôm nay tôi sẽ chỉ cho bạn cách xây dựng trình phát nhạc của riêng bạn với đầy đủ tính năng dễ dàng đến thế nào. Để khiến mọi thứ trở nên dễ dàng hơn, chúng tôi sẽ sử dụng Vuetify.js, thư viện UI được Vue.js hỗ trợ, sẽ tăng tốc độ xây dựng UI. Tôi gần như có thể cảm thấy sự thiếu kiên nhẫn của bạn, vì vậy chúng ta hãy bắt đầu.
Bạn có thể tìm thấy toàn bộ mã nguồn trên GitHub repo. Và đây là ví dụ minh hoạ. Để theo dõi hướng dẫn này, bạn nên quen thuộc với Vue component, Vue single file components và cú pháp ES2015.
Lập kế hoạch cho ứng dụng
Mỗi sáng tạo bắt đầu từ một ý tưởng và ít nhất là kế hoạch cơ bản. Vậy trước tiên ta cần quyết định xem ta muốn xây dựng cái gì và chức năng nào cần triển khai. Hình ảnh đáng giá bằng cả ngàn lời nói, vì thế hãy bắt đầu bằng một phác thảo đơn giản của trình phát nhạc.



Tôi thực hiện bản wireframe (minh hoạ mẫu) để bạn có thể có khái niệm chung về UI chúng ta muốn xây dựng. Bước kế tiếp là mô tả chức năng chúng ta dự định triển khai.
Như John Johnson nói:
Đầu tiên hãy giải quyết vấn đề. Sau đó viết code.
Chúng ta sẽ dùng nó làm kim chỉ nam, và sẽ lên kế hoạch cho ứng dụng trước khi ta bắt đầu code.
Bổ sung component
Vue.js là một framework lấy component làm cơ sở. Vì thế trước tiên ta cần chia ứng dụng thành những component cá thể (trong trường hợp của ta là 5, như bản phác thảo bên trên), và để bố trí các tính năng nổi bật và chức năng cho mỗi component.
Thanh tiêu đề
Thành phần này sẽ chứa các phần sau:
- một menu ở phía bên trái
- tên ứng dụng ở trung tâm
- ba biểu tượng tĩnh ở phía bên phải
Bảng thông tin
Thành phần này sẽ hiển thị thông tin cơ bản về bản nhạc hiện đang được phát:
- nghệ sĩ và tiêu đề của bản nhạc ở bên trái
- vị trí và thời lượng của bản nhạc hiện tại ở bên phải
Thanh điều khiển
Component này sẽ gồm hai thanh, chúng sẽ gồm các bộ điều khiển cần thiết để xử lý các bản thu âm thanh trong danh sách phát nhạc.
- Một thanh trượt điều chỉnh âm lượng cùng một biểu tượng bên trái (diện mạo của nó sẽ theo đổi tuỳ theo mức độ âm lượng và khi âm thanh bị tắt đi) và phần trăm âm lương ở bên phải.
- các nút để phát nhạc, tạm dừng, ngưng phát, và bỏ qua bài nhạc.
- hai nút ở phía xa bên phải: một dùng để phát lại bản nhạc hiện thời, và một dùng để phát nhạc ngẫu nhiên không theo thứ tự.
- một thanh tìm kiếm hiển thị vị trí của bản nhạc đang được phát, với khả năng thay đổi chỉ với 1 cú nhấp chuột.
Bảng danh sách nhạc
Component này sẽ chứa danh sách bản nhạc với chức năng sau đây:
- hiển thị một bản nhạc với con số phù hợp, nhạc sĩ, tên bài hát, và các thuộc tính thời lượng
- chọn một bản nhạc với một cú click
- phát bản nhạc bằng click đúp.
Thanh tìm kiếm
Component này sẽ đề xuất chức năng tìm kiếm trong những trường hợp chúng tôi muốn tìm và chơi các bản nhạc cụ thể.
Dĩ nhiên, bố trí bên trên không thể bao trùm tất cả chi tiết và sắc thái, và điều này hoàn toàn ổn. Ngay lúc này, thế này đã đủ cho chúng ta để có bức tranh tổng thể của sản phẩm sau cùng. Chúng ta sẽ nghiên cứu tất cả chi tiết và các thách thức trong suốt quá trình xây dựng.
Vậy hãy đi vào phần thú vị và viết code nào!
Bắt đầu
Trang khởi đầu nhanh của Vuetify đề xuất nhiều lựa chọn giúp bạn bắt đầu. Chúng ta sẽ dùng một trong số các template tạo sẵn của Vue CLI gọi là Webpack Simple. Chạy các lệnh sau trong thư mục bạn muốn sử dụng cho dự án này:
Đầu tiên, cài đặt Vue CLI:
1 |
$ npm install -g vue-cli |
Sau đó tạo ứng dụng:
1 |
$ vue init vuetifyjs/webpack-simple vue-music-player
|
Tiếp theo, đến thư mục của ứng dụng và cài đặt các phụ thuộc:
1 |
$ cd vue-music player |
2 |
$ npm install |
Ta sẽ dùng Howler.js (một thư viện âm thanh của JavaScript) để xử lý phần âm thanh của trình phát nhạc. Vì thế chúng ta cũng cần bao gồm nó vào dự án. Chạy lênh dưới đây
1 |
$ npm install --save howler |
Và sau cùng, chạy ứng dụng:
1 |
$ npm run dev
|
Ứng dụng sẽ chạy trên localhost:8080 trong trình duyệt mặc định của bạn. Bạn sẽ nhìn thấy cấu trúc căn bản của ứng dụng Vuetify đơn giản:
Điều chỉnh template
Để điều chỉnh theo nhu cầu của chúng ta, ta cần dọn sách template và điều chỉnh một chút. Đặt tên file App.vue thành Player.vue, mở ra và xoá mọi thức bên trong, và bổ sung phần sau đây thay thế:
1 |
<template>
|
2 |
<v-app dark> |
3 |
<v-content>
|
4 |
<v-container>
|
5 |
<!-- The player components go here -->
|
6 |
</v-container>
|
7 |
</v-content>
|
8 |
</v-app>
|
9 |
</template>
|
10 |
|
11 |
<script>
|
12 |
export default { |
13 |
data () { |
14 |
return { |
15 |
|
16 |
}
|
17 |
}
|
18 |
}
|
19 |
</script>
|
Chúng ta bao bọc ứng dụng trình phát nhạc trong component v-app, điều này giúp ứng dụng hoạt động trơn tru. Chúng ta cũng đưa prop dark vào, để áp dụng cho theme Vuetify tông tối.
Giờ mở file main.js, xoá nội dung cũ, và bổ sung nội dung sau:
1 |
import Vue from 'vue' |
2 |
import Vuetify from 'vuetify' |
3 |
import 'vuetify/dist/vuetify.css' |
4 |
import Player from './Player.vue' |
5 |
|
6 |
import {Howl, Howler} from 'howler' |
7 |
|
8 |
Vue.use(Vuetify) |
9 |
|
10 |
new Vue({ |
11 |
el: '#app', |
12 |
render: h => h(Player) |
13 |
})
|
Mở file index.html và thay đổi nội dung thẻ <title> thành Vue Music Player. thành Vue Music Player.
Bây giờ trong trình duyệt, bạn sẽ thấy một trang trống màu tối. Và tuyệt, bạn đã sẵn sàng tạo ứng dụng rồi.
Trước khi bạn bằt đầu viết mã, bạn nên biết rằng Vuetify đề xuất các đoạn mã và autocompletion (tự hoàn thành code) cho những trình biên tập mã lớn: VS Code, Atom và Sublime. Để lấy những đoạn mã có sẵn, tìm kiếm phần mở rộng cho trình biên tập ưa thích của bạn (vuetify-vscode, hoặc vuetify-atom, hoặc vuetify-sublime).
Xây dựng component cho thanh tiêu đề
Trong thư mục src, tạo thư mục mới components. Trong thư mục mới này, tạo file PlayerTitleBar.vue với nội dung sau đây:
1 |
<template>
|
2 |
<v-system-bar window> |
3 |
<v-menu offset-y transition="slide-y-transition"> |
4 |
<v-btn flat small right slot="activator"> |
5 |
<v-icon>headset</v-icon> MENU |
6 |
</v-btn>
|
7 |
<v-list>
|
8 |
<v-list-tile @click="dialog = true"> |
9 |
<v-list-tile-title>About</v-list-tile-title> |
10 |
</v-list-tile>
|
11 |
<v-dialog v-model="dialog" max-width="300"> |
12 |
<v-card>
|
13 |
<v-card-title><h2>Vue Music Player</h2></v-card-title> |
14 |
<v-card-text>Version 1.0.0</v-card-text> |
15 |
<v-card-actions>
|
16 |
<v-spacer></v-spacer>
|
17 |
<v-btn flat @click="dialog = false">OK</v-btn> |
18 |
</v-card-actions>
|
19 |
</v-card>
|
20 |
</v-dialog>
|
21 |
</v-list>
|
22 |
</v-menu>
|
23 |
<v-spacer></v-spacer>
|
24 |
VUE MUSIC PLAYER |
25 |
<v-spacer></v-spacer>
|
26 |
<v-icon>remove</v-icon> |
27 |
<v-icon>check_box_outline_blank</v-icon> |
28 |
<v-icon>close</v-icon> |
29 |
</v-system-bar>
|
30 |
</template>
|
31 |
|
32 |
<script>
|
33 |
export default { |
34 |
data () { |
35 |
return { |
36 |
dialog: false |
37 |
}
|
38 |
},
|
39 |
}
|
40 |
</script>
|
Ở đây ta dùng component của Vuetify: toolbar, menu, button, icon, list, dialog và card.
Chúng ta tách biệt menu, name, và icons với component <v-spacer>. Để ẩn hoặc hiện hộp thoại, chúng ta tạo thuộc tính dữ liệu dialog:false. Giá trị của nó sẽ thay đổi khi ta nhấp vào mục About.
Giờ trong file Player.vue, import component thanh tiêu đề (title bar), đăng ký với đối tượng component, và bổ sung nó vào template.
1 |
<template>
|
2 |
<v-app dark> |
3 |
<v-content>
|
4 |
<v-container>
|
5 |
<player-title-bar></player-title-bar> // ADD the component in the template
|
6 |
</v-container>
|
7 |
</v-content>
|
8 |
</v-app>
|
9 |
</template>
|
10 |
|
11 |
<script>
|
12 |
import PlayerTitleBar from './components/PlayerTitleBar.vue' // IMPORT the component |
13 |
|
14 |
export default { |
15 |
components: { |
16 |
PlayerTitleBar // REGISTER the component |
17 |
},
|
18 |
data () { |
19 |
return { |
20 |
|
21 |
}
|
22 |
}
|
23 |
}
|
24 |
</script>
|
Bây giờ, hãy kiểm tra kết quả trong trình duyệt của bạn. Bạn sẽ thấy như sau:



Chúng ta sẽ lặp lại ba bước này cho bốn component còn lại. Vì vậy, khi trong các phần tiếp theo tôi nói bạn cần import, đăng ký và bổ sung một thành phần trong template, thì bạn nên tuân theo y hệt trình tự được mô tả ở đây.
Xây dựng thành phần playlist (danh sách bài hát)
Trong thư mục gốc, hãy tạo một thư mục playlist và bổ sung những file âm thanh mà bạn muốn. Tên của các file phải có dấu gạch chân giữa các từ và phần mở rộng .mp3 ở cuối cùng - ví dụ, Remember_the_Way.mp3. Giờ hãy tạo một mảng chứa các bản âm thanh trong đối tượng dữ liệu của Player.vue:
1 |
playlist: [ |
2 |
{title: "Streets of Sant'Ivo", artist: "Ask Again", howl: null, display: true}, |
3 |
{title: "Remember the Way", artist: "Ask Again", howl: null, display: true}, |
4 |
...
|
5 |
]
|
Mỗi bản nhạc có thuộc tính title và artist, một đối tượng howl có giá trị null, và thuộc tính display xét thành true.
Thuộc tính display sẽ được dùng khi ta triển khai chức năng tìm kiếm. Giờ nó được xét là true cho tất cả bản nhạc, vì thế tất cả đều hiện thị.
Howler chứa file âm thanh trong đối tượng howl. Ta xét howl thành null bởi vì ta sẽ đưa dữ liệu động vào lúc tạo ra đối tượng Vue. Để thực hiện điều đó, ta sử dụng created lifecycle hook của Vue.
1 |
created: function () { |
2 |
this.playlist.forEach( (track) => { |
3 |
let file = track.title.replace(/\s/g, "_") |
4 |
track.howl = new Howl({ |
5 |
src: [`./playlist/${file}.mp3`] |
6 |
})
|
7 |
})
|
8 |
}
|
Nó sẽ xét đối tượng Howl cho mỗi bản nhạc trong danh sách nhạc.
Giờ tạo component PlayerPlaylistPanel.vue và bổ sung đối tượng đó vào:
1 |
<template>
|
2 |
<v-card height="330"> |
3 |
<v-list>
|
4 |
<v-list-tile
|
5 |
v-for="(track, index) in playlist" |
6 |
:key="track.title" |
7 |
v-show="track.display"> |
8 |
<v-list-tile-content>
|
9 |
<v-list-tile-title>{{ index }} {{ track.artist }} - {{ track.title }}</v-list-tile-title> |
10 |
</v-list-tile-content>
|
11 |
<v-spacer></v-spacer>
|
12 |
{{ track.howl.duration() }}
|
13 |
</v-list-tile>
|
14 |
</v-list>
|
15 |
</v-card>
|
16 |
</template>
|
17 |
|
18 |
<script>
|
19 |
export default { |
20 |
props: { |
21 |
playlist: Array |
22 |
}
|
23 |
}
|
24 |
</script>
|
Đầu tiên ta truyền prop playlist từ file Player.vue. Tiếp theo, trong template, chúng ta đi qua từng bản nhạc với directive v-for và hiển thị index của bản nhạc, theo sau là tên nghệ sĩ và tự đề, và thời lượng của bản nhạc ở phía xa bên phải. Ta cũng dùng v-show ràng buộc với thuộc tính display. Một bản nhạc sẽ hiển thị chỉ khi display là true.
Trong file Player.vue. ta import (nhập), đăng ký và bổ sung component danh sách bản nhạc trong template. Sau đó, ta gán prop playlist cho thuộc tính dữ liệu playlist như sau: <player-playlist-panel :playlist="playlist"></player-playlist-panel>.
Hãy kiểm tra kết quả trên trình duyệt:



Có 2 vấn đề ở đây. Thứ nhất là số lượng bản nhạc không đúng và thứ hai, thời lượng của bản nhạc được hiển thị ở mili giây, nhưng ta muốn là phút. Chúng ta sẽ sửa chúng bằng cách tạo ra một bộ lọc định dạng.
Trong main.js, hãy tạo bộ lọc (filter) numbers và minutes, có thể được truy xuất toàn cục. Tiếp theo, trong PlayerPlaylistPanel.vue, chúng ta sẽ dùng chúng như sau: {{ index | numbers }} và {{ track.howl.duration() | minutes }}.
Giờ nếu bạn kiểm tra ứng dụng, mọi thứ sẽ hiển thị đúng.



Làm các bản nhạc có thể chọn lựa
Trong file Player.vue, bổ sung thuộc tính dữ liệu selectedTrack: null và gán nó vào component danh sách bản nhạc (:selectedTrack="selectedTrack"). Sau đó chúng ta truyền vào prop trong file PlayerPlaylistPanel.vue (selectedTrack: Object).
Ta cũng bổ sung trình nghe sự kiện cho <v-list-title-content-@click="selectTrack(track)"> và sau đó tạo phương thức selectTrack().
1 |
methods: { |
2 |
selectTrack (track) { |
3 |
this.$emit('selecttrack', track) |
4 |
}
|
5 |
}
|
Giờ, trở lại Player.vue, bổ sung sự kiện selecttrack cho component danh sách bản nhạc (@selecttrack="selectTrack") và tạo phương thức selectTrack():
1 |
selectTrack (track) { |
2 |
this.selectedTrack = track |
3 |
}
|
Giờ nếu bạn đến danh sách nhạc và nhấp vào một bản nhạc, nó sẽ được chọn. Ta không thể thấy nó nhưng có thể thấy rõ trong Vue DevTools. Trong ảnh chụp màn hình sau đây, bản nhạc thứ hai được chọn:



Tạo kiểu cho các dòng và phần chọn lựa
Bước tiếp theo là làm cho phần được chọn hiển thị. Để làm điều này, ta sẽ gán một class để tô màu cho bản nhạc được chọn thành màu cam và một class khác sẽ làm cho dòng đó trở nên tối để các bản nhạc dễ phân biệt hơn. Đưa dòng sau vào sau v-show:
1 |
:class="[{selected: track === selectedTrack}, {even: index % 2 == 0}]"
|
Ta cũng sẽ bổ sung một class khác, để hiển thị thanh cuộn khi danh sách trở nên dài hơn.
1 |
<v-card height="330" :class="{playlist}"> |
Ta bổ sung những class cần thiết vào cuối file.
1 |
<style scoped> |
2 |
.selected { |
3 |
background-color: orange !important; |
4 |
}
|
5 |
.even { |
6 |
background-color: #505050 |
7 |
}
|
8 |
.playlist { |
9 |
overflow: auto |
10 |
}
|
11 |
</style> |
Và thế đấy. Giờ bản nhạc được chọn đã được tô sáng màu cam.



Ta sẽ bổ sung chức năng nhấp đúp để phát nhạc ở cuối phần này.
Xây dựng component bộ điều khiển trình phát nhạc
Hãy tạo các điều khiển trình phát nhạc ngay bây giờ. Chúng ta sẽ bắt đầu với các nút play, pause và stop.
Thêm các nút Play, Pause và Stop
Tạo thành phần PlayerControlsBars.vue và bổ sung nội dung này vào:
1 |
<template>
|
2 |
<div>
|
3 |
<v-toolbar flat height=90> |
4 |
<v-spacer></v-spacer>
|
5 |
<v-btn outline fab small color="light-blue" @click="stopTrack"> |
6 |
<v-icon>stop</v-icon> |
7 |
</v-btn>
|
8 |
<v-btn outline fab color="light-blue" @click="playTrack()"> |
9 |
<v-icon large>play_arrow</v-icon> |
10 |
</v-btn>
|
11 |
<v-btn outline fab small color="light-blue" @click="pauseTrack"> |
12 |
<v-icon>pause</v-icon> |
13 |
</v-btn>
|
14 |
<v-spacer></v-spacer>
|
15 |
</v-toolbar>
|
16 |
</div>
|
17 |
</template>
|
Ở đây ta sử dụng component toolbar của Vuetify.
Có ba nút với trình nghe sự kiện click (nhấp chuôt) đã đăng ký. Hãy tạo các phương thức cho chúng:
1 |
methods: { |
2 |
playTrack(index) { |
3 |
this.$emit('playtrack', index) |
4 |
},
|
5 |
pauseTrack() { |
6 |
this.$emit('pausetrack') |
7 |
},
|
8 |
stopTrack() { |
9 |
this.$emit('stoptrack') |
10 |
}
|
11 |
}
|
Bây giờ, trong tệp Player.vue, import, đăng ký và bổ sung component vào template. Sau đó, đăng ký trình nghe sự kiện (event listener) (@playtrack="play", @pausetrack="pause", @stoptrack="stop").
Tiếp theo, tạo thuộc tính dữ liệu index: 0, nó sẽ lưu index của bản nhạc hiện thời. Sau đó, tạo phương thức currentTrack():
1 |
computed: { |
2 |
currentTrack () { |
3 |
return this.playlist[this.index] |
4 |
}
|
5 |
}
|
Và giờ ta có thể bắt đầu tạo phương thức play, pause và stop. Chúng ta bắt đầu với phương thức play(), nhưng trước đó ta cần tạo thuộc tính dữ liệu playing: false, thuộc tính này sẽ báo hiệu liệu bản nhạc có đang phát hay không. Bổ sung mã nguồn sau vào phương thức play():
1 |
play (index) { |
2 |
let selectedTrackIndex = this.playlist.findIndex(track => track === this.selectedTrack) |
3 |
|
4 |
if (typeof index === 'number') { |
5 |
index = index |
6 |
} else if (this.selectedTrack) { |
7 |
if (this.selectedTrack != this.currentTrack) { |
8 |
this.stop() |
9 |
}
|
10 |
index = selectedTrackIndex |
11 |
} else { |
12 |
index = this.index |
13 |
}
|
14 |
|
15 |
let track = this.playlist[index].howl |
16 |
|
17 |
if (track.playing()) { |
18 |
return
|
19 |
} else { |
20 |
track.play() |
21 |
}
|
22 |
|
23 |
this.selectedTrack = this.playlist[index] |
24 |
this.playing = true |
25 |
this.index = index |
26 |
}
|
Phương thức này lấy index làm tham số, nó chỉ định bản nhạc sẽ được phát. Đầu tiên, chúng ta lấy index của bản nhạc đã chọn. Sau đó, chúng tôi thực hiện một số kiểm tra để xác định giá trị của index. Nếu một index được cung cấp dưới dạng một đối số và đó là một con số, thì chúng ta sử dụng nó. Nếu một bản nhạc được chọn, chúng tôi sẽ sử dụng index của bài hát đã chọn. Nếu bản nhạc được chọn khác với bản nhạc hiện tại, chúng ta sử dụng phương thức stop() để dừng bản nhạc hiện tại. Sau cùng, nếu không có đối số index được truyền vào hoặc không có bản nhạc nào được chọn, chúng tôi sử dụng giá trị của thuộc tính dữ liệu index.
Tiếp theo, chúng ta sẽ nhận được tiếng ồn (dựa trên giá trị chỉ mục) cho bản nhạc và kiểm tra xem nó có đang phát hay không. Nếu có, chúng tôi không trả lại gì cả; nếu không, chúng tôi sẽ chơi nó.
Cuối cùng, chúng ta cập nhật các thuộc tính dữ liệu selectedTrack, playing và index.
Giờ hãy tạo các phương thức pause() và stop().
1 |
pause () { |
2 |
this.currentTrack.howl.pause() |
3 |
this.playing = false |
4 |
},
|
5 |
stop () { |
6 |
this.currentTrack.howl.stop() |
7 |
this.playing = false |
8 |
}
|
Ở đây chúng ta chỉ tạm dừng hoặc dừng hẳn bản nhạc hiện thời và cập nhật thuộc tính playing.
Hãy làm bản nhạc bắt đầu phát bằng cú nhấp đúp chuột.
Bổ sung @dblclick="playTrack()" cho trong PlayerPlaylistPanel.vue và tạo phương thức playTrack():
1 |
playTrack(index) { |
2 |
this.$emit('playtrack', index) |
3 |
}
|
Đăng ký trình nghe sự kiện #@playtrack="play" trong file Player.vue.
Bổ sung các nút Trước và Sau
Giờ hãy tạo các nút trước và sau
1 |
<v-btn outline fab small color="light-blue" @click="skipTrack('prev')"> |
2 |
<v-icon>skip_previous</v-icon> |
3 |
</v-btn>
|
4 |
|
5 |
<!-- stop, play, and pause buttons are here -->
|
6 |
|
7 |
<v-btn outline fab small color="light-blue" @click="skipTrack('next')"> |
8 |
<v-icon>skip_next</v-icon> |
9 |
</v-btn>
|
Tạo phương thức skipTrack():
1 |
skipTrack (direction) { |
2 |
this.$emit('skiptrack', direction) |
3 |
}
|
Đăng kỳ trình nghe sự kiện (@skiptrack="skip") trong Player.vue
Và tạo phương thức skip():
1 |
skip (direction) { |
2 |
let index = 0 |
3 |
|
4 |
if (direction === "next") { |
5 |
index = this.index + 1 |
6 |
if (index >= this.playlist.length) { |
7 |
index = 0 |
8 |
}
|
9 |
} else { |
10 |
index = this.index - 1 |
11 |
if (index < 0) { |
12 |
index = this.playlist.length - 1 |
13 |
}
|
14 |
}
|
15 |
|
16 |
this.skipTo(index) |
17 |
},
|
18 |
skipTo (index) { |
19 |
if (this.currentTrack) { |
20 |
this.currentTrack.howl.stop() |
21 |
}
|
22 |
|
23 |
this.play(index) |
24 |
}
|
Đầu tiên chúng tôi kiểm tra xem hướng đi tiếp theo có phải là next không. Nếu vậy, chúng tôi tăng index lên 1. Và nếu index lớn hơn chỉ số cuối cùng trong mảng, thì chúng tôi bắt đầu lại từ 0. Khi hướng đi là prev, chúng tôi giảm index xuống 1. Nếu index nhỏ hơn 0, thì chúng ta dùng index cuối cùng. Sau cùng, ta dùng index làm đối số cho phương thức skipTo(). Nó dừng bản nhạc hiện thời và chơi bản nhạc kế tiếp.
Đây là diện mạo của trình phát nhạc khi có các nút:



Bổ sung thanh trượt âm lượng
Bổ sung phần sau vào trước tất cảc nút bấm:
1 |
<v-slider v-model="volume" @input="updateVolume(volume)" max="1" step="0.1"></v-slider> |
Ở đây ta dùng component slider của Vuetify:
Bổ sung thuộc tính dữ liệu volume: 0.5, và sau đó tạo phương thức updateVolume():
1 |
updateVolume (volume) { |
2 |
Howler.volume(volume) |
3 |
}
|
Ở đây, chúng ta dùng đối tượng toàn cục Howler để xét giá trị âm lượng cho tất cả howl.
Đồng thời, ta cũng cần đồng bộ âm lượng ban đầu của Howler, nó mặc định được xét là 1, cho thuộc tính volume. Nếu bạn không làm điều này, volume sẽ hiển thị 0.5 nhưng khởi đầu sẽ là 1. Bạn sẽ dùng hook created lần nữa:
1 |
created: function () { |
2 |
Howler.volume(this.volume) |
3 |
}
|
Chúng ta muốn thấy mức độ âm lượng ở dạng phần trăm ở bên phải thanh trượt âm lượng, vì thế chúng ta bổ sung nó vào template: {{this.volume * 100 + '%'}}
Bổ sung nút tắt âm thanh
Bây giờ, chúng ta thêm một biểu tượng âm lượng trước thanh trượt.
1 |
<v-btn flat icon @click="toggleMute"> |
2 |
<template v-if="!this.muted"> |
3 |
<v-icon v-if="this.volume >= 0.5">volume_up</v-icon> |
4 |
<v-icon v-else-if="this.volume > 0">volume_down</v-icon> |
5 |
<v-icon v-else>volume_mute</v-icon> |
6 |
</template>
|
7 |
<v-icon v-show="this.muted">volume_off</v-icon> |
8 |
</v-btn>
|
Biểu tượng sẽ đổi tuỳ theo giá trị của thuộc tính volume và muted.
Bổ sung thêm thuộc tính dữ liệu muted: false và tạo phương thức toggleMute():
1 |
toggleMute () { |
2 |
Howler.mute(!this.muted) |
3 |
this.muted = !this.muted |
4 |
}
|
Chúng tôi sử dụng đối tượng toàn cục Howler lần nữa để thiết lập tắt tiếng trên toàn cục, và sau đó chúng tôi chuyển đổi giá trị muted.
Trong ảnh chụp màn hình bên dưới, bạn có thể xem thanh trượt âm lượng trông như thế nào:



Bổ sung nút phát lại
Bổ sung phần sau đây sau khi tất cả các nút:
1 |
<v-btn flat icon @click="toggleLoop"> |
2 |
<v-icon color="light-blue" v-if="this.loop">repeat_one</v-icon> |
3 |
<v-icon color="blue-grey" v-else>repeat_one</v-icon> |
4 |
</v-btn>
|
Bổ sung thuộc tính loop:false trong Player.vue, gắn kết nó :loop="loop" và truyền prop (loop:Boolean) trong PlayerControlsBars.vue.
Bây giờ, hãy tạo phương thức toggleLoop():
1 |
toggleLoop () { |
2 |
this.$emit('toggleloop', !this.loop) |
3 |
}
|
Bây giờ, hãy quay lại Player.vue, đăng ký trình xử lý sự kiện (@ toggleloop = "toggleLoop") và tạo phương thức toggleLoop():
1 |
toggleLoop (value) { |
2 |
this.loop = value |
3 |
}
|
Tại thời điểm này, chúng ta phải đối mặt với một vấn đề nhỏ. Khi một bản nhạc kết thúc, nó sẽ dừng lại. Trình phát nhạc không chuyển sang bài hát tiếp theo, cũng không lặp lại bài hát hiện tại. Để khắc phục điều đó, chúng ta cần bổ sung phần sau đây vào hàm created sau thuộc tính src:
1 |
onend: () => { |
2 |
if (this.loop) { |
3 |
this.play(this.index) |
4 |
} else { |
5 |
this.skip('next') |
6 |
}
|
7 |
}
|
Giờ khi loop được mở, bản nhạc hiện tại sẽ được phát lại. Nếu nó tắt, trình phát nhạc sẽ phát bản nhạc kế tiếp.
Bổ sung nút Shuffle (nghe ngẫu nhiên)
Bổ sung nội dung sau đây sau nút repeat (phát lại):
1 |
<v-btn flat icon @click="toggleShuffle"> |
2 |
<v-icon color="light-blue" v-if="this.shuffle">shuffle</v-icon> |
3 |
<v-icon color="blue-grey" v-else>shuffle</v-icon> |
4 |
</v-btn>
|
Bổ sung thuộc tính shuffle: false trong Player.vue, gán vào nó (:shuffle="shuffle"), và truyền giá trị prop (shuffle: Boolean) trong PlayerControlsBars.vue.
Giờ hãy tạo phương thức toggleShuffle():
1 |
toggleShuffle () { |
2 |
this.$emit('toggleshuffle', !this.shuffle) |
3 |
}
|
Giờ hãy trở về Player.vue, đăng ký trình nghe sự kiện (@toggleshuffle="toggleShuffle") và tạo phương thức toggleShuffle():
1 |
toggleShuffle (value) { |
2 |
this.shuffle = value |
3 |
}
|
Giờ hãy bổ sung phần sau vào phương thức skip() sau index = 0:
1 |
lastIndex = this.playlist.length - 1 |
2 |
|
3 |
if (this.shuffle) { |
4 |
index = Math.round(Math.random() * lastIndex) |
5 |
while (index === this.index) { |
6 |
index = Math.round(Math.random() * lastIndex) |
7 |
}
|
8 |
} else if (direction === "next") { ... |
Đây diện mạo của ứng dụng của bạn:



Bổ sung Seek Bar (thanh tìm kiếm)
Đầu tiên, trong Player.vue, tạo thuộc tính seek:0. Sau đó ta sẽ theo dõi thuộc tính playing để cập nhật phần tìm kiếm.
1 |
watch: { |
2 |
playing(playing) { |
3 |
this.seek = this.currentTrack.howl.seek() |
4 |
let updateSeek |
5 |
if (playing) { |
6 |
updateSeek = setInterval(() => { |
7 |
this.seek = this.currentTrack.howl.seek() |
8 |
}, 250) |
9 |
} else { |
10 |
clearInterval(updateSeek) |
11 |
}
|
12 |
},
|
13 |
}
|
Nó sẽ cập nhật kết quả tìm kiếm 4 lần mỗi giây.
Giờ hãy tạo một progress() đã tính toán:
1 |
progress () { |
2 |
if (this.currentTrack.howl.duration() === 0) return 0 |
3 |
return this.seek / this.currentTrack.howl.duration() |
4 |
}
|
Gán nó vào (:progress="progress") trong template.
Giờ trong PlayerControlsBars.vue, truyền prop progress (progress: Number) và bổ sung một thanh công cụ khác bên dưới thanh công cụ ta vừa tạo ra:
1 |
<v-toolbar flat height="40"> |
2 |
<v-progress-linear height="40" v-model="trackProgress" @click="updateSeek($event)"></v-progress-linear> |
3 |
</v-toolbar>
|
Ở đây ta dùng component progress của Vuetify.
Tạo một trackProgress() đã được tính toán, nó sẽ lấy tiến trình của bản nhạc ở dạng phần trăm.
1 |
computed: { |
2 |
trackProgress () { |
3 |
return this.progress * 100 |
4 |
},
|
5 |
}
|
Và giờ, tạo phương thức updateSeek():
1 |
updateSeek (event) { |
2 |
let el = document.querySelector(".progress-linear__bar"), |
3 |
mousePos = event.offsetX, |
4 |
elWidth = el.clientWidth, |
5 |
percents = (mousePos / elWidth) * 100 |
6 |
this.$emit('updateseek', percents) |
7 |
}
|
Ở đây, chúng ta có thành phần thanh trạng thái, dùng class .progress-linear__bar. Tôi tìm thấy nó trong Browser DevTools. Tiếp theo, chúng ta lấy vị trí con chuột và chiều rộng của thanh trạng thái. Sau đó, chúng ta lấy vị trí nhấp chuột làm phần trăm.
Trở lại Player.vue, bổ sung và đăng ký sự trình nghe sự kiện (@updateseek="setSeek") và tạo phương thức setSeek():
1 |
setSeek (percents) { |
2 |
let track = this.currentTrack.howl |
3 |
|
4 |
if (track.playing()) { |
5 |
track.seek((track.duration() / 100) * percents) |
6 |
}
|
7 |
}
|
Bạn có thể sử dụng chuột để thay đổi vị trí của bản nhạc được chơi.
Xây dựng component cho bảng thông tin
Tạo file PlayerInfoPanel.vue với nội dung sau đây:
1 |
<template>
|
2 |
<v-card height="60"> |
3 |
<v-card-title>
|
4 |
<h2>{{ trackInfo.artist }} - {{ trackInfo.title }}</h2> |
5 |
<v-spacer></v-spacer>
|
6 |
<h3>{{trackInfo.seek | minutes}}/{{trackInfo.duration | minutes}}</h3> |
7 |
</v-card-title>
|
8 |
</v-card>
|
9 |
</template>
|
10 |
|
11 |
<script>
|
12 |
export default { |
13 |
props: { |
14 |
trackInfo: Object |
15 |
},
|
16 |
}
|
17 |
</script>
|
Ở đây, chúng ta truyền prop trackInfo, ta dùng nó để đưa thông tin bản nhạc trong component của chúng ta.
Giờ quay lại Player.vue, import và đăng ký và bổ sung component trong template.
Sau đó, tạo getTrackInfo():
1 |
getTrackInfo () { |
2 |
let artist = this.currentTrack.artist, |
3 |
title = this.currentTrack.title, |
4 |
seek = this.seek, |
5 |
duration = this.currentTrack.howl.duration() |
6 |
return { |
7 |
artist, |
8 |
title, |
9 |
seek, |
10 |
duration, |
11 |
}
|
12 |
}
|
Tiếp theo, ta gán nó vào template (:trackInfo="getTrackInfo"). Chúng ta lấy vài thông tin cơ bản cho bản nhạc đang phát, bạn có thể nhìn thấy trong ảnh chụp màn hình bên dưới:



Xây dựng component cho thanh tìm kiếm
Tạo file PlayerSearchBar.vue với nội dung sau đây:
1 |
<template>
|
2 |
<v-toolbar flat> |
3 |
<v-text-field
|
4 |
clearable
|
5 |
prepend-icon="search" |
6 |
placeholder="Quick search" |
7 |
v-model="searchString" |
8 |
@input="searchPlaylist"> |
9 |
</v-text-field>
|
10 |
<v-spacer></v-spacer>
|
11 |
</v-toolbar>
|
12 |
</template>
|
13 |
|
14 |
<script>
|
15 |
export default { |
16 |
props: { |
17 |
playlist: Array |
18 |
},
|
19 |
data () { |
20 |
return { |
21 |
searchString: "", |
22 |
}
|
23 |
},
|
24 |
methods: { |
25 |
searchPlaylist () { |
26 |
this.playlist.forEach((track) => { |
27 |
if (this.searchString) { |
28 |
if (!track.title.toLowerCase().includes(this.searchString.toLowerCase()) && !track.artist.toLowerCase().includes(this.searchString.toLowerCase())) { |
29 |
track.display = false |
30 |
} else { |
31 |
track.display = true |
32 |
}
|
33 |
} else if (this.searchString === "" || this.searchString === null) { |
34 |
track.display = true |
35 |
}
|
36 |
})
|
37 |
}
|
38 |
},
|
39 |
}
|
40 |
</script>
|
Chúng ta tạo một text field và bổ sung prop clearable để hiển thị một biểu tương xoá nội dung khi chung ta gõ gì đó vào.
Với v-model, chúng ta gán nó vào searchString, nó là một chuỗi rỗng lúc ban đầu. Và chúng ta bổ sung một trình lắng nghe việc nhập dữ liệu.
Chúng ta cũng truyền vào prop playlist, ta sẽ dùng nó trong phương thức searchPlaylist. Trong phương thức này, chúng ta dùng thuộc tính display và chuyển thành off cho mỗi bản nhạc khi tựa đề và tên nghệ sĩ không khớp với chuỗi tìm kiếm, và chúng ta duy trì hoặc chuyển thành on cho các kết quả phù hợp. Cuối cùng, nếu chuỗi tìm kiếm rỗng hoặc bẳng với null, điều này xảy ra khi chúng ta xoá ô nhập với nút clear (xoá), ta chuyển thành on cho display của tất cả bản nhạc.
Giờ quay lại Player.vue, import, đăng ký và bổ sung component vào template.
Gán nó vào thuộc tính danh sách nhạc (:playlist="playlist") và kiểm tra chức năng. Đây là diện mạo của nó trong thực tiễn:



Những ý tưởng cải tiến
Như bạn có thể thấy, với mục tiêu rõ ràng và kế hoạch phù hợp, việc xây dựng ứng dụng Vue/Vuetify có thể thực sự dễ dàng và thú vị. Bây giờ bạn có một trình nghe nhạc hoạt động tốt để bạn có thể sử dụng trong thời gian thư giãn hoặc viết mã của bạn. Dĩ nhiên luôn có không gian để cải thiện và bổ sung nhiều hơn, vì thế đây là vài ý tưởng bạn có thể thử để khiến trình chơi nhạc có thêm nhiều tính năng:
- hỗ trợ nhiều danh sách nhạc
- bổ sung và xoá bỏ bản nhạc khỏi danh sách
- hỗ trợ kéo thả
- sắp xếp các bản nhạc
- trực quan hoá âm thanh
Tổng kết
Trong hướng dẫn này, chúng ta đã thấy để xây dựng một ứng dụng với Vue.js dễ dàng và thú vị ra sao, và đặc biệt là với Vuetify.js. Tôi hy vọng bạn tận hưởng việc xây dựng trình phát nhạc này nhiêu như tôi. Tôi sẽ rất vui mừng khi thấy phiên bản cải tiến cho trình phát nhạc của chính bạn. Vậy nếu bạn đã tạo ra nó, hãy gửi một liên kết demo trong phần bình luận nhé!



