jQuery.Danmu.js

弹幕jQuery.Danmu.js是一个让网页某div上运行弹幕效果的jQuery插件,具备彩色弹幕、顶端底端弹幕、自定义弹幕速度、实时调整透明度等弹幕功能,可以给video加上与之时间线同步的弹幕,也可以把弹幕放在网页的其他任何地方,只要你觉得酷炫。

Tính đến 12-11-2016. Xem phiên bản mới nhất.

Script này sẽ không được không được cài đặt trực tiếp. Nó là một thư viện cho các script khác để bao gồm các chỉ thị meta // @require https://updategreasyfork.deno.dev/scripts/24783/157449/jQueryDanmujs.js

// ==UserScript==
// @name         jQuery.Danmu.js
// @namespace    http://tampermonkey.net/
// @version      0.1.0
// @description  弹幕jQuery.Danmu.js是一个让网页某div上运行弹幕效果的jQuery插件,具备彩色弹幕、顶端底端弹幕、自定义弹幕速度、实时调整透明度等弹幕功能,可以给video加上与之时间线同步的弹幕,也可以把弹幕放在网页的其他任何地方,只要你觉得酷炫。
// @author       cyntax
// @match        https://www.douyu.com/*
// @match        http://www.douyu.com/*
// @icon         http://www.douyu.com/favicon.ico
// @grant        none
// @copyrig h (c) 2011 Cyntax Technologies - http://huck
// ==/UserScript==

/**
 * 专为danmuplayer定制的jquery.danmu.js
 *
 *
 * jQuery Generic Plugin Module
 * Version 0.1
 * Copyright (c) 2011 Cyntax Technologies - http://cyntaxtech.com
 * Licensed under the Cyntax Open Technology License
 *     http://code.cyntax.com/licenses/cyntax-open-technology
 */

(function ($) {
    $.jQueryPlugin = function (name) {
        $.fn[name] = function (options) {
            var args = Array.prototype.slice.call(arguments, 1);
            if (this.length) {
                return this.each(function () {
                    var instance = $.data(this, name) || $.data(this, name, new cyntax.plugins[name](this, options)._init());
                    if (typeof options === "string") {
                        options = options.replace(/^_/, "");
                        if (instance[options]) {
                            instance[options].apply(instance, args);
                        }
                    }
                });
            }
        };
    };
})(jQuery);

var cyntax = {
    plugins: {}
};
;
/*!
 * Pause jQuery plugin v0.1
 *
 * Copyright 2010 by Tobia Conforto <[email protected]>
 *
 * Based on Pause-resume-animation jQuery plugin by Joe Weitzel
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or(at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
/* Changelog:
 *
 * 0.1    2010-06-13  Initial release
 */
(function () {
    var $ = jQuery,
        pauseId = 'jQuery.pause',
        uuid = 1,
        oldAnimate = $.fn.animate,
        anims = {};

    function now() {
        return new Date().getTime();
    }

    $.fn.animate = function (prop, speed, easing, callback) {
        var optall = $.speed(speed, easing, callback);
        optall.complete = optall.old; // unwrap callback
        return this.each(function () {
            // check pauseId
            if (!this[pauseId])
                this[pauseId] = uuid++;
            // start animation
            var opt = $.extend({}, optall);
            oldAnimate.apply($(this), [prop, $.extend({}, opt)]);
            // store data
            anims[this[pauseId]] = {
                run: true,
                prop: prop,
                opt: opt,
                start: now(),
                done: 0
            };
        });
    };

    $.fn.pause = function () {
        return this.each(function () {
            // check pauseId
            if (!this[pauseId])
                this[pauseId] = uuid++;
            // fetch data
            var data = anims[this[pauseId]];
            if (data && data.run) {
                data.done += now() - data.start;
                if (data.done > data.opt.duration) {
                    // remove stale entry
                    delete anims[this[pauseId]];
                } else {
                    // pause animation
                    $(this).stop().stop().stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    $(this).stop();
                    data.run = false;
                }
            }
        });
    };

    $.fn.resume = function () {
        return this.each(function () {
            // check pauseId
            if (!this[pauseId])
                this[pauseId] = uuid++;
            // fetch data
            var data = anims[this[pauseId]];
            if (data && !data.run) {
                // resume animation
                data.opt.duration -= data.done;
                data.done = 0;
                data.run = true;
                data.start = now();
                oldAnimate.apply($(this), [data.prop, $.extend({}, data.opt)]);
            }
        });
    };
})();
;
/**
 * jQuery Timer Plugin
 * Project page - http://code.cyntaxtech.com/plugins/jquery-timer
 * Version 0.1.1
 * Copyright (c) 2011 Cyntax Technologies - http://cyntaxtech.com
 * dependencies: jquery.plugin.js
 * Licensed under the Cyntax Open Technology License
 *     http://code.cyntax.com/licenses/cyntax-open-technology
 * ------------------------------------
 * For details, please visit:
 * http://code.cyntaxtech.com/plugins/jquery-timer
 */

