ホームページ > ウェブフロントエンド > jsチュートリアル > ネイティブ JavaScript で弾幕コンポーネントを実装する方法

ネイティブ JavaScript で弾幕コンポーネントを実装する方法

coldplay.xixi
リリース: 2020-10-09 16:32:29
転載
2299 人が閲覧しました

今日の

JavaScript コラムでは、ネイティブ JavaScript で弾幕コンポーネントを実装する方法を紹介します。

ネイティブ JavaScript で弾幕コンポーネントを実装する方法

まえがき

現在、ほとんどすべてのビデオ Web サイトに弾幕機能が備わっているため、今回はネイティブの JavaScript 弾幕タイプを使用して独自にこれをカプセル化します。 。このクラスは、次の属性とインスタンス メソッドを持つことを望んでいます:

Attribute

  • elコンテナ ノードのセレクタ。コンテナ ノードは絶対的に配置され、かつ配置される必要があります。
  • height 各弾幕の高さ
  • mode 弾幕モードでは、半分は弾幕の高さの半分になります。フルは弾幕が画面を横切るのにかかる時間
  • スピード
  • ギャップ幅距離次の弾幕と前の弾幕の間

Method

  • pushData 弾幕メタデータの追加
  • addData弾幕への参加を続ける
  • start弾幕のスケジュール設定を開始
  • stop弾幕を停止
  • restart弾幕を再開
  • clearData弾幕をクリア
  • #closeClose
  • open弾幕を再表示
PS: 自己カプセル化されたツール関数がいくつかありますが、ここでは掲載しません。意味を理解する必要があるだけです。

初期化

JavaScript ファイルを導入した後、次のように使用したいと考えています。まず、デフォルトの構成を採用します。

let barrage = new Barrage({    el: '#container'})复制代码
ログイン後にコピー

パラメータの初期化:

function Barrage(options) {    let {
        el,
        height,
        mode,
        speed,
        gapWidth,
    } = options    this.container = document.querySelector(el)    this.height = height || 30
    this.speed = speed || 15000 //2000ms
    this.gapWidth = gapWidth || 20
    this.list = []    this.mode = mode || 'half'
    this.boxSize = getBoxSize(this.container)    this.perSpeed = Math.round(this.boxSize.width / this.speed)    this.rows = initRows(this.boxSize, this.mode, this.height)    this.timeoutFuncs = []    this.indexs = []    this.idMap = []
}复制代码
ログイン後にコピー

最初にパラメータを受け入れてから初期化します。

getBoxSizeinitRows

function getBoxSize(box) {    let {
        height,
        width
    } = window.getComputedStyle(box)    return {        height: px2num(height),        width: px2num(width)
    }    function px2num(str) {        return Number(str.substring(0, str.indexOf('p')))
    }
}复制代码
ログイン後にコピー
## がどのように行われるかを見てみましょう。 # pass

getComputedStyle

api はボックスの幅と高さを計算します。これはコンテナの幅と高さを計算するために使用され、後で使用されます。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">function initRows(box, mode, height) { let pisor = getpisor(mode) rows = Math.ceil(box.height * pisor / height) return rows }function getpisor(mode) { let pisor = .5 switch (mode) { case &amp;#39;half&amp;#39;: pisor = .5 break case &amp;#39;top&amp;#39;: pisor = 1 / 3 break; case &amp;#39;full&amp;#39;: pisor = 1; break default: break; } return pisor }复制代码</pre><div class="contentsignin">ログイン後にコピー</div></div> 高さに基づいて弾幕のライン数を計算します。ライン数は下のどこかで使用されます。

データの挿入

データを挿入するには 2 つの方法があります。1 つはソース データを追加する方法、もう 1 つは継続的に追加する方法です。まず、ソース データを追加するメソッドを見てみましょう:

this.pushData = function (data) {    this.initDom()    if (getType(data) == &#39;[object Object]&#39;) {        //插入单条
        this.pushOne(data)
    }    if (getType(data) == &#39;[object Array]&#39;) {        //插入多条
        this.pushArr(data)
    }
}this.initDom = function () {    if (!document.querySelector(`${el} .barrage-list`)) {        //注册dom节点
        for (let i = 0; i < this.rows; i++) {            let p = document.createElement(&#39;p&#39;)
            p.classList = `barrage-list barrage-list-${i}`
            p.style.height = `${this.boxSize.height*getpisor(this.mode)/this.rows}px`
            this.container.appendChild(p)
        }
    }
}复制代码
ログイン後にコピー
this.pushOne = function (data) {    for (let i = 0; i < this.rows; i++) {        if (!this.list[i]) this.list[i] = []

    }    let leastRow = getLeastRow(this.list) //获取弹幕列表中最少的那一列,弹幕列表是一个二维数组
    this.list[leastRow].push(data)
}this.pushArr = function (data) {    let list = sliceRowList(this.rows, data)
    list.forEach((item, index) => {        if (this.list[index]) {            this.list[index] = this.list[index].concat(...item)
        } else {            this.list[index] = item
        }
    })
}//根据行数把一维的弹幕list切分成rows行的二维数组function sliceRowList(rows, list) {    let sliceList = [],
        perNum = Math.round(list.length / rows)    for (let i = 0; i < rows; i++) {        let arr = []        if (i == rows - 1) {
            arr = list.slice(i * perNum)
        } else {
            i == 0 ? arr = list.slice(0, perNum) : arr = list.slice(i * perNum, (i + 1) * perNum)
        }
        sliceList.push(arr)
    }    return sliceList
}复制代码
ログイン後にコピー

