序文#
このプロジェクトは、ある日、寮で音楽を聴いているときに思いついたものです。ネットイースクラウドに登録してから 5 年が経ちましたが、このプラットフォームはますます私を失望させるようになっていますが、5 年間聴いてきたので感情が出てきました。だから、この休暇中にほぼ 1 ヶ月かけてこのプロジェクトを書きました。なぜモバイルページを作ったのかというと、私が音楽を聴いていたのは携帯電話だったからです... だから、UI も Android 版のネットイースクラウドミュージックを模倣しました。すべての機能を実装するわけではありませんが、比較的中心的な再生ページを作成しました。このブログでは、このプロジェクトの実装プロセスといくつかのトラブルについて記録しています。
プロジェクトは Vue + Typescript + Vuetify UI で実装されています。
プロジェクトのアドレス:CloudMusic
実装された機能:
- ログイン
- プレイリストの取得
- プレイリストの作成
- プレイリストの削除 / お気に入り解除
- 曲の再生
- ランキング
- 毎日のおすすめ
- おすすめのプレイリスト
プロジェクトのスクリーンショット:
再生#
これは核心機能と言えるでしょう。やはり音楽を聴くプラットフォームは、音楽を聴けないといけません。まず、曲の選択はプレイリストから来ることもありますし、ディスカバリーページのスライドショーから来ることもありますし、再生ページの選択から来ることもあります。そして、曲の再生 / 一時停止を制御するには、他のページの下部の再生タブで制御することもできますし、再生ページで制御することもできます。Prop と Emit を使用すると、非常に多くのものが混乱するため、ここでは Vuex を使用しました。
Vuex は、Vue.js アプリケーションのための状態管理パターンです。アプリケーションのすべてのコンポーネントの状態を集中的に管理し、予測可能な方法で状態を変化させるための対応するルールを提供します。
実際には、必要なコンポーネントの共有状態を抽出し、グローバルなシングルトンモデルとして管理し、任意のコンポーネントでその状態や値を変更できるようにします。したがって、選択した曲の ID と現在の再生リスト情報、ID を Vuex に渡し、再生ページでは Vuex で曲の ID の変化を監視するだけで新しい曲を再生できます。Vuex の一時停止の状態を監視して、オーディオコンポーネントの一時停止または再生を制御します。
再生進捗#
進捗バーには Vuetify UI のスライダーコンポーネントを使用し、v-model を使用して値を設定します。
オーディオコンポーネントでは、@timeupdateを使用してオーディオの現在の再生タイムスタンプを監視し、再生総時間で割り、100 を乗算して現在の再生進捗のパーセンテージを取得し、スライダーコンポーネントに割り当てます。進捗バーの左側の時間も、現在の再生タイムスタンプを時間に変換して取得します。
しかし、ここで問題が発生します。スライダーを移動して再生の進捗を変更する必要がありますが、ちょうど @timeupdate にバインドされたメソッドでは、進捗をスライダーに単方向に割り当てるだけで、音楽が再生されているときにこのメソッドが常に実行されるため、どのようにスライダーを移動しても、スライダーはすぐに現在の再生位置に瞬間移動してしまいます。したがって、スライダーが移動中かどうかを監視するメソッドを追加する必要があります。スライダーが移動中の場合、現在の再生進捗をスライダーに割り当てないようにし、手を離すときにスライダーの現在の進捗をタイムスタンプに変換してオーディオの再生時間に再割り当てします。
私の解決策は、スライダーコンポーネントに @mousedown と @mouseup を導入することでした。マウスが押されたときに変数に true を割り当て、離されたときに false を割り当て、@timeupdate にバインドされたメソッドを変更し、その変数が false の場合にのみスライダーコンポーネントに値を割り当てるように設定しました。ただし、スライダーをスライドするときにも現在の再生時間を変更する必要があるため、スライダーコンポーネントに @change を導入し、スライダーの値が手動で変更された場合にこのメソッドがトリガーされるようにしました。このメソッドでは、オーディオの再生進捗を変更します。
歌詞の実装#
バックエンドから取得した歌詞は文字列ですが、各行の歌詞は \n で区切られていますので、文字列を \n で分割すると各行の歌詞が得られます。各行の歌詞は、角括弧で囲まれた時間で分割する必要があります。最後に、時間をタイムスタンプに変換し、歌詞と一緒に配列にクラスとして追加します。
インデックスを設定し、何行目の歌詞かを記録するために使用します。歌詞コンポーネントの高さを offsetHeight で取得し、歌詞の中央線の位置を確認するためにインデックスに行の高さを乗算してスクロールするかどうかを判断します。
実際には、外部の div には overflowを設定し、margin-top を変更してスクロール効果を実現します。margin-top の値は、中央線の高さからインデックスに行の高さを乗算したものによって決まります。同時に、変更の時間を指定するために transition 属性を使用できます。
例:transition: margin-top 1s;
これは、margin-top プロパティが 1 秒で変更されることを意味します。
Vue のカスタムディレクティブについて#
作成中に、特定の機能を実現するために、要素の外側をクリックした場合にその要素を非表示にする機能を実装したいと思いました。
調査中に、Vue にはそのようなディレクティブはないことを発見しましたが、自分でカスタムディレクティブを作成してこの機能を実現することができます。
まず、カスタムディレクティブオブジェクトのフック関数を見てみましょう:
- bind:要素が初めてバインドされたときに呼び出されます。ここでは、一度だけの初期化設定を行うことができます。
- inserted:バインドされた要素が親ノードに挿入されたときに呼び出されます(親ノードが存在することは保証されますが、ドキュメントに挿入されているわけではありません)。
- update:ディレクティブが含まれるコンポーネントの VNode が更新されると呼び出されますが、子 VNode の更新よりも前に発生する可能性があります。ディレクティブの値が変更されたかどうかは問わず、更新前後の値を比較して不要なテンプレートの更新を無視することができます(詳細なフック関数のパラメータについては以下を参照)。
- componentUpdated:ディレクティブが含まれるコンポーネントの VNode およびそのすべての子 VNode が更新された後に呼び出されます。
- unbind:ディレクティブが要素から解除されるときに呼び出されます。
これらはすべて Vue のドキュメントから来ていますが、それぞれのフック関数と変数はオプションであり、必要なものだけを選択すればよいです。この機能を実現するためには、bind と unbind だけを使用し、変数も el と binding だけを使用しました。
import {DirectiveOptions} from "vue";
// カスタムディレクティブclickoutside、要素以外をクリックした場合にバインドされたメソッドを実行します
const clickoutside: DirectiveOptions = {
// ディレクティブの初期化
bind (el: any, binding: any, vnode) {
function documentHandler (e: any) {
// クリックした要素が自身であるかどうかを判断し、自身であれば戻ります
if (el.contains(e.target)) {
return false
}
// ディレクティブに関数がバインドされているかどうかを判断します
if (binding.expression) {
// バインドされた関数があれば、その関数を呼び出します
binding.value(e)
}
}
// 現在の要素にプライベート変数をバインドし、unbindでイベントリスナーを解除できるようにします
el.vueClickOutside = documentHandler
document.addEventListener('click', documentHandler)
},
unbind (el: any, binding) {
// イベントリスナーを解除します
document.removeEventListener('click', el.vueClickOutside)
delete el.vueClickOutside
}
}
export default clickoutside;
必要な場所でインポートします:
@Component({
directives:{
clickoutside,
}
})
使用方法(div 以外をクリックした場合に outside メソッドを呼び出します):
<div v-clickoutside="outside"></div>
この機能はすでに Vuetify に統合されていることに気づいたので、直接使用できます。これは、ドキュメントをちゃんと読まないという結果です。
最後に⭐️#
🙏 ネットイースミュージック API が提供するインターフェースとドキュメントに感謝します。
また、このプロジェクトは個人の学習のために作成されましたので、正常に使用する場合はネットイースミュージックにアクセスしてください👈