(function ($) {
    cyntax.plugins.timer = function (ele, options) {
        this.$this = $(ele);
        this.options = $.extend({}, this.defaults, options);
        this.timer_info = {id: null, index: null, state: 0};
    };
    cyntax.plugins.timer.prototype = {
        defaults: {
            delay: 1000,      // delay in milliseconds (optional)
            repeat: false,    // true to repeat the timer continuously, or a number for repeating this number of times (optional)
            autostart: true,	// timer starts as soon as it is created, set false to start manually
            callback: null,   // callback (optional)
            url: '',          // url to load content from (optional)
            post: ''          // post data (optional)
        },
        _init: function () {
            if (this.options.autostart) {
                this.timer_info.state = 1;
                this.timer_info.id = setTimeout($.proxy(this._timer_fn, this), this.options.delay);
            }
            return this;
        },
        _timer_fn: function () {
            if (typeof this.options.callback == "function")
                $.proxy(this.options.callback, this.$this).call(this, ++this.timer_info.index);
            else if (typeof this.options.url == "string") {
                ajax_options = {
                    url: this.options.url,
                    context: this,
                    type: (typeof this.options.post == "string" && typeof this.options.post != "" == "" ? "POST" : "GET"),
                    success: function (data, textStatus, jqXHR) {
                        this.$this.html(data);
                    }
                };
                if (typeof this.options.post == "string" && typeof this.options.post != "")
                    ajax_options.data = this.options.post;
                $.ajax(ajax_options);
            }
            if (this.options.repeat && this.timer_info.state == 1 &&
                (typeof this.options.repeat == "boolean" || parseInt(this.options.repeat) > this.timer_info.index))
                this.timer_info.id = setTimeout($.proxy(this._timer_fn, this), this.options.delay);
            else
                this.timer_id = null;
        },
        start: function () {
            if (this.timer_info.state == 0) {
                this.timer_info.index = 0;
                this.timer_info.state = 1;
                this.timer_id = setTimeout($.proxy(this._timer_fn, this), this.options.delay);
            }
        },

        stop: function () {
            if (this.timer_info.state == 1 && this.timer_info.id) {
                clearTimeout(this.timer_info.id);
                this.timer_id = null;
            }
            this.timer_info.state = 0;
        },

        pause: function () {
            if (this.timer_info.state == 1 && this.timer_info.id)
                clearTimeout(this.timer_info.id);
            this.timer_info.state = 0;
        },

        resume: function () {
            this.timer_info.state = 1;
            this.timer_id = setTimeout($.proxy(this._timer_fn, this), this.options.delay);
        }
    };

    $.jQueryPlugin("timer");

})(jQuery);
/*!
 *弹幕引擎核心
 *
 * Copyright 2015 by Ruiko Of AcGit.cc
 * @license MIT
 *
 * 版本3.0 2015/08/12
 */


;
(function ($) {
    var Danmu = function (element, options) {
        this.$element = $(element);
        this.options = options;
        this.id = $(element).attr("id");
        $(element).data("nowTime", 0);
        $(element).data("danmuList", options.danmuList);
        $(element).data("opacity", options.opacity);
        $(element).data("paused", 1);
        $(element).data("topSpace", 0);
        $(element).data("bottomSpace", 0);
        this.$element.css({
            "position": "absolute",
            "left": this.options.left,
            "top": this.options.top,
            "width": this.options.width,
            "height": this.options.height,
            "z-index": this.options.zindex,
            "color": options.defaultFontColor,
            "overflow": "hidden"
        });
        var me = this;
        //播放器长宽
        me.height = this.$element.height();
        me.width = this.$element.width();
        //速度
        me.speed = 1000/options.speed;

        //防止重复
        this.launched = [];
        this.preTime = 0;
        //最大弹幕数控制
        var maxCount = this.options.maxCountInScreen;
        var maxCountPerSec = this.options.maxCountPerSec;
        var nowCount = 0;
        var nowSecCount = 0;
        //格式控制
        this.rowCount = parseInt(me.height / options.FontSizeBig);
        if (me.options.SubtitleProtection) {
            me.rowCount = me.rowCount - 3;
        }
        this.rows = [];
        this.topRows=[];
        this.bottomRows=[];
        this.initRows = function (me) {
            // me.rowCount = parseInt(me.height / options.FontSizeBig);
            for (var i = 0; i < me.rowCount; i++) {
                me.rows[i] = 0;
                me.topRows[i]=0;
                me.bottomRows[i]=0;
            }

        };

        this.initRows(this);
        me.getRow = function (me) {
            var result = 0;
            while (me.rows[result] !== 0) {
                result = result + 1;
                if (result >= me.rowCount) {

                    me.initRows(me);
                    result = 0;
                    break;
                }
            }
            return result;
        };
        me.getTopRow = function (me) {
            for(var i=0;i<me.topRows.length;i++){
                if (me.topRows[i] == 0){
                    return i;
                }
            }
        };

        me.getBottomRow = function (me) {
            for(var i=0;i<me.bottomRows.length;i++){
                if (me.bottomRows[i] == 0){
                    return i;
                }
            }
        };
        me.checkRow = function (me) {
            for (var i in me.rows) {
                if (me.rows[i] !== 0 && typeof($("#" + me.rows[i]).position()) !== "undefined" && ( $("#" + me.rows[i]).position().left < (me.$element.width() - $("#" + me.rows[i]).width()) )) {
                    me.rows[i] = 0
                }
            }
        };
        //me.startCheck = function(me){
        //    setInterval(me.checkRow(me),10);
        //};
        //  me.startCheck(me);

        $("<div class='danmakuTimer'></div>").appendTo(this.$element);
        this.$timer = $(".danmakuTimer");
        this.$timer.timer({
            delay: 100,
            repeat: options.sumTime,
            autostart: false,
            callback: function (index) {
                setTimeout(function () {
                    //计时前置  试验表明前置很好
                    if (me.options.danmuLoop && $(element).data("nowTime") >= $(element).data("sumTime")) {
                        $(element).data("nowTime", 0);
                    }
                    $(element).data("nowTime", $(element).data("nowTime") + 1);
                    //更新播放器面积参数
                    me.height = $(element).height();
                    me.width = $(element).width();
                    //防止重复
                    if (Math.abs($(element).data("nowTime") - (me.preTime + 1)) > 10) {
                        me.launched = [];
                    }
                    me.preTime = $(element).data("nowTime");
                    //更新行数
                    var rowCOld = me.rowCount;
                    me.rowCount = parseInt(me.height / options.FontSizeBig);
                    setTimeout(me.checkRow(me), 0);
                    //字幕保护
                    if (me.options.SubtitleProtection) {
                        me.rowCount = me.rowCount - 3;
                    }
                    if (rowCOld !== 0 && me.rowCount !== rowCOld) {
                        me.initRows(me);
                    }
                    nowSecCount = 0;

                    if ($(element).data("danmuList")[$(element).data("nowTime")] && me.launched.indexOf($(element).data("nowTime")) < 0) {
                        var nowTime = $(element).data("nowTime");
                        var danmus = $(element).data("danmuList")[nowTime];
                        for (var i = (danmus.length - 1); i >= 0; i--) {
                            setTimeout(me.checkRow(me), 0);
                            //setTimeout(me.runDanmu(danmus[i],nowCount,maxCount,nowSecCount,maxCountPerSec,options,me,$(element),speed,$(this)),1);
                            //  setTimeout(me.runDanmu(danmus[i],options,me,$(element),speed,$(this)),1);

                            // console.log(nowCount);
                            var a_danmu = "<span class='danmaku' id='" + me.id + "tempDanmaku'></span>";
                            $(element).append(a_danmu);
                            var danmaku = danmus[i];
                            $("#" + me.id + "tempDanmaku").text(danmaku.text)
                                .css({
                                    "color": danmaku.color
                                    , "text-shadow": " 0px 0px 2px #000000"
                                    , "-moz-opacity": $(element).data("opacity")
                                    , "opacity": $(element).data("opacity")
                                    , "white-space": "nowrap"
                                    , "font-weight": "bold"
                                    , "font-family": "SimHei"
                                    , "font-size": options.FontSizeBig
                                });
                            if (danmaku.color < "#777777")
                                $("#" + me.id + "tempDanmaku").css({
                                    "text-shadow": " 0px 0px 2px #FFFFFF"
                                });
                            if (danmaku.hasOwnProperty('isnew')) {
                                $("#" + me.id + "tempDanmaku").css({"border": "2px solid " + danmaku.color});
                            }
                            if (danmaku.size == 0)  $("#" + me.id + "tempDanmaku").css("font-size", options.fontSizeSmall);
                            if (danmaku.position == 0) {
                                var flyTmpName = me.id + "fly" + parseInt(new Date().getTime()).toString();
                                $("#" + me.id + "tempDanmaku").attr("id", flyTmpName);
                                if (nowCount <= maxCount && nowSecCount <= maxCountPerSec) {
                                    me.checkRow(me);
                                    var row = me.getRow(me);
                                    me.rows[row] = flyTmpName;
                                    danmaku["row"] = row;
                                    var top_local = (row) * options.FontSizeBig;
                                    danmaku["width"] = $("#" + flyTmpName).width();
                                    // var offsetLeft = parseInt(Math.random() * 2 * options.FontSizeBig);
                                    var left_local = $("#" + me.id).width();
                                    $("#" + flyTmpName).css({
                                        "width": $("#" + flyTmpName).width()
                                        , "position": "absolute"
                                        , "top": top_local
                                        , "left": left_local
                                    });
                                    var newSpeed = ($(element).width()+400)/me.speed;
                                    nowCount++;
                                    nowSecCount++;
                                    $("#" + flyTmpName).animate({left: -($("#" + flyTmpName).width() + 400)}, newSpeed
                                        , function () {
                                            $(this).remove();
                                            nowCount--;
                                            nowSecCount--;
                                        }
                                    );
                                }
                                else {
                                    $("#" + flyTmpName).remove();
                                }
                            }
                            else if (danmaku.position == 1) {
                                var topTmpId = me.id + "top" + parseInt(10000 * Math.random()).toString();
                                $("#" + me.id + "tempDanmaku").attr("id", topTmpId);
                                var temRow=me.getTopRow(me);
                                $(element).data("topSpace", options.FontSizeBig*temRow);
                                me.topRows[temRow]=1;
                                $("#" + topTmpId).css({
                                    "width": "100%"
                                    , "text-align": "center"
                                    , "position": "absolute"
                                    , "top": ($(element).data("topSpace"))
                                    , "left": "0"
                                });
                                $("#" + topTmpId).data("row",temRow);
                                $("#" + topTmpId).fadeTo(options.topBottomDanmuTime, $(element).data("opacity"), function () {
                                        me.topRows[$(this).data("row")]=0;
                                        $(this).remove();

                                    }
                                );
                            }
                            else if (danmaku.position == 2) {
                                var bottomTmpId = me.id + "bottom" + parseInt(10000 * Math.random()).toString();
                                $("#" + me.id + "tempDanmaku").attr("id", bottomTmpId);
                                var temRow=me.getBottomRow(me);
                                $(element).data("bottomSpace", options.FontSizeBig*temRow);
                                me.bottomRows[temRow]=1;
                                $("#" + bottomTmpId).css({
                                    "width": options.width
                                    , "left": "0"
                                    , "text-align": "center"
                                    , "position": "absolute"
                                    , "bottom": 0 + $(element).data("bottomSpace")
                                });
                                $("#" + bottomTmpId).data("row",temRow);
                                $("#" + bottomTmpId).fadeTo(options.topBottomDanmuTime, $(element).data("opacity"), function () {
                                        me.bottomRows[$(this).data("row")]=0;
                                        $(this).remove();
                                    }
                                );

                            } //else if
                            danmus[i] = danmaku;
                        }   // for in danmus
                        $(element).data("danmuList")[nowTime] = danmus
                    }  //if (danmus)
                    me.launched.push($(element).data("nowTime"));
                    //   }, 0);

                    //循环
                    if (index == options.sumTime && options.isLoop) {
                        me.$timer.timer('stop');
                        me.$timer.timer('start');
                    }

                })
            }
        });
    };


    Danmu.DEFAULTS = {
        left: 0,
        top: 0,
        height: 360,
        width: 640,
        zindex: 200,
        speed: 8000,
        sumTime: 65535,
        danmuLoop: false,
        danmuList: {},
        defaultFontColor: "#FFFFFF",
        fontSizeSmall: 16,
        FontSizeBig: 24,
        opacity: "0.9",
        topBottomDanmuTime: 6000,
        SubtitleProtection: false,
        positionOptimize: false,
        maxCountInScreen: 40,
        maxCountPerSec: 10
    };


    Danmu.prototype.danmuStart = function () {
        this.$timer.timer('start');
        this.$element.data("paused", 0);
    };


    Danmu.prototype.danmuStop = function () {
        this.$timer.timer('stop');
        $("#" + this.id + ' .danmaku').remove();
        nowTime = 0;
        this.$element.data("paused", 1);
        this.$element.data("nowTime", 0);
    };


    Danmu.prototype.danmuPause = function () {
        this.$timer.timer('pause');
        $("#" + this.id + ' .danmaku').pause();
        this.$element.data("paused", 1);
    };


    Danmu.prototype.danmuResume = function () {
        this.$timer.timer('resume');
        $("#" + this.id + ' .danmaku').resume();
        this.$element.data("paused", 0);
    };

    Danmu.prototype.danmuHideAll = function () {
        $("#" + this.id + ' .danmaku').css({"opacity": 0});
        this.initRows(this);
    };


    Danmu.prototype.setTime = function (arg) {
        $("#" + this.id + ' .danmaku').remove();
        this.$element.data("nowTime", arg);

    };

    Danmu.prototype.setOpacity = function (arg) {
        $("#" + this.id + ' .danmaku').css("opacity", arg);
        this.$element.data("opacity", arg);

    };


    Danmu.prototype.addDanmu = function (arg) {
        if (arg instanceof Array) {
            for (var i in arg) {
                if (this.$element.data("danmuList")[arg[i]["time"]]) {
                    this.$element.data("danmuList")[arg[i]["time"]].push(arg[i]);
                }
                else {
                    this.$element.data("danmuList")[arg[i]["time"]] = [];
                    this.$element.data("danmuList")[arg[i]["time"]].push(arg[i]);
                }
            }
        }
        else {
            if (this.$element.data("danmuList")[arg.time]) {
                this.$element.data("danmuList")[arg.time].push(arg);
            }
            else {
                this.$element.data("danmuList")[arg.time] = [];
                this.$element.data("danmuList")[arg.time].push(arg);
            }
        }
    };


    function Plugin(option, arg) {
        return this.each(function () {
            var $this = $(this);
            var options = $.extend({}, Danmu.DEFAULTS, typeof option == 'object' && option);
            var data = $this.data('danmu');
            var action = typeof option == 'string' ? option : NaN;
            if (!data) $this.data('danmu', (data = new Danmu(this, options)));
            if (action)    data[action](arg);
        })
    };


    $.fn.danmu = Plugin;
    $.fn.danmu.Constructor = Danmu;


})(jQuery);