データを継続的に追加する方法は、ソース データを追加するメソッドを呼び出してスケジュールを開始するだけです

this.addData = function (data) {    this.pushData(data)    this.start()
}复制代码
ログイン後にコピー

集中砲火を開始します

弾幕を放つロジックを見てみましょう

this.start = function () {    //开始调度list
    this.dispatchList(this.list)
}this.dispatchList = function (list) {    for (let i = 0; i < list.length; i++) {        this.dispatchRow(list[i], i)
    }
}this.dispatchRow = function (row, i) {    if (!this.indexs[i] && this.indexs[i] !== 0) {        this.indexs[i] = 0
    }    //真正的调度从这里开始,用一个实例变量存储好当前调度的下标。
    if (row[this.indexs[i]]) {        this.dispatchItem(row[this.indexs[i]], i, this.indexs[i])
    }
}复制代码
ログイン後にコピー
this.dispatchItem = function (item, i) {    //调度过一次的某条弹幕下一次在调度就不需要了
    if (!item || this.idMap[item.id]) {        return
    }    let index = this.indexs[i]    this.idMap[item.id] = item.id    let p = document.createElement(&#39;p&#39;),
        parent = document.querySelector(`${el} .barrage-list-${i}`),
        width,
        pastTime
    p.innerHTML = item.content
    p.className = &#39;barrage-item&#39;
    parent.appendChild(p)
    width = getBoxSize(p).width
    p.style = `width:${width}px;display:none`
    pastTime = this.computeTime(width) //计算出下一条弹幕应该出现的时间
    //弹幕飞一会~
    this.run(p)    if (index > this.list[i].length - 1) {        return
    }    let len = this.timeoutFuncs.length    //记录好定时器,后面清空
    this.timeoutFuncs[len] = setTimeout(() => {        this.indexs[i] = index + 1
        //递归调用下一条
        this.dispatchItem(this.list[i][index + 1], i, index + 1)
    }, pastTime);
}复制代码
ログイン後にコピー
//用css动画,整体还是比较流畅的this.run = function (item) {
    item.classList += &#39; running&#39;
    item.style.left = "left:100%"
    item.style.display = &#39;&#39;
    item.style.animation = `run ${this.speed/1000}s linear`
    //已完成的打一个标记
    setTimeout(() => {
        item.classList+=&#39; done&#39;
    }, this.speed);
}复制代码
ログイン後にコピー
//根据弹幕的宽度和gapWth,算出下一条弹幕应该出现的时间this.computeTime = function (width) {    let length = width + this.gapWidth    let time = Math.round(length / this.boxSize.width * this.speed/2)    return time
}复制代码
ログイン後にコピー

アニメーションCSSは次のとおりです

@keyframes run {
    0% {        left: 100%;
    }

    50% {        left: 0
    }

    100% {        left: -100%;
    }
}.run {    animation-name: run;
}复制代码
ログイン後にコピー

その他のメソッド

停止

一時停止したものを使用します停止するアニメーションの属性

this.stop = function () {    let items = document.querySelectorAll(`${el} .barrage-item`);
    [...items].forEach(item => {
        item.className += &#39; pause&#39;
    })
}复制代码
ログイン後にコピー
.pause {    animation-play-state: paused !important;
}复制代码
ログイン後にコピー

もう一度開始

一時停止クラスを削除

this.restart = function () {    let items = document.querySelectorAll(`${el} .barrage-item`);
    [...items].forEach(item => {
        removeClassName(item, &#39;pause&#39;)
    })
}复制代码
ログイン後にコピー

開いて閉じる

表示非表示ロジックを作成するだけ

this.close = function () {    this.container.style.display = &#39;none&#39;}this.open = function () {    this.container.style.display = &#39;&#39;}复制代码
ログイン後にコピー

弾幕をクリーンアップ

this.clearData = function () {    //清除list
    this.list = []    //清除dom
    document.querySelector(`${el}`).innerHTML = &#39;&#39;
    //清除timeout
    this.timeoutFuncs.forEach(fun => clearTimeout(fun))
}复制代码
ログイン後にコピー

最後に、タイマーを使用して期限切れの弾幕をクリーンアップします:

setInterval(() => {    let items = document.querySelectorAll(`${el} .done`);
    [...items].forEach(item=>{
        item.parentNode.removeChild(item)
    })
}, this.speed*5);复制代码
ログイン後にコピー

最後に

これの実装は次のような気がします。このように設計した場合、どのようにクラスを設計しますか?

関連する無料学習の推奨事項:

javascript(ビデオ)

以上がネイティブ JavaScript で弾幕コンポーネントを実装する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.im
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート