Vue ソング プログレス バーのサンプル コード

Vue ソング プログレス バーのサンプル コード

なお、これはvue-cliで作成したプロジェクトではありません。vue.jsを参照して記述したHTMLファイルです。HTMLファイルに貼り付けるだけで使用できます。私の音楽リンクは一定期間後に無効になります。音楽は自分で用意する必要があります。ドラッグとクリックで再生進行を切り替える機能があります。

デモ写真

ここに画像の説明を挿入

コード

<!DOCTYPE html>
<html lang="ja">

<ヘッド>
  <メタ文字セット="UTF-8">
  <meta http-equiv="X-UA-compatible" content="IE=edge">
  <meta name="viewport" content="width=デバイス幅、初期スケール=1.0">
  <title>ドキュメント</title>
</head>

<本文>
  <div id="アプリ">
    <audio ref="audioRef" 自動再生 @canplay='canplay' @timeupdate='update'></audio>
    <button @click="play">再生</button>
    <button @click="pause">一時停止</button>
    <div class="progress-wrapper">
      <span class="time time-l">{{formatTime(currentTime)}}</span>
      <div class="プログレスバーラッパー">
        <cpn :progress=進捗状況 
          @progress-changing="onProgressChanging" 
          @progress-changed='progressChanged'>
       </cpn>
      </div>
      <span class="time time-l">{{formatTime(duration)}}</span>
    </div>

  </div>

  <!-- 子コンポーネント -->
  <テンプレートid="myCpn">

    <div class="プログレスバー">
      <!-- 後ろに黒い帯があります -->
      <div class="bar-inner" @click="clickProgress">
        <!-- プレイ済みのエリア -->
        <div class="progress" :style='progressStyle' ref="progress">
        </div>
        <!-- ボタン -->
        <div class="progress-btn-wrapper" :style='btnStyle' @touchstart.preventDefault='onTouchStart'
          @touchmove.preventDefault='onTouchMove' @touchend.preventDefault='onTouchEnd' >
          <div class="progress-btn"></div>
        </div>
      </div>
    </div>
  </テンプレート>


  <script src="../../js/vue.js"></script>

  <スクリプト>
    オーディオEl = null
    定数プログレスボタン幅 = 16
    // サブコンポーネント const cpn = {
      テンプレート: "#myCpn",
      小道具: {
        進捗:
          タイプ: 数値、
          デフォルト: 0
        }
      },
      データ() {
        戻る {
          オフセット: 0
        }
      },
      マウント() {

      },
      作成された() {
        this.touch = {}
      },
      計算: {
        進捗スタイル() {
          `width: ${this.offset}px` を返します
        },
        btnスタイル() {
          // コンソールログ('fds');
          `transform: translate3d(${this.offset}px,0,0)` を返します
        },
      },
      時計:
        進捗状況(新しい進捗状況) {
          // プログレスバーの幅 const barWidth = this.$el.clientWidth - progressBtnWidth
          this.offset = barWidth * newProgress
        }
      },
      メソッド: {
        onTouchStart(e) {
          // コンソールログ(e);
          this.touch.x1 = e.changedTouches[0].clientX
          // 黄色のプログレスバーの初期幅 this.touch.beginWidth = this.$refs.progress.clientWidth
          コンソールにログを記録します。
        },
        onTouchMove(e) {
          // コンソールログ(e);
          // x オフセット const delta = e.changedTouches[0].clientX - this.touch.x1
          // 前回の幅 + 今回ドラッグして追加されたオフセット = 黄色のバーの予想される長さ const tempWidth = this.touch.beginWidth + delta
          // barWidthを再度取得する
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // 黄色のバーの長さ/バー幅 = 進行状況 現在の進行状況は const progress = tempWidth / barWidth である必要があります
          this.offset = barWidth * 進捗状況
          this.$emit('progress-changing', 進行状況)
          // console.log("tempWidth", tempWidth);
          // console.log("barWidth", barWidth);
          // console.log("進行状況", 進行状況);

        },
        onTouchEnd(e) {
          // コンソールログ(e);
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const progress = this.$refs.progress.clientWidth / barWidth
          this.$emit('progress-changed', 進行状況)
        },
        // プログレスバーをクリック clickProgress(e){
          // コンソールログ("fds");
          console.log('getBoundingClientRect', this.$el.getBoundingClientRect());
          定数rect = this.$el.getBoundingClientRect()
          // 黄色のバーの幅 const offsetWidth = e.pageX - rect.x
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const 進捗 = offsetWidth / barWidth
          this.$emit('progress-changed', 進行状況)
          console.log(オフセット幅)
        }
      },
    }

    constアプリ = 新しいVue({
      el: "#app",
      データ: {
        コンテンツ: 'fdasdf'、
        ソース: 'https://music.163.com/song/media/outer/url?id=1463165983.mp3',
        現在の時刻: 0,
        期間: 0,
        表示: false,
        進捗状況の変更: false
      },
      コンポーネント:
        cpn
      },

      マウント() {
        this.$nextTick(() => {
          audioEl = this.$refs.audioRef
          audioEl.src = this.src
          // デフォルトで一時停止 audioEl.pause()
        })
      },
      計算: {
        進捗() {
          this.currentTime / this.duration を返します
          console.log("進行状況", this.currentTime / this.duration);
        },

      },
      メソッド: {
        遊ぶ() {
          audioEl.play()
          this.isplay = true
        },
        一時停止() {
          audioEl.一時停止()
          this.isplay = false
          // コンソールログ();
        },
        再生可能(e) {
          // コンソール.log(123456);
          コンソールログ(e);
          this.duration = e.target.duration
        },
        更新(e) {
          if(!this.progressChanging){
            this.currentTime = e.target.currentTime
          }
        },
        onProgressChanging(e) {
          // console.log("onProgressChanging", e);
          this.progressChanging = true
          // リアルタイムで currentTime 値を変更します this.currentTime = this.duration * e 
        },
        進捗状況が変更されました(e){
          // コンソールログ(e);
          this.progressChanging = false
          audioEl.currentTime = this.currentTime = this.duration * e 
          if(!this.isplay){
            コンソールログ("------");
            audioEl.play()
          }
        },
        フォーマット時間(間隔) {
          // 間隔を切り捨てた interval = interval | 0
          // 2桁未満の場合は0で埋める
          分 = ((間隔 / 60 | 0) + '') とします。
          秒 = ((間隔 % 60 | 0) + '') とします
          len = 分.長さとする
          (; 長さ < 2; 長さ++) {
            分 = '0' + 分
          }
          len = 秒.長さ
          (; 長さ < 2; 長さ++) {
            秒 = '0' + 秒
          }
          `${分}:${秒}` を返します
        },

      },
    })
  </スクリプト>
</本文>
<スタイル>
  #アプリ {
    幅: 100%;
  }

  .progress-wrapper {
    ディスプレイ: フレックス;
    幅: 80%;
    パディング: 10px 0;
    アイテムの位置を中央揃えにします。
    マージン: 0 自動;
  }

  。時間 {
    幅: 40px;
    フレックス: 0 0 40px;
    フォントサイズ: 8px;
    マージン: 0 自動;
    パディング: 0 8px;
  }

  .time-l {
    テキスト配置: 左;
  }

  .time-l {
    テキスト配置: 右;
  }

  .プログレスバーラッパー{
    フレックス: 1;
  }

  /* サブコンポーネントスタイル */
  .プログレスバー{
    高さ: 30px;
  }

  .bar-inner {
    位置: 相対的;
    上: 11px;
    高さ: 8px;
    背景色: rgba(87, 82, 82, 0.062);
    境界線の半径: 5px;
  }

  。進捗 {
    位置: 絶対;
    高さ: 100%;
    背景色: rgb(238, 238, 136);
  }

  .progress-btn-wrapper {
    位置: 絶対;
    左: -8px;
    上: -11px;
    幅: 30ピクセル;
    高さ: 30px;
  }

  .progress-btn {
    位置: 相対的;
    上: 7px;
    左: 7px;
    ボックスのサイズ: 境界線ボックス;
    幅: 16px;
    高さ: 16px;
    境界線: 3px実線rgb(189, 189, 218);
    境界線の半径: 50%;
    背景: rgb(123, 192, 212);
  }
</スタイル>

</html>

解説

https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent

中央の進行状況バーは進行状況バー コンポーネントです。黒い背景は進行状況の合計の長さです。左側の黄色のバーは、現在の再生の進行状況です。中央のスライダーを左右にドラッグして、進行状況バーを手動で変更できます。再生中は、進行状況バーが長くなり、スライダーが右に移動します。スライダーを左右にドラッグして再生の進行状況を変更すると、左側の時間が変わります。

再生プロセスを実現するために、進行状況バーも再生されます。コンポーネントの状態を決定するものは何ですか?進行状況によって決定できます。コンポーネントの任意の状態は進行状況に基づいて決定できます。親コンポーネントはデジタルタイプの進行状況を渡します

ボタンの位置と進行状況を示す黄色のバーの幅は進行状況に基づいて計算されます。幅はデータ オフセット (データの定義) で表すことができます。次に、進行状況を監視する必要があります。

https://cn.vuejs.org/v2/api/#vm-el

ルートDOM要素を取得するための知識

      時計:
        進捗状況(新しい進捗状況) {
          // プログレスバーの幅 const barWidth = this.$el.clientWidth - progressBtnWidth
          //オフセット this.offset = barWidth * newProgress
        }
      }

もちろん、computed を使ってもよいですが、el の幅は最初に取得できないので注意が必要です。computed は最初に 1 回計算し、テンプレートがレンダリングされるときに offset にアクセスして、el の幅を計算します。このとき、コンポーネントはマウントされておらず、取得できません。watch を使用すると、進捗が変わったときに実際にレンダリングされているため、clientWidth を取得できます。また、一部のロジックは後で処理する必要があるため、ロジックの記述に傾きやすいため、watch を使用して実装する必要があります。

オフセットを取得したら、DOM をマップし、黄色のプログレス バーとボタンの動的なスタイルを設定する必要があります。

ここに画像の説明を挿入

どちらのスタイルもオフセットに基づいて計算されます。

      計算: {
        進捗スタイル(){
          `width: ${this.offset}px` を返します
        },
        btnスタイル() {
          `transform: translate3d(${this.offset}px,0,0)` を返します
        }
      },

次に、オフセットに基づいてスタイルを計算します。progress 属性を受け入れます。外部の進捗状況が変化すると、進捗状況に基づいてオフセットが計算されます。オフセットにより、スタイルを変更できます。

質問: flex 0 0 40px と width は同様の効果がありますが、場合によっては flex レイアウトが圧縮されたり折りたたまれたりして幅が圧縮されてしまうことがあります。そのため、width を設定することで幅が変化しないようにすることができます。

再生可能なイベントモニターはこちら

ここに画像の説明を挿入

親コンポーネントは、再生の進行状況プロパティ(再生時間/合計時間)を計算します。合計時間が取得されました。再生時間は、イベントtimeupdateで監視できます。

ここに画像の説明を挿入

現在の効果

これは秒数であり、時間をフォーマットする必要があることがわかります。ツール関数を定義します

IIFE: 自己実行関数のカリー化といくつかのビット演算 https://www.jianshu.com/p/a3202bc3f7a4

ここに画像の説明を挿入

ここに画像の説明を挿入

質問: なぜ xxx.yyy|0 は xxx に等しいのですか? ここでの or 演算子が丸め機能を持つのはなぜですか?

ナレッジパッドスタートメソッド

formatTime 関数

フォーマット時間(間隔) {
  // 間隔を切り捨てた interval = interval | 0
  // 2桁未満の場合は0で埋める
  定数分 = ((間隔 / 60 | 0) + '').padstart(2, '0')
  定数秒 = ((間隔 % 60 | 0) + '').padstart(2, '0')
  `${分}:${秒}` を返します
}

しかし、パッドスタート方式は認識できません。

だから自分で書いたんだ

        フォーマット時間(間隔) {
          // 間隔を切り捨てた interval = interval | 0
          // 2桁未満の場合は0で埋める
          分 = ((間隔 / 60 | 0) + '') とします。
          秒 = ((間隔 % 60 | 0) + '') とします
          len = 分.長さとする
          for(;len<2;len++){
            分='0'+分
          }
          len = 秒.長さ
          for(;len<2;len++){
            秒='0'+秒
          }
          `${分}:${秒}` を返します
        }

次に、プログレスバーのインタラクティブロジックを記述します。

ドラッグとクリックをサポート

モバイルデバイスで最も一般的なものは、ontouchstart、ontouchmove、ontouchendです。
https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent

知識防止修飾子

スライダーに3つのイベントを追加する

      メソッド: {
        onTouchStart(e) {
          コンソールログ(e);
        },
        onTouchMove(e) {
          コンソールログ(e);
        },
        onTouchEnd(e) {
          コンソールログ(e);
        }
      },

取得する必要がある情報は 2 つあります。1 つはクリックされた場所、つまりその水平座標を知ることです。左側のプログレスバーの幅(オフセット)

[画面X クライアントX ページX コンセプト

touchmove 中に水平軸の位置も取得する必要があるため、データを共有オブジェクトにバインドすることができます。作成されたフック関数内でオブジェクトを定義できます。

      作成された() {
        this.touch = {}
      },

黄色のバーに参照を与えた後

        onTouchStart(e) {
          // コンソールログ(e);
          this.touch.x1=e.changedTouches[0].clientX
          // 黄色のプログレスバーの初期幅 this.touch.beginWidth = this.$refs.progress.clientWidth
          コンソールにログを記録します。
        },
        onTouchStart(e) {
          // コンソールログ(e);
          this.touch.x1=e.changedTouches[0].clientX
          // 黄色のプログレスバーの初期幅 this.touch.beginWidth = this.$refs.progress.clientWidth
          コンソールにログを記録します。
        },
        onTouchMove(e) {
          // コンソールログ(e);
          // x オフセット const delta = e.changedTouches[0].clientX-this.touch.x1
          // 前回の幅 + 今回ドラッグして追加されたオフセット = 黄色のバーの予想される長さ const tempWidth = this.touch.beginWidth + delta
          // barWidthを再度取得する
          const barWidth = this.$el.clientWidth - progressBtnWidth
          // 黄色のバーの長さ/バー幅 = 進行状況 現在の進行状況 const progress = tempWidth/barWidth
          this.offset = barWidth * 進捗状況
          // console.log("tempWidth", tempWidth);
          // console.log("barWidth", barWidth);
          // console.log("進行状況", 進行状況);

        },

整理してみましょう。最終的な目標はオフセットを取得することです。オフセットは、進捗状況と barWidth によって決まります。ここで進捗状況を計算するにはどうすればよいでしょうか。黄色のバーの現在の幅を合計幅で割る必要があります。黄色のバーの幅は、このスライドの初期幅 + x 距離です。次に、barWidth を取得するのは簡単で、計算できます。

これは冗長だと思いますか? 元の黄色のバーの幅 + このスライドの長さを使用すればよいのではないでしょうか? なぜ進行状況を計算する必要があるのでしょうか? 曲の進行状況が変わったことを外部に知らせる必要があり、それらを対応させる必要があるためです。 最終的には、オーディオを変更する必要があります。 これは親コンポーネントで行われます。 今はドラッグのみを実装しているので、イベントをディスパッチする必要があります。 ここでは、2 つのカスタム イベントをディスパッチします。1 つは進行状況変更イベントで、指がまだドラッグ中であり、離れていないことを意味します。 指が離れると、進行状況変更イベントがディスパッチされ、新しい進行状況が渡されます。

currentTimeの値をリアルタイムで変更する

ここに画像の説明を挿入

これはドラッグ時に現在の時間を変更するためのもので、手を離した時に音楽が変更されます。

ここに画像の説明を挿入

しかし、一時停止中はドラッグできるものの、再生中にドラッグすると不具合が発生することがわかりました。

最適化: 変更時に、エフェクトが一時停止している場合は再生します。このとき、再生/一時停止をクリックしたときに反転する isplay を定義する必要があります。

ここに画像の説明を挿入

では、バグを修正しましょう。再生中に、進行状況をドラッグすると問題が発生します。なぜでしょうか? progressChanging を監視することで、currentTime を変更します。currentTime が変更されると、progress は currentTime に基づいて新しい計算を行い、それを子コンポーネントに渡します。子コンポーネントはこのロジックに入ります。

ここに画像の説明を挿入

オフセットが再計算されます。

最後に、

ここに画像の説明を挿入

更新中に何らかの制御を行う必要があり、変更プロセス中にフラグを追加する必要があります。

ここに画像の説明を挿入

つまり、更新機能では、変更がドラッグの過程にある場合、currentTime を変更しません。変更の過程では、プログレス バーが変更されたと見なされ、プログレス バーを変更する優先度が高くなります。自身の再生によって引き起こされる currentTime の変更の優先度は比較的低くなります。

それでおしまい。

ドラッグするだけでなく、クリックして対応する位置にジャンプすることもできます。

Knowledge webapi --getBoundingClientRect メソッドは、要素のサイズとビューポートに対する位置を返します (短い方を取得します)。

ここに画像の説明を挿入

長いものを取得するにはpagexを使用します

        クリックプログレス(e){
          // コンソールログ("fds");
          console.log('getBoundingClientRect', this.$el.getBoundingClientRect());
          定数rect = this.$el.getBoundingClientRect()
          // 黄色のバーの幅 const offsetWidth = e.pageX - rect.x
          const barWidth = this.$el.clientWidth - progressBtnWidth
          const 進捗 = offsetWidth / barWidth
          this.$emit('progress-changed', 進行状況)
          console.log(オフセット幅)
        }

これで、vue ソング プログレス バー デモに関するこの記事は終了です。vue ソング プログレス バーに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後も 123WORDPRESS.COM を応援していただければ幸いです。

以下もご興味があるかもしれません:
  • Vue2.0+SVGは音楽再生の円形プログレスバーコンポーネントを実現します
  • Vue2.0は音楽/ビデオ再生プログレスバーコンポーネントを実装します
  • vue.js+ElementUI はパスワードの強度を促すプログレスバーの効果を実現します
  • Vue ページの読み込み時の進捗バー機能 (サンプル コード)

<<:  dockerログマウントの問題を解決する

>>:  MySQL 8.0.22 zip圧縮パッケージ版(無料インストール)のダウンロード、インストール、および構成手順の詳細

推薦する

HTML の大なり、小なり、スペース、引用符などでよく使用されるエスケープ コードのリスト。

表は以下のとおりです。 HTMLソースコード結果を表示説明する&lt; <未満記号また...

meta name="" content="の機能の詳細な説明

1. 文法: <meta name="名前" content="...

経験者のHTMLの書き方と理由の分析

1. ナビゲーション: 順序なしリストとその他のラベル要素ナビゲーションを記述するために最も一般的に...

MySQLデータベースの圧縮バージョンのインストールと設定に関する詳細なチュートリアル

目次1. MySQLをダウンロードする2. 圧縮パッケージを解凍する3. MySQLを初期化する4....

React クラスコンポーネントのライフサイクルと実行順序

1. Reactコンポーネントを定義する2つの方法1. 関数コンポーネント。単純な関数コンポーネント...

5 分で vue-cli3 を使用してプロジェクトを作成する方法を説明します (初心者向けガイド)

目次1. Vue環境を構築する2. Vue スキャフォールディングツール3. プロジェクトを作成する...

テキストエリアタグはサイズ変更できず、マウスでドラッグすることもできません

テキストエリアタグのサイズは不変ですコードをコピーコードは次のとおりです。 <textarea...

MySQL ダーティ ページ フラッシュとテーブル スペースの縮小の原理の分析

mysql ダーティページWAL メカニズムにより、InnoDB はステートメントを更新するときに、...

js を使用して過去 1 週間、1 か月、3 か月の時間を取得する簡単な例

目次過去1週間の時間を取得する過去1か月の時間を取得する過去3か月分を取得新しい Date() と ...

ウェブデザインにおけるテキスト入力ボックスのパラメータの説明

<br />一般的なゲストブック、フォーラムなどでは、テキスト入力ボックスが使われていま...

CocosCreatorプロジェクト構造の仕組みの詳細な説明

目次1. プロジェクトフォルダ構造1. アセットフォルダ2. 図書館3. ローカル設定(ローカル) ...

Apache クロスドメイン リソース アクセス エラーの解決策

多くの場合、大規模および中規模の Web サイトでは、静的リソース (フォント ファイル、画像など)...

Nginxドメイン名転送のhttpsアクセスの実装

事前に一言:突然、複数のドメイン名のアクセスを https に転送するというタスクを受け取りました。...

HTML ウェブページの段落レイアウトと改行

Web ページの外観はレイアウトに大きく左右されます。ページ内に長い段落のテキストがある場合、通常は...

ソースコードから、Vue2がデータとメソッドを直接取得できる理由がわかる

目次1. 例: これはデータとメソッドを直接取得できます2. 環境を準備し、ソースコードをデバッグし...