├── .babelrc ├── .editorconfig ├── .gitignore ├── .travis.yml ├── assets ├── css │ └── slider.css ├── images │ └── bg.png ├── js │ ├── slider.min.js │ └── stickup.min.js └── unslider │ ├── css │ ├── unslider-dots.css │ └── unslider.css │ └── js │ └── unslider-min.js ├── demo ├── bangumi.html ├── bangumi_index.html ├── index.html ├── play.html ├── search.html └── sort.html ├── docs ├── bh5.png ├── css │ ├── main.css │ └── slider.css ├── favicon.ico ├── img │ ├── bg0.png │ └── bg1.jpg ├── index.html └── js │ ├── app.js │ ├── slider.min.js │ └── stickup.min.js ├── favicon.ico ├── gulpfile.babel.js ├── package.json ├── readme.md └── src ├── images ├── bg0.png └── bg1.jpg ├── index.html ├── js ├── App.js ├── Config.js ├── Index.js ├── components │ ├── AllRank.js │ ├── BangumiIndex.js │ ├── BangumiInfo.js │ ├── BangumiList.js │ ├── IndexBanner.js │ ├── IndexHot.js │ ├── Loading.js │ ├── Pager.js │ ├── Player.js │ ├── SearchContent.js │ ├── SortHot.js │ ├── SortRank.js │ └── Video.js ├── danmaku │ ├── BilibiliParser.js │ ├── CommentManager.js │ ├── CommentObject.js │ ├── ScrollComment.js │ ├── StaticComment.js │ └── VideojsPlugin.js ├── page │ ├── BangumiIndexPage.js │ ├── BangumiInfoPage.js │ ├── IndexPage.js │ ├── SearchPage.js │ ├── SortPage.js │ └── ViewPage.js ├── parts │ ├── Error.js │ ├── Footer.js │ └── Loading.js └── utils │ └── VideojsHotKeys.js └── styles ├── bangumi.css ├── main.css └── player.css /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | .tmp 4 | 5 | .idea 6 | 7 | dist 8 | 9 | .publish 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.0.0" 4 | - "4.0.0" 5 | -------------------------------------------------------------------------------- /assets/css/slider.css: -------------------------------------------------------------------------------- 1 | .unslider{overflow:auto;margin:0;padding:0}.unslider-wrap{position:relative}.unslider-wrap.unslider-carousel>li{float:left}.unslider-vertical>ul{height:100%}.unslider-vertical li{float:none;width:100%}.unslider-fade{position:relative}.unslider-fade .unslider-wrap li{position:absolute;left:0;top:0;right:0;z-index:8}.unslider-fade .unslider-wrap li.unslider-active{z-index:10}.unslider li,.unslider ol,.unslider ul{list-style:none;margin:0;padding:0;border:none}.unslider-arrow{position:absolute;left:20px;z-index:2;cursor:pointer}.unslider-arrow.next{left:auto;right:20px} -------------------------------------------------------------------------------- /assets/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/assets/images/bg.png -------------------------------------------------------------------------------- /assets/js/slider.min.js: -------------------------------------------------------------------------------- 1 | !function($){return $?($.Unslider=function(t,n){var e=this;return e._="unslider",e.defaults={autoplay:!1,delay:3e3,speed:750,easing:"swing",keys:{prev:37,next:39},nav:!0,arrows:{prev:'',next:''},animation:"horizontal",selectors:{container:"ul:first",slides:"li"},animateHeight:!1,activeClass:e._+"-active",swipe:!0,swipeThreshold:.2},e.$context=t,e.options={},e.$parent=null,e.$container=null,e.$slides=null,e.$nav=null,e.$arrows=[],e.total=0,e.current=0,e.prefix=e._+"-",e.eventSuffix="."+e.prefix+~~(2e3*Math.random()),e.interval=null,e.init=function(t){return e.options=$.extend({},e.defaults,t),e.$container=e.$context.find(e.options.selectors.container).addClass(e.prefix+"wrap"),e.$slides=e.$container.children(e.options.selectors.slides),e.setup(),$.each(["nav","arrows","keys","infinite"],function(t,n){e.options[n]&&e["init"+$._ucfirst(n)]()}),jQuery.event.special.swipe&&e.options.swipe&&e.initSwipe(),e.options.autoplay&&e.start(),e.calculateSlides(),e.$context.trigger(e._+".ready"),e.animate(e.options.index||e.current,"init")},e.setup=function(){e.$context.addClass(e.prefix+e.options.animation).wrap('
'),e.$parent=e.$context.parent("."+e._);var t=e.$context.css("position");"static"===t&&e.$context.css("position","relative"),e.$context.css("overflow","hidden")},e.calculateSlides=function(){if(e.total=e.$slides.length,"fade"!==e.options.animation){var t="width";"vertical"===e.options.animation&&(t="height"),e.$container.css(t,100*e.total+"%").addClass(e.prefix+"carousel"),e.$slides.css(t,100/e.total+"%")}},e.start=function(){return e.interval=setTimeout(function(){e.next()},e.options.delay),e},e.stop=function(){return clearTimeout(e.interval),e},e.initNav=function(){var t=$('');e.$slides.each(function(n){var i=this.getAttribute("data-nav")||n+1;$.isFunction(e.options.nav)&&(i=e.options.nav.call(e.$slides.eq(n),n,i)),t.children("ol").append('
  • '+i+"
  • ")}),e.$nav=t.insertAfter(e.$context),e.$nav.find("li").on("click"+e.eventSuffix,function(){var t=$(this).addClass(e.options.activeClass);t.siblings().removeClass(e.options.activeClass),e.animate(t.attr("data-slide"))})},e.initArrows=function(){e.options.arrows===!0&&(e.options.arrows=e.defaults.arrows),$.each(e.options.arrows,function(t,n){e.$arrows.push($(n).insertAfter(e.$context).on("click"+e.eventSuffix,e[t]))})},e.initKeys=function(){e.options.keys===!0&&(e.options.keys=e.defaults.keys),$(document).on("keyup"+e.eventSuffix,function(t){$.each(e.options.keys,function(n,i){t.which===i&&$.isFunction(e[n])&&e[n].call(e)})})},e.initSwipe=function(){var t=e.$slides.width();"fade"!==e.options.animation&&e.$container.on({movestart:function(t){return t.distX>t.distY&&t.distX<-t.distY||t.distX-t.distY?!!t.preventDefault():void e.$container.css("position","relative")},move:function(n){e.$container.css("left",-(100*e.current)+100*n.distX/t+"%")},moveend:function(n){Math.abs(n.distX)/t>e.options.swipeThreshold?e[n.distX<0?"next":"prev"]():e.$container.animate({left:-(100*e.current)+"%"},e.options.speed/2)}})},e.initInfinite=function(){var t=["first","last"];$.each(t,function(n,i){e.$slides.push.apply(e.$slides,e.$slides.filter(':not(".'+e._+'-clone")')[i]().clone().addClass(e._+"-clone")["insert"+(0===n?"After":"Before")](e.$slides[t[~~!n]]()))})},e.destroyArrows=function(){$.each(e.$arrows,function(t,n){n.remove()})},e.destroySwipe=function(){e.$container.off("movestart move moveend")},e.destroyKeys=function(){$(document).off("keyup"+e.eventSuffix)},e.setIndex=function(t){return 0>t&&(t=e.total-1),e.current=Math.min(Math.max(0,t),e.total-1),e.options.nav&&e.$nav.find('[data-slide="'+e.current+'"]')._active(e.options.activeClass),e.$slides.eq(e.current)._active(e.options.activeClass),e},e.animate=function(t,n){if("first"===t&&(t=0),"last"===t&&(t=e.total),isNaN(t))return e;e.options.autoplay&&e.stop().start(),e.setIndex(t),e.$context.trigger(e._+".change",[t,e.$slides.eq(t)]);var i="animate"+$._ucfirst(e.options.animation);return $.isFunction(e[i])&&e[i](e.current,n),e},e.next=function(){var t=e.current+1;return t>=e.total&&(t=0),e.animate(t,"next")},e.prev=function(){return e.animate(e.current-1,"prev")},e.animateHorizontal=function(t){var n="left";return"rtl"===e.$context.attr("dir")&&(n="right"),e.options.infinite&&e.$container.css("margin-"+n,"-100%"),e.slide(n,t)},e.animateVertical=function(t){return e.options.animateHeight=!0,e.options.infinite&&e.$container.css("margin-top",-e.$slides.outerHeight()),e.slide("top",t)},e.slide=function(t,n){if(e.options.animateHeight&&e._move(e.$context,{height:e.$slides.eq(n).outerHeight()},!1),e.options.infinite){var i;n===e.total-1&&(i=e.total-3,n=-1),n===e.total-2&&(i=0,n=e.total-2),"number"==typeof i&&(e.setIndex(i),e.$context.on(e._+".moved",function(){e.current===i&&e.$container.css(t,-(100*i)+"%").off(e._+".moved")}))}var o={};return o[t]=-(100*n)+"%",e._move(e.$container,o)},e.animateFade=function(t){var n=e.$slides.eq(t).addClass(e.options.activeClass);e._move(n.siblings().removeClass(e.options.activeClass),{opacity:0}),e._move(n,{opacity:1},!1)},e._move=function(t,n,i,o){return i!==!1&&(i=function(){e.$context.trigger(e._+".moved")}),t._move(n,o||e.options.speed,e.options.easing,i)},e.init(n)},$.fn._active=function(t){return this.addClass(t).siblings().removeClass(t)},$._ucfirst=function(t){return(t+"").toLowerCase().replace(/^./,function(t){return t.toUpperCase()})},$.fn._move=function(){return this.stop(!0,!0),$.fn[$.fn.velocity?"velocity":"animate"].apply(this,arguments)},void($.fn.unslider=function(t){return this.each(function(){var n=$(this);if("string"==typeof t&&n.data("unslider")){t=t.split(":");var e=n.data("unslider")[t[0]];if($.isFunction(e))return e.apply(n,t[1]?t[1].split(","):null)}return n.data("unslider",new $.Unslider(n,t))})})):console.warn("Unslider needs jQuery")}(window.jQuery); -------------------------------------------------------------------------------- /assets/js/stickup.min.js: -------------------------------------------------------------------------------- 1 | jQuery(function(t){"use strict";var e=function(){},o={},n=0;e.prototype={dataProperty:"data-menu",itemClass:"",itemHover:"",marginTop:0,beforeStick:null,afterStick:null,beforeUnstick:null,afterUnstick:null,region:"top",_selector:"",_jqDom:null,_menuItems:[],_height:0,_parentMarginTop:0,_top:0,_marginBottom:0,onScroll:function(e,o){var n=null,r=null,i=this;if(i._menuItems&&i._menuItems.length>0)for(var a=null,s=0,m=null,l=0;ls-50&&s+50>o&&(i._jqDom.find("."+i.itemClass).removeClass(i.itemHover),i._jqDom.find("."+i.itemClass+":eq("+l+")").addClass(i.itemHover)),"up"==e&&(n=.4*m.height(),r=s-n,o>r?(i._jqDom.find("."+i.itemClass).removeClass(i.itemHover),i._jqDom.find("."+i.itemClass+":eq("+l+")").addClass(i.itemHover)):50>o&&(i._jqDom.find("."+i.itemClass).removeClass(i.itemHover),i._jqDom.find("."+i.itemClass+":eq(0)").addClass(i.itemHover)));i._top0?a.marginTop=parseInt(r.topMargin.replace("px","")):isNaN(parseInt(r.topMargin))?(console.log("incorrect argument, ignored."),a.marginTop=0):a.marginTop=parseInt(r.topMargin):a.marginTop=0,a.itemClass=r.itemClass,a.itemHover=r.itemHover):console.log("warm:needs arguments"),a.dataProperty=r.dataProperty||a.dataProperty,a.region=r.region||a.region,a._height=parseInt(a._jqDom.height()),a._marginBottom=parseInt(a._jqDom.css("margin-bottom")),a._parentMarginTop=parseInt(a._jqDom.next().closest("div").css("margin-top")),a._top=parseInt(a._jqDom.offset().top),o[a._selector]=a},t.fn.stickUp=function(t){o[this.selector]||e._init_(this,t)},t(document).on("scroll",function(){var e=parseInt(t(document).scrollTop()),r=e>n?"down":"up";for(var i in o)o[i].onScroll(r,e);n=e})}); -------------------------------------------------------------------------------- /assets/unslider/css/unslider-dots.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Here's where everything gets included. You don't need 3 | * to change anything here, and doing so might break 4 | * stuff. Here be dragons and all that. 5 | */ 6 | /** 7 | * Default variables 8 | * 9 | * While these can be set with JavaScript, it's probably 10 | * better and faster to just set them here, compile to 11 | * CSS and include that instead to use some of that 12 | * hardware-accelerated goodness. 13 | */ 14 | .unslider-nav ol { 15 | list-style: none; 16 | text-align: center; 17 | } 18 | .unslider-nav ol li { 19 | display: inline-block; 20 | width: 6px; 21 | height: 6px; 22 | margin: 0 4px; 23 | background: transparent; 24 | border-radius: 5px; 25 | overflow: hidden; 26 | text-indent: -999em; 27 | border: 2px solid #fff; 28 | cursor: pointer; 29 | } 30 | .unslider-nav ol li.unslider-active { 31 | background: #fff; 32 | cursor: default; 33 | } 34 | -------------------------------------------------------------------------------- /assets/unslider/css/unslider.css: -------------------------------------------------------------------------------- 1 | .unslider{overflow:auto;margin:0;padding:0}.unslider-wrap{position:relative}.unslider-wrap.unslider-carousel>li{float:left}.unslider-vertical>ul{height:100%}.unslider-vertical li{float:none;width:100%}.unslider-fade{position:relative}.unslider-fade .unslider-wrap li{position:absolute;left:0;top:0;right:0;z-index:8}.unslider-fade .unslider-wrap li.unslider-active{z-index:10}.unslider li,.unslider ol,.unslider ul{list-style:none;margin:0;padding:0;border:none}.unslider-arrow{position:absolute;left:20px;z-index:2;cursor:pointer}.unslider-arrow.next{left:auto;right:20px} -------------------------------------------------------------------------------- /assets/unslider/js/unslider-min.js: -------------------------------------------------------------------------------- 1 | !function($){return $?($.Unslider=function(t,n){var e=this;return e._="unslider",e.defaults={autoplay:!1,delay:3e3,speed:750,easing:"swing",keys:{prev:37,next:39},nav:!0,arrows:{prev:'',next:''},animation:"horizontal",selectors:{container:"ul:first",slides:"li"},animateHeight:!1,activeClass:e._+"-active",swipe:!0,swipeThreshold:.2},e.$context=t,e.options={},e.$parent=null,e.$container=null,e.$slides=null,e.$nav=null,e.$arrows=[],e.total=0,e.current=0,e.prefix=e._+"-",e.eventSuffix="."+e.prefix+~~(2e3*Math.random()),e.interval=null,e.init=function(t){return e.options=$.extend({},e.defaults,t),e.$container=e.$context.find(e.options.selectors.container).addClass(e.prefix+"wrap"),e.$slides=e.$container.children(e.options.selectors.slides),e.setup(),$.each(["nav","arrows","keys","infinite"],function(t,n){e.options[n]&&e["init"+$._ucfirst(n)]()}),jQuery.event.special.swipe&&e.options.swipe&&e.initSwipe(),e.options.autoplay&&e.start(),e.calculateSlides(),e.$context.trigger(e._+".ready"),e.animate(e.options.index||e.current,"init")},e.setup=function(){e.$context.addClass(e.prefix+e.options.animation).wrap('
    '),e.$parent=e.$context.parent("."+e._);var t=e.$context.css("position");"static"===t&&e.$context.css("position","relative"),e.$context.css("overflow","hidden")},e.calculateSlides=function(){if(e.total=e.$slides.length,"fade"!==e.options.animation){var t="width";"vertical"===e.options.animation&&(t="height"),e.$container.css(t,100*e.total+"%").addClass(e.prefix+"carousel"),e.$slides.css(t,100/e.total+"%")}},e.start=function(){return e.interval=setTimeout(function(){e.next()},e.options.delay),e},e.stop=function(){return clearTimeout(e.interval),e},e.initNav=function(){var t=$('');e.$slides.each(function(n){var i=this.getAttribute("data-nav")||n+1;$.isFunction(e.options.nav)&&(i=e.options.nav.call(e.$slides.eq(n),n,i)),t.children("ol").append('
  • '+i+"
  • ")}),e.$nav=t.insertAfter(e.$context),e.$nav.find("li").on("click"+e.eventSuffix,function(){var t=$(this).addClass(e.options.activeClass);t.siblings().removeClass(e.options.activeClass),e.animate(t.attr("data-slide"))})},e.initArrows=function(){e.options.arrows===!0&&(e.options.arrows=e.defaults.arrows),$.each(e.options.arrows,function(t,n){e.$arrows.push($(n).insertAfter(e.$context).on("click"+e.eventSuffix,e[t]))})},e.initKeys=function(){e.options.keys===!0&&(e.options.keys=e.defaults.keys),$(document).on("keyup"+e.eventSuffix,function(t){$.each(e.options.keys,function(n,i){t.which===i&&$.isFunction(e[n])&&e[n].call(e)})})},e.initSwipe=function(){var t=e.$slides.width();"fade"!==e.options.animation&&e.$container.on({movestart:function(t){return t.distX>t.distY&&t.distX<-t.distY||t.distX-t.distY?!!t.preventDefault():void e.$container.css("position","relative")},move:function(n){e.$container.css("left",-(100*e.current)+100*n.distX/t+"%")},moveend:function(n){Math.abs(n.distX)/t>e.options.swipeThreshold?e[n.distX<0?"next":"prev"]():e.$container.animate({left:-(100*e.current)+"%"},e.options.speed/2)}})},e.initInfinite=function(){var t=["first","last"];$.each(t,function(n,i){e.$slides.push.apply(e.$slides,e.$slides.filter(':not(".'+e._+'-clone")')[i]().clone().addClass(e._+"-clone")["insert"+(0===n?"After":"Before")](e.$slides[t[~~!n]]()))})},e.destroyArrows=function(){$.each(e.$arrows,function(t,n){n.remove()})},e.destroySwipe=function(){e.$container.off("movestart move moveend")},e.destroyKeys=function(){$(document).off("keyup"+e.eventSuffix)},e.setIndex=function(t){return 0>t&&(t=e.total-1),e.current=Math.min(Math.max(0,t),e.total-1),e.options.nav&&e.$nav.find('[data-slide="'+e.current+'"]')._active(e.options.activeClass),e.$slides.eq(e.current)._active(e.options.activeClass),e},e.animate=function(t,n){if("first"===t&&(t=0),"last"===t&&(t=e.total),isNaN(t))return e;e.options.autoplay&&e.stop().start(),e.setIndex(t),e.$context.trigger(e._+".change",[t,e.$slides.eq(t)]);var i="animate"+$._ucfirst(e.options.animation);return $.isFunction(e[i])&&e[i](e.current,n),e},e.next=function(){var t=e.current+1;return t>=e.total&&(t=0),e.animate(t,"next")},e.prev=function(){return e.animate(e.current-1,"prev")},e.animateHorizontal=function(t){var n="left";return"rtl"===e.$context.attr("dir")&&(n="right"),e.options.infinite&&e.$container.css("margin-"+n,"-100%"),e.slide(n,t)},e.animateVertical=function(t){return e.options.animateHeight=!0,e.options.infinite&&e.$container.css("margin-top",-e.$slides.outerHeight()),e.slide("top",t)},e.slide=function(t,n){if(e.options.animateHeight&&e._move(e.$context,{height:e.$slides.eq(n).outerHeight()},!1),e.options.infinite){var i;n===e.total-1&&(i=e.total-3,n=-1),n===e.total-2&&(i=0,n=e.total-2),"number"==typeof i&&(e.setIndex(i),e.$context.on(e._+".moved",function(){e.current===i&&e.$container.css(t,-(100*i)+"%").off(e._+".moved")}))}var o={};return o[t]=-(100*n)+"%",e._move(e.$container,o)},e.animateFade=function(t){var n=e.$slides.eq(t).addClass(e.options.activeClass);e._move(n.siblings().removeClass(e.options.activeClass),{opacity:0}),e._move(n,{opacity:1},!1)},e._move=function(t,n,i,o){return i!==!1&&(i=function(){e.$context.trigger(e._+".moved")}),t._move(n,o||e.options.speed,e.options.easing,i)},e.init(n)},$.fn._active=function(t){return this.addClass(t).siblings().removeClass(t)},$._ucfirst=function(t){return(t+"").toLowerCase().replace(/^./,function(t){return t.toUpperCase()})},$.fn._move=function(){return this.stop(!0,!0),$.fn[$.fn.velocity?"velocity":"animate"].apply(this,arguments)},void($.fn.unslider=function(t){return this.each(function(){var n=$(this);if("string"==typeof t&&n.data("unslider")){t=t.split(":");var e=n.data("unslider")[t[0]];if($.isFunction(e))return e.apply(n,t[1]?t[1].split(","):null)}return n.data("unslider",new $.Unslider(n,t))})})):console.warn("Unslider needs jQuery")}(window.jQuery); -------------------------------------------------------------------------------- /demo/bangumi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 |
    18 |
    19 | 55 |
    56 |
    57 |
    58 | 59 |
    60 | 61 |
    62 |

    Re:从零开始的异世界生活

    63 |
    64 | 65 |
    66 |
    67 |

    声优:小林裕介 、高桥李依 、内山夕实 、赤崎千夏 、水濑祈 、村川梨衣 、新井里美 、中村悠一

    68 |

    动画制作:WHITE FOX

    69 |

    在异世界陷入迷茫的普通高中生菜月昴,邂逅了一位银发的美少女。但想助她一臂之力的昴,却一次次地遭遇敌袭,背叛、暴力,甚至是死亡……“死亡重置” 70 | ——无力的少年拥有的唯一能力,能将死后时间倒转回一开始。使用了这般力量,便会失去过去的回忆,可为了守护最重要的人们,昴必须抗争到底。“即使你忘却了我,我也不会遗忘你。” 71 |

    72 |

    播放数:4469.3万 | 弹幕数:5550

    73 | 74 |
    75 |
    76 | 77 | 78 |
    79 |
    80 |
    81 | 82 |
    83 |
    84 | FATE/EXTRA 85 |

    Fate(フェイト)系列作品最早的是由TYPE-MOON于2004年1月30日发售的PC平台18禁文字冒险游戏《Fate/stay 86 | night》,同时也是TYPE-MOON商业化后初次亮相的作品。由于广受欢迎,吸引了一些作家参与制作,由本篇衍生诸多人气作品。

    87 |
    88 |
    播放: 2333
    89 |
    收藏: 333
    90 |
    更新时间:2013-02-16
    91 |
    92 |
    93 |
    94 | 95 |
    96 |
    97 | 98 |
    99 |
    100 | FATE/EXTRA 101 |

    Fate(フェイト)系列作品最早的是由TYPE-MOON于2004年1月30日发售的PC平台18禁文字冒险游戏《Fate/stay 102 | night》,同时也是TYPE-MOON商业化后初次亮相的作品。由于广受欢迎,吸引了一些作家参与制作,由本篇衍生诸多人气作品。

    103 |
    104 |
    播放: 2333
    105 |
    收藏: 333
    106 |
    更新时间:2013-02-16
    107 |
    108 |
    109 |
    110 |
    111 | 112 |
    113 | 114 |
    115 |

    剧集:

    116 |
      117 |
    • 1
    • 118 |
    • 1
    • 119 |
    • 1
    • 120 |
    • 1
    • 121 |
    • 1
    • 122 |
    • 1
    • 123 |
    • 1
    • 124 |
    125 |
    126 | 127 |
    128 | 129 |
    130 | 131 | 132 | 133 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /demo/bangumi_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 |
    18 |
    19 | 55 |
    56 |
    57 |
    58 | 59 |
    60 | 61 |
    62 |
    63 |
    64 | 71 |
    72 |
    73 |
    74 | 75 |
    76 |
    77 |
    78 |

    番剧推荐

    79 |
    80 | 81 |
    82 |
    83 | 84 | 85 | 86 |
    20:04
    87 | 88 | 标题: 【东方手绘剧场】东方现世幻想侭 -预告-【莳乃汉化组】 89 | UP主: 莳乃字幕屋 90 | 91 |
    92 |
    93 | 播放: 2333 94 |
    95 |
    96 | 弹幕: 233 97 |
    98 |
    99 |
    100 | 101 |
    102 |
    103 | 104 |
    105 |
    106 | 107 | 108 |
    109 |
    110 |
    111 |

    番剧列表

    112 |
    113 |
    114 | 115 |
    116 |
    117 | 118 |
    119 |
    120 |

    乌冬面之国的金色毛球

    121 |

    更新到: 第10集

    122 |
    123 |
    124 |
    125 |
    126 | 136 |
    137 |
    138 | 139 |
    140 | 显示/隐藏 141 |
    142 | 143 |
    144 |
    145 | 146 |
    147 | 148 | 149 | 150 | 151 | 152 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 |
    18 |
    19 | 56 |
    57 |
    58 |
    59 | 60 |
    61 | 62 | 124 | 125 |
    126 |
    127 |
    128 | 129 |

    番剧

    130 | 131 |
    132 | 133 | 134 |
    135 | 136 | 137 |
    138 |
    139 | 140 | 141 | 142 |
    20:04
    143 | 144 | 标题: 【东方手绘剧场】东方现世幻想侭 -预告-【莳乃汉化组】 145 | UP主: 莳乃字幕屋 146 | 147 |
    148 |
    149 | 播放: 2333 150 |
    151 |
    152 | 弹幕: 233 153 |
    154 |
    155 |
    156 | 157 |
    158 | 159 | 160 | 161 |
    20:04
    162 | 163 | 标题: 【东方手绘剧场】东方现世幻想侭 -预告-【莳乃汉化组】 164 | UP主: 莳乃字幕屋 165 | 166 |
    167 |
    168 | 播放: 2333 169 |
    170 |
    171 | 弹幕: 233 172 |
    173 |
    174 |
    175 | 176 |
    177 | 178 | 179 | 180 |
    20:04
    181 | 182 | 标题: 【东方手绘剧场】东方现世幻想侭 -预告-【莳乃汉化组】 183 | UP主: 莳乃字幕屋 184 | 185 |
    186 |
    187 | 播放: 2333 188 |
    189 |
    190 | 弹幕: 233 191 |
    192 |
    193 |
    194 | 195 |
    196 | 197 | 198 | 199 |
    20:04
    200 | 201 | 标题: 【东方手绘剧场】东方现世幻想侭 -预告-【莳乃汉化组】 202 | UP主: 莳乃字幕屋 203 | 204 |
    205 |
    206 | 播放: 2333 207 |
    208 |
    209 | 弹幕: 233 210 |
    211 |
    212 |
    213 | 214 |
    215 | 216 | 217 | 218 |
    20:04
    219 | 220 | 标题: 【东方手绘剧场】东方现世幻想侭 -预告-【莳乃汉化组】 221 | UP主: 莳乃字幕屋 222 | 223 |
    224 |
    225 | 播放: 2333 226 |
    227 |
    228 | 弹幕: 233 229 |
    230 |
    231 |
    232 | 233 |
    234 | 235 |
    236 | 237 |
    238 |
    239 | 240 |
    241 |
    242 |
    243 | 244 |

    动画

    245 | 246 |
    247 | 248 | 249 |
    250 |
    251 |
    252 | 253 |
    254 |
    255 |
    256 | 257 |

    音乐

    258 | 259 |
    260 | 261 | 262 |
    263 |
    264 |
    265 | 266 | 267 |
    268 | 269 | 297 | 298 | 299 | 300 | 301 | 302 | 314 | 315 | 316 | 317 | -------------------------------------------------------------------------------- /demo/play.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 |
    29 |
    31 |
    32 |
    33 | 69 |
    70 |
    71 | 72 |
    73 |
    74 |
    75 |
    76 |
    77 |
    78 |

    【4月】黑色残骸 05【生肉】

    79 |
    80 | 81 |
    82 |
    分类: 连载动画
    83 |
    时间: 2016-05-05 21:50
    84 |
    85 |
    86 | 87 |
    88 |
    播放: 23233
    89 |
    弹幕: 2333
    90 |
    硬币: 23
    91 |
    92 |
    93 | 94 |
    95 |
    96 |
    97 | 98 | 99 |
    100 |
    101 | 八云境界 102 |
    【F宅字幕组】【群:233862998】~境界无处不在~~我与你之间的境界在哪呢~
    103 |
    104 |
    105 |
    106 |
    107 |
    108 |
    109 | 110 |
    111 |
    112 |
    113 | 1、失去的存在 114 |
    115 |
    116 | 117 | 121 |
    122 |
    123 | 124 |
    125 |
    126 |
    127 |
      128 |
    • 黑色残骸
    • 129 |
    • クロムクロ
    • 130 |
      131 |
    132 |
    133 |
    ~ 第5話 「学び舎に来た男」
    134 | 135 |
    136 | 评论 137 |

    (施工中)

    138 |
    139 |
    140 |
    141 |
    142 | 143 |
    144 | 145 | 173 | 174 | 175 | 176 | 177 | 178 | 196 | 197 | 198 | 199 | -------------------------------------------------------------------------------- /demo/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    16 |
    18 |
    19 | 55 |
    56 |
    57 |
    58 | 59 |
    60 |
    61 |
    62 |
    63 | 64 |
    65 |
    搜索
    66 |
    67 |
    68 | 69 |
    70 |
      71 |
    • 视频
    • 72 |
    • 番剧
    • 73 |
    • 专题
    • 74 |
    • UP主
    • 75 |
    76 |
    77 |
    78 | 79 |
    80 |
      81 |
    • 综合排序
    • 82 |
    • 最多点击
    • 83 |
    • 最新发布
    • 84 |
    85 |
    86 |
    87 |
    88 | 89 |
    90 |
    91 | 92 |
    93 |
    94 | 95 |
    96 |
    97 | FATE/EXTRA 98 |

    Fate(フェイト)系列作品最早的是由TYPE-MOON于2004年1月30日发售的PC平台18禁文字冒险游戏《Fate/stay 99 | night》,同时也是TYPE-MOON商业化后初次亮相的作品。由于广受欢迎,吸引了一些作家参与制作,由本篇衍生诸多人气作品。

    100 |
    101 |
    播放: 2333
    102 |
    收藏: 333
    103 |
    更新时间:2013-02-16
    104 |
    105 |
    106 |
    107 | 108 |
    109 |
    110 | 111 |
    112 |
    113 | FATE/EXTRA 114 |

    Fate(フェイト)系列作品最早的是由TYPE-MOON于2004年1月30日发售的PC平台18禁文字冒险游戏《Fate/stay 115 | night》,同时也是TYPE-MOON商业化后初次亮相的作品。由于广受欢迎,吸引了一些作家参与制作,由本篇衍生诸多人气作品。

    116 |
    117 |
    播放: 2333
    118 |
    收藏: 333
    119 |
    更新时间:2013-02-16
    120 |
    121 |
    122 |
    123 | 124 |
    125 | 126 |
    127 | 128 | 19:58 129 |
    130 |
    131 |
    132 | 这是你看过的Fate系列吗? 133 |
    134 |
    播放: 2333
    135 |
    时间:2013-02-16
    136 |
    Up: 蓝白
    137 |
    138 |
    139 |
    140 | 141 |
    142 | 143 |
    144 | 145 | 19:58 146 |
    147 |
    148 |
    149 | 这是你看过的Fate系列吗? 150 |
    151 |
    播放: 2333
    152 |
    时间:2013-02-16
    153 |
    Up: 蓝白
    154 |
    155 |
    156 |
    157 | 158 |
    159 | 160 |
    161 | 162 | 19:58 163 |
    164 |
    165 |
    166 | 这是你看过的Fate系列吗? 167 |
    168 |
    播放: 2333
    169 |
    时间:2013-02-16
    170 |
    Up: 蓝白
    171 |
    172 |
    173 |
    174 | 175 |
    176 | 177 |
    178 | 179 | 19:58 180 |
    181 |
    182 |
    183 | 这是你看过的Fate系列吗? 184 |
    185 |
    播放: 2333
    186 |
    时间:2013-02-16
    187 |
    Up: 蓝白
    188 |
    189 |
    190 |
    191 | 192 |
    193 | 194 |
    195 | 196 | 19:58 197 |
    198 |
    199 |
    200 | 这是你看过的Fate系列吗? 201 |
    202 |
    播放: 2333
    203 |
    时间:2013-02-16
    204 |
    Up: 蓝白
    205 |
    206 |
    207 |
    208 | 209 |
    210 |
    211 |
    212 | 213 |
    214 | 215 | 216 | 217 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /demo/sort.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |
    17 |
    18 |
    19 | 55 |
    56 |
    57 | 58 |
    59 |
    60 |
    61 |
    62 | 65 |
    66 | 最新发布 67 | 点击最高 68 |
    69 |
    70 | 71 |
    72 |
    73 |
    74 |
    75 | 76 |
    77 | 78 |
    79 |
    80 |
    81 |
    播放: 2333
    82 |
    弹幕: 2333
    83 |
    84 |
    85 |
    86 | 87 | 源自NICO,sm28768651 ゴーストルール 歌ってみた【夏代孝明とnqrse】. 88 | 89 | up: WhiteBlue 90 |

    自制,做字幕到一半vge崩了,效果不够好,唱得也不好,抱歉辣
    使用了武士桑的吉他翻弹做伴奏,特别感谢.

    91 |
    92 |
    93 |
    94 |
    95 |
    96 | 97 |
    98 | 99 |
    100 | 首页 101 | 上一页 102 | 4 103 | 5 104 | 6 105 | 7 106 | 8 107 | 9 108 | 10 109 | 下一页 110 |
    111 | 112 | 113 |
    114 |
    115 | 118 |
    119 |
    120 |
    121 | 122 |
    123 |
    124 | 【猫田琹茶籽】サリシノハラ 125 | 猫田琹茶籽 126 |
    127 |
    播放: 2333
    128 |
    弹幕: 2333
    129 |
    130 |
    131 |
    132 |
    133 |
    134 |
    135 |
    136 |
    137 |
    138 | 139 | 140 | 141 | 142 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /docs/bh5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/docs/bh5.png -------------------------------------------------------------------------------- /docs/css/main.css: -------------------------------------------------------------------------------- 1 | .bangumi-banner-block{margin:20px auto;width:640px;height:200px;position:relative;overflow:hidden}.bangumi-banner{width:640px;height:200px}.area-banner>h2{margin:20px 10px;font-size:25px;font-weight:700;color:#666}.bangumi_item{width:200px;height:120px;margin:10px 20px;border-radius:4px;border:1px solid #f6f6f6;overflow:hidden;-webkit-transition:border .2s ease;-moz-transition:border .2s ease;-ms-transition:border .2s ease;-o-transition:border .2s ease;transition:border .2s ease}.bangumi_item:hover{border:1px solid #666;box-shadow:0 0 5px #b3b2b2}.bangumi_item:hover>.info>.title{color:#e04270}.bangumi_item>.img-small{width:99px;height:119px;overflow:hidden}.bangumi_item>.info{width:99px;height:119px;padding:0 5px;overflow:hidden}.bangumi_item>.info>.title{display:block;padding:5px 5px;position:relative;font-size:13px;font-weight:700;color:#000;height:60px;overflow:hidden}.bangumi_item>.info>.other{display:block;padding:10px 5px;position:relative;font-size:11px;font-weight:700;color:#666;max-height:50px;overflow:hidden}.img-small>img{width:100%}.list-bangumi-block{width:960px;max-height:140px;overflow:hidden;margin:20px auto}.toggle-btn{width:100px;height:30px;margin:10px auto;border-radius:4px;border:1px solid #666}.toggle-btn>a{display:block;text-align:center;margin:5px auto;font-size:13px;font-weight:700;color:#555}.toggle-btn>a:hover{color:#e04270}.bangumi-header{width:1000px;min-height:400px;margin:20px auto;background-color:#fff}.bangumi-header>h2{display:block;position:relative;left:150px;top:25px;width:600px;color:#525659;overflow:hidden;line-height:24px;text-overflow:ellipsis;font-weight:400;white-space:nowrap;font-size:20px}.bangumi-header-img{float:left;position:relative;left:50px;top:25px;width:200px;height:293px}.bangumi-header-img>img{width:200px}.bangumi-header-info{float:left;position:relative;left:150px;top:80px;width:600px;min-height:250px;color:#666;font-weight:700}.bangumi-header-info>p{font-size:14px;line-height:150%;font-weight:100}.bangumi-header-info>span{font-size:14px;line-height:150%;font-weight:100}.bangumi-part{background-color:#fff;width:1000px;min-height:200px}.bangumi-part>ul{margin:0 0;position:relative;top:40px;left:35px;width:90%}.bangumi-part>p{position:relative;top:20px;left:30px;font-size:25px;font-weight:700;color:#666}.bangumi-part>ul>li{display:block;margin:5px 10px;min-width:64px;padding:0 5px;height:32px;border:1px solid #999;border-radius:4px;-webkit-transition:background-color .2s ease;-moz-transition:background-color .2s ease;-ms-transition:background-color .2s ease;-o-transition:background-color .2s ease;transition:background-color .2s ease}.bangumi-part>ul>li>a{display:block;height:32px;text-decoration:none;text-align:center}.bangumi-part>ul>li:hover{background-color:#aeb7bf}.bangumi-part-span{text-decoration:none;color:#999;position:relative;top:6px}.bangumi-part>ul>li:hover .video_more_span{color:#fff}.bangumi-season{background-color:#fff;margin:20px auto;padding:0 10px;width:1000px}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;margin:0 auto;padding:0;outline:0;border:0;vertical-align:baseline;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;zoom:1}a{color:#369;text-decoration:none;white-space:normal;word-break:break-all;cursor:pointer;font-size:12px;font-weight:400}body{background-color:#f6f6f6;position:static;font-size:12px;font-family:"Helvetica Neue",Helvetica,Arial,STHeiti,"Microsoft Yahei",sans-serif;line-height:16px;-webkit-font-smoothing:antialiased}.clear{clear:both}.floatleft{position:relative;float:left}.floatright{position:relative;float:right}#main-container{width:100%;margin:40px 0 0 0;padding:0}#main-container.concat{margin-top:0}#main-container>#index-head-area{padding-top:26px}.area{position:relative}.area-inner{width:980px;margin:0 auto}.area-banner{position:relative;width:auto;height:36px;color:#666;text-align:left;font-size:14px;line-height:34px;zoom:1;margin:0;padding:0}.area-banner-ch{position:relative;margin-right:10px;display:inline-block}.area-banner-ch>h3{display:block;font-size:16px;letter-spacing:.06em;font-weight:400;color:#fff;background-color:#00a1d6;padding:0 24px;height:26px;line-height:26px;border-radius:6px}.area-banner-line-left{position:absolute;width:50%;height:7px;bottom:-7px;background-color:#f6f6f6;z-index:2;left:0;border-right:1px solid #00a1d6}.area-banner-line-right{display:block;position:absolute;width:100%;border-top:1px solid #00a1d6;font-size:14px}.area-banner-line-circle{position:absolute;width:6px;height:6px;background-color:#ffa5c7;right:0;bottom:-1px}.list-video-block{margin:20px auto}.list-video-block>.video-block{margin:10px 18px}.video-block{width:160px;height:145px;overflow:hidden}.video-block-main{display:block;width:160px;height:90px;overflow:hidden}.video-block-main>img{width:160px;height:100%;border-radius:16px 12px 12px 12px}.video-block-info{-webkit-transition:color .2s ease,height .2s ease;-moz-transition:color .2s ease,height .2s ease;-ms-transition:color .2s ease,height .2s ease;-o-transition:color .2s ease,height .2s ease;transition:color .2s ease,height .2s ease;display:block;width:100%;margin:8px 0;font-size:12px;color:#000;line-height:16px;height:18px;overflow:hidden}.video-block-time{-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;-ms-transition:opacity .2s ease;-o-transition:opacity .2s ease;transition:opacity .2s ease;position:absolute;color:#fff;background-color:rgba(0,0,0,.7);padding:0 5px;top:72px;right:0;height:18px;overflow:hidden;opacity:0;border-radius:0 0 12px 0}.video-block:hover .video-block-time{opacity:1}.video-block-info-hidden{color:#999;height:24px;line-height:24px;overflow:hidden;font-size:11px;top:114px;left:0}.video-block:hover .video-block-info{height:32px}.video-block-info-hidden>.left{margin-left:2px}.video-block-info-hidden>.right{margin-right:2px}.video-block-info:hover{color:#ffa5c7}.video-block-info:visited{color:#888}.video-block-mask{width:160px;height:100px;overflow:hidden}.video-block-mask-preview{height:100px;background:0;overflow:hidden;border-radius:4px}.video-block-mask-preview>img{height:100%;margin:0 auto;display:block}.video-block-mask-mask{position:absolute;width:100%;height:100%;opacity:0;visibility:hidden;top:0;left:0;background:#000;background:rgba(0,0,0,.7);filter:alpha(opacity=70);z-index:1;transition:.3s all linear;border-radius:4px}.video-block-mask:hover .video-block-mask-mask{visibility:visible;opacity:1}.video-block-mask-info{height:30px;padding:0;overflow:hidden;position:absolute;top:70px;width:100%;bottom:0;left:0;z-index:1;background:-webkit-linear-gradient(transparent,rgba(0,0,0,.1) 20%,rgba(0,0,0,.2) 35%,rgba(0,0,0,.6) 65%,rgba(0,0,0,.9));background:-o-linear-gradient(transparent,rgba(0,0,0,.1) 20%,rgba(0,0,0,.2) 35%,rgba(0,0,0,.6) 65%,rgba(0,0,0,.9));background:-moz-linear-gradient(transparent,rgba(0,0,0,.1) 20%,rgba(0,0,0,.2) 35%,rgba(0,0,0,.6) 65%,rgba(0,0,0,.9));background:linear-gradient(transparent,rgba(0,0,0,.1) 20%,rgba(0,0,0,.2) 35%,rgba(0,0,0,.6) 65%,rgba(0,0,0,.9));border-radius:0 0 4px 4px}.video-block-mask:hover .video-block-mask-info{top:0;height:70px;background:0 0}.video-block-mask:hover .play,.video-block-mask:hover .up{opacity:1}.video-block-mask-info>.title{height:36px;margin:10px 5px 0 5px;line-height:18px;overflow:hidden;color:#fff;font-size:12px;word-break:break-all;word-wrap:break-word}.video-block-mask-info>.up{margin:10px 0 5px 5px}.video-block-mask-info>.play{margin:5px 5px 0}.video-block-mask-info>.play,.video-block-mask-info>.up{color:#99a2aa;transition:.4s all linear;opacity:0;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}#main-header{z-index:2;width:100%}#main-header.hidden-background .main-header-image{display:none}.main-header-nav{background-color:#fff;border-bottom:1px solid #ffa5c7;-webkit-box-shadow:0 1px 8px rgba(0,0,0,.3);box-shadow:0 1px 8px rgba(0,0,0,.3)}.main-header-nav-body{height:50px;width:980px;margin:0 auto}.main-header-nav-body>.nav-li{position:relative;font-size:14px;display:block;height:50px}.nav-li>a{color:#222;display:block;font-size:14px;margin:15px 20px auto 20px}.nav-li>a:hover{color:#00a1d6}.nav-li .nav-li-list{z-index:10;background-color:#fff;display:none;width:128px;position:absolute;top:50px;box-shadow:0 2px 3px #ccc;overflow:hidden}.nav-li-list:hover,.nav-li:hover .nav-li-list,.nav-li>a:hover .nav-li-list{display:block}.nav-li-list>a{display:block;color:#000;width:118px;height:34px;margin:10px auto 0 10px;padding-top:5px;font-size:14px;transition:.3s}.nav-li-list>a:hover{color:#ffa5c7;border-right:2px solid #00a1d6}.nav-li.now{border-bottom:2px solid #00a1d6}.main-header-image{width:100%;height:300px;background:transparent no-repeat fixed center;position:relative}.header-search{width:271px;height:32px;background-color:#e5e9ef;background-color:rgba(0,0,0,.12);border-radius:6px;margin:8px 20px auto 20px}.header-container{width:100%;background-color:#fff;z-index:3}.header-search input{float:left;width:190px;height:35px;line-height:35px;border:1px solid #00a1d6;box-shadow:0 0 4px rgba(0,0,0,.05) inset;border-right:0;margin:0;padding:0 10px 0 30px;border-radius:8px 0 0 8px;box-sizing:border-box;color:#999;font-size:12px}.btn:hover{background-color:#bbb}.btn{height:28px;line-height:28px;padding:0 12px;border-radius:1px 0 0 1px;color:#333;border-color:#bbb}.btn.active{border:0;background-color:#288ecf;color:#fff}.header-search .btn-search{border-radius:0 8px 8px 0;width:80px;height:35px;margin:0;padding-left:10px;border:1px solid #00a1d6;background-color:#00a1d6;color:#fff;font-size:16px;cursor:pointer;overflow:hidden}.btn-search:hover{background-color:#76c4d2;border-color:#76c4d2}.unslider-banner{width:450px;overflow:hidden}.index-banner{margin-top:20px;width:430px;height:215px}.block-banner{float:left;overflow:hidden;border-radius:12px}.block-banner img{height:100%}.block-banner>ul{height:100%;width:500%;position:absolute;left:0}.block-banner>.info-bottom{height:34px;line-height:40px;padding:0 80px 0 11px;cursor:auto;position:absolute;bottom:0;left:0;background:rgba(0,0,0,.45);background:-webkit-linear-gradient(bottom,rgba(0,0,0,.7) 0,rgba(0,0,0,0) 100%);background:-moz-linear-gradient(bottom,rgba(0,0,0,.7) 0,rgba(0,0,0,0) 100%);background:-ms-linear-gradient(bottom,rgba(0,0,0,.7) 0,rgba(0,0,0,0) 100%);background:-o-linear-gradient(bottom,rgba(0,0,0,.7) 0,rgba(0,0,0,0) 100%);background:linear-gradient(bottom,rgba(0,0,0,.7) 0,rgba(0,0,0,0) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#66000000, endColorstr=#66000000);width:100%;color:#fff;text-shadow:1px 1px 2px rgba(0,0,0,.6)}.unslider .unslider-nav{display:block;position:absolute;right:18px;bottom:6px;height:14px}.unslider .unslider-nav li{display:block;cursor:pointer;margin:0 4px;width:10px;height:10px;border-radius:5px;background-color:#00a1d6;float:left;line-height:0;font-size:0}#index-head-area .left-block{width:450px;margin:0 auto;height:300px}#index-head-area .right-block{width:530px}.right-block>.video-block-mask{margin:14px 8px}.sort-left>.banner{height:28px;line-height:28px;border-bottom:1px solid #ddd}.banner>p{font-size:20px;color:#333;height:25px;width:auto;line-height:20px;padding:0;overflow:hidden}.area-inner>.sort-left{width:700px}.area-inner>.sort-right{margin-left:30px;width:250px}.area-inner>.sort-right>.banner{border-bottom:1px solid #e84c3d}.sort-tool-box{width:360px;margin:12px 0 0 0}.sort-tool-box>.btn{margin:5px 15px 5px 0;cursor:pointer}.list-sort-block{margin-top:10px;border-top:1px solid #e5e9ef}.video-item{border-right:1px solid #e5e9ef;border-bottom:1px solid #e5e9ef}.video-block-small{width:330px;height:94px;margin:10px 9px 40px 9px;overflow:hidden}.video-block-small>.left{width:150px}.video-block-small>.right{width:180px;padding-left:10px}.video-block-small .thumb{width:150px;height:94px;overflow:hidden}.video-block-small .thumb>img{margin:0 12px 0 0;height:100%}.video-block-small .info{position:absolute;height:22px;line-height:26px;padding:0 3px;color:#ccc;bottom:0;left:0;width:150px;top:auto;background-color:transparent;background-color:rgba(0,0,0,.45);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#66000000, endColorstr=#66000000)}.info>.info-text{color:#ccc;font-size:11px}.video-block-small .title{font-size:14px;line-height:16px;height:32px;width:170px;margin:0;color:#333;overflow:hidden;word-break:break-all;-webkit-transition:color .2s ease;-moz-transition:color .2s ease;-ms-transition:color .2s ease;-o-transition:color .2s ease;transition:color .2s ease}.video-block-small .title:hover{color:#e04270}.up{display:block;height:16px;overflow:hidden;margin:8px 0;color:#ff9101}.up:hover{color:#e84c3d}.video-block-small .desc{color:#999;display:inline-block;overflow:hidden;line-height:16px;height:16px}.side-video-list{width:100%;margin-top:20px}.side-video-item{height:65px;margin:0 0 12px;border-bottom:1px solid #eee;padding:0 4px;position:relative}.side-video-item>.left{display:block;width:74px}.side-video-item>.left>img{width:74px;height:41px}.side-video-item>.right{width:150px;margin:0 auto 0 10px}.side-video-item .title{display:block;width:150px;font-size:12px;height:16px;line-height:16px;overflow:hidden;-webkit-transition:color .2s ease;-moz-transition:color .2s ease;-ms-transition:color .2s ease;-o-transition:color .2s ease;transition:color .2s ease;color:#888}.side-video-item .title:hover{color:#e04270}#footer{margin-top:50px;width:100%;height:230px;background-color:#eee}.about-block{margin-top:40px;overflow:hidden}.about-item{width:320px;height:150px;padding-left:20px}.about-item.left{border-right:1px solid #cfd3d8}.about-item.right{border-left:1px solid #cfd3d8}.about-title{text-align:left;list-style:none;line-height:22px;font-size:14px;color:#7e858c}.about-links>a{margin:3px 0;display:block;color:#222;line-height:22px;font-size:13px}.about-links>a:hover{color:#00a1d6}.about-links{margin-top:20px}.area-player{margin-top:30px;margin-bottom:30px}.video-info{width:100%;padding:0 0 10px 0;background-color:#FFF;border-bottom:1px solid #dadde3}.video-info .left{padding:5px 0 8px 0;width:640px;overflow:hidden}.video-info .right{width:240px;overflow:hidden}.video-info .title>h1{display:block;color:#525659;overflow:hidden;line-height:24px;width:100%;text-overflow:ellipsis;font-weight:400;white-space:nowrap;padding:8px 0;font-size:20px}.video-info .info{width:400px;margin:10px auto}.video-info .play-info>.info-text{color:#99a2aa;font-size:11px;margin-right:20px}.video-info .info>.info-text{color:#99a2aa;font-size:13px}.video-info .up-face{margin:15px 15px 0 0}.video-info .up-face>a>img{display:block;height:64px;width:64px;border-radius:64px;margin:0 auto;border:2px solid transparent;position:relative;z-index:1}.video-info .up-info{margin:15px auto 10px 20px}.up-info>.title{max-width:120px;font-size:14px;word-wrap:break-word;overflow:hidden;word-break:break-all;text-overflow:ellipsis;height:20px;white-space:nowrap}.up-info>.brief{width:140px;color:#333;line-height:20px;min-height:11px;overflow:hidden;word-wrap:break-word}.video-brief{padding:30px 0;border-top:1px solid #dadde3;border-bottom:1px solid #dadde3}.video-brief .desc{max-height:120px;overflow:hidden;white-space:pre-wrap}.video-brief .tags{width:100%;margin:20px 0 30px 0;border-bottom:1px solid #dadde3}.video-brief .tags>li{list-style-type:none;margin:0 10px 8px 0;border:1px solid #00a1d6;border-radius:20px;padding:0 10px;position:relative;height:22px}.label{display:inline-block;font-size:18px;line-height:24px}.comments{margin-top:30px}.video-part-select{margin-bottom:8px;overflow:hidden}.video-part-select>span.active{color:#fff;background-color:#00a1d6;border-color:#00a1d6;cursor:not-allowed}.video-part-select>span{display:block;color:#222;border-radius:4px;border:1px solid #ccd0d7;overflow:hidden;padding:3px 7px 3px 7px;margin:0 20px 12px 0;background:#fff;height:23px;line-height:17px;min-width:117px;text-overflow:ellipsis;white-space:nowrap;transition:.15s;transition-property:background-color,border-color,color;cursor:pointer}.video-part-select>span:hover{color:#fff;background-color:#00a1d6}.search-info{padding-top:30px;padding-bottom:30px;background-color:#fff;border-bottom:1px solid #dadde3}.search-block{height:42px;width:430px}.search-block>.input-wrap{height:100%;position:relative;width:330px;border-radius:4px;margin-right:10px}.input-wrap>input{width:300px;height:100%;box-shadow:none;margin-left:20px;padding:10px 15px;background:0 0;border:2px solid #ccd0d7;border-radius:4px}.search-block>.search-btn{cursor:pointer;float:left;width:90px;color:#fff;background:#00a1d6;border-radius:4px;font-size:16px;letter-spacing:2px;line-height:42px;text-align:center}.search-select-block{margin:20px auto;width:316px;height:100%;border-bottom:1px solid #ccd0d7}.search-select-block>.wrap{display:block}.search-select-block>.wrap>.sub{list-style-type:none;width:39px;height:100%;line-height:54px;font-size:16px;color:#222;margin:0 20px;text-align:center;cursor:pointer}.search-select-block>.wrap>.sub.active{border-bottom:2px solid #00a1d6}.search-select-block>.wrap>.sub:hover{color:#00a1d6}.search-fliter-block{margin:20px auto;width:980px;border-bottom:1px solid #ccd0d7}.search-fliter-block>.wrap{display:block;margin:20px auto}.search-fliter-block>.wrap .sub{list-style-type:none;padding-left:8px;padding-right:8px;border-radius:4px;margin-right:16px;font-size:14px;color:#222;text-decoration:none;cursor:pointer;line-height:24px}.search-fliter-block>.wrap>.sub:hover{color:#00a1d6}.search-fliter-block>.wrap>.sub.active{color:#fff;background-color:#00a1d6}.search-result-content{margin:20px auto;width:980px}.special-li{width:100%;height:110px;padding:20px 0;position:relative;border-bottom:1px solid #e5e9ef}.special-li>.img{height:70px;width:70px;border-radius:4px;overflow:hidden}.special-li img{width:100%;min-height:100%}.special-li>.info{padding-left:20px;width:800px;height:70px}.special-li>.info>.title{font-size:16px;line-height:16px;color:#222;-webkit-transition:color .2s ease;-moz-transition:color .2s ease;-ms-transition:color .2s ease;-o-transition:color .2s ease;transition:color .2s ease}.special-li>.info>.title:hover{color:#e04270}.special-li>.info>.desc{margin-top:12px;color:#99a2aa;font-size:12px;line-height:18px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.special-li>.info>.info{margin-top:5px}.special-li>.info .info-text{color:#879098;font-size:12px;margin-right:20px}.search-video-block{height:208px;width:168px;border:1px solid #e5e9ef;border-radius:4px;float:left;margin:20px 13px 0 13px}.search-video-block .img{height:100px;border-radius:4px;overflow:hidden;position:relative}.search-video-block .img>img{width:100%;min-height:100%}.search-video-block .img>.time{position:absolute;right:0;bottom:0;line-height:18px;padding:0 5px;color:#fff;background-color:#333;background-color:rgba(0,0,0,.5);border-top-left-radius:4px}.search-video-block .other-info{width:166px;text-align:center;margin:10px auto;overflow:hidden}.search-video-block .other-info>.title{display:block;font-size:13px;line-height:20px;height:35px;overflow:hidden;color:#222;-webkit-transition:color .2s ease;-moz-transition:color .2s ease;-ms-transition:color .2s ease;-o-transition:color .2s ease;transition:color .2s ease}.search-video-block .other-info>.title:hover{color:#00a1d6}.search-video-block .other-info .info{margin:10px 5px}.search-video-block .other-info .info-text{color:#879098;font-size:12px;margin-right:20px}.area-pager{overflow:hidden;margin:4px auto;padding:4px 0;height:auto}.area-pager>span{display:block;margin:0 4px 0 0;padding:0 4px;min-width:32px;width:auto;height:32px;border:none;border-radius:1px;background-color:#3a9bd9;box-shadow:0 1px 1px rgba(0,0,0,.1);color:#fff;text-align:center;text-shadow:0 1px 1px rgba(0,0,0,.1);font-weight:700;font-size:12px;line-height:32px;cursor:pointer}.area-pager>span:hover{background-color:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);color:#333}.area-pager>span.active{background-color:#eee;box-shadow:inset 0 1px 2px rgba(0,0,0,.2);color:#333}.area-pager>span.active:hover{cursor:not-allowed}.search-pager{margin:30px auto 20px 20px}.hidden{display:none}@-webkit-keyframes scale{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}45%{-webkit-transform:scale(.1);transform:scale(.1);opacity:.7}80%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes scale{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}45%{-webkit-transform:scale(.1);transform:scale(.1);opacity:.7}80%{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@-webkit-keyframes rotate_pacman_half_up{0%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}50%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}100%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}}@keyframes rotate_pacman_half_up{0%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}50%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}100%{-webkit-transform:rotate(270deg);transform:rotate(270deg)}}@-webkit-keyframes rotate_pacman_half_down{0%{-webkit-transform:rotate(90deg);transform:rotate(90deg)}50%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(90deg);transform:rotate(90deg)}}@keyframes rotate_pacman_half_down{0%{-webkit-transform:rotate(90deg);transform:rotate(90deg)}50%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(90deg);transform:rotate(90deg)}}@-webkit-keyframes pacman-balls{75%{opacity:.7}100%{-webkit-transform:translate(-100px,-6.25px);transform:translate(-100px,-6.25px)}}@keyframes pacman-balls{75%{opacity:.7}100%{-webkit-transform:translate(-100px,-6.25px);transform:translate(-100px,-6.25px)}}.pacman{position:relative}.pacman>div:nth-child(2){-webkit-animation:pacman-balls 1s 0s infinite linear;animation:pacman-balls 1s 0s infinite linear}.pacman>div:nth-child(3){-webkit-animation:pacman-balls 1s .33s infinite linear;animation:pacman-balls 1s .33s infinite linear}.pacman>div:nth-child(4){-webkit-animation:pacman-balls 1s .66s infinite linear;animation:pacman-balls 1s .66s infinite linear}.pacman>div:nth-child(5){-webkit-animation:pacman-balls 1s .99s infinite linear;animation:pacman-balls 1s .99s infinite linear}.pacman>div:first-of-type{width:0;height:0;border-right:25px solid transparent;border-top:25px solid #ffa5c7;border-left:25px solid #ffa5c7;border-bottom:25px solid #ffa5c7;border-radius:25px;-webkit-animation:rotate_pacman_half_up .5s 0s infinite;animation:rotate_pacman_half_up .5s 0s infinite}.pacman>div:nth-child(2){width:0;height:0;border-right:25px solid transparent;border-top:25px solid #ffa5c7;border-left:25px solid #ffa5c7;border-bottom:25px solid #ffa5c7;border-radius:25px;-webkit-animation:rotate_pacman_half_down .5s 0s infinite;animation:rotate_pacman_half_down .5s 0s infinite;margin-top:-50px}.pacman>div:nth-child(3),.pacman>div:nth-child(4),.pacman>div:nth-child(5),.pacman>div:nth-child(6){background-color:#ffa5c7;width:15px;height:15px;border-radius:100%;margin:2px;position:absolute;-webkit-transform:translate(0,-6.25px);-ms-transform:translate(0,-6.25px);transform:translate(0,-6.25px);top:25px;left:100px}.loading-pacman-block{width:150px;height:70px;padding-top:10px;margin:100px auto}.loading-content{width:100%;height:300px}.video-js .vjs-danmu{overflow:hidden;position:absolute;top:0;left:0;bottom:0;right:0;pointer-events:none;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.video-js .vjs-danmu .cmt{white-space:nowrap;padding:3px 0 0 0;margin:0;line-height:100%;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;text-decoration:none;letter-spacing:0;position:absolute;word-break:keep-all;text-wrap:none;border:0!important;-webkit-text-size-adjust:none;text-size-adjust:none}.video-js .vjs-danmu .cmt.noshadow{text-shadow:none!important}.video-js .vjs-danmu .cmt.rshadow{text-shadow:-1px 0 #fff,0 1px #fff,1px 0 #fff,0 -1px #fff!important}.vjs-danmu-control .vjs-menu-icon:before{content:'\f110';font-family:VideoJS;font-weight:400;font-style:normal;font-size:1.8em;line-height:1.67em}.vjs-danmu-control .vjs-danmu-switch{font-size:1em;line-height:3em;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center;box-sizing:inherit}.cmt{will-change:transform}@keyframes cmt-move-left{from{transform:translateX(100%)}}@keyframes cmt-move-right{to{transform:translateX(-100%)}} -------------------------------------------------------------------------------- /docs/css/slider.css: -------------------------------------------------------------------------------- 1 | .unslider{overflow:auto;margin:0;padding:0}.unslider-wrap{position:relative}.unslider-wrap.unslider-carousel>li{float:left}.unslider-vertical>ul{height:100%}.unslider-vertical li{float:none;width:100%}.unslider-fade{position:relative}.unslider-fade .unslider-wrap li{position:absolute;left:0;top:0;right:0;z-index:8}.unslider-fade .unslider-wrap li.unslider-active{z-index:10}.unslider li,.unslider ol,.unslider ul{list-style:none;margin:0;padding:0;border:none}.unslider-arrow{position:absolute;left:20px;z-index:2;cursor:pointer}.unslider-arrow.next{left:auto;right:20px} -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/docs/favicon.ico -------------------------------------------------------------------------------- /docs/img/bg0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/docs/img/bg0.png -------------------------------------------------------------------------------- /docs/img/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/docs/img/bg1.jpg -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BH-5 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 |
    19 |
    20 | 119 |
    120 |
    121 |
    122 | 123 | 124 |
    125 |
    126 |
    127 | 128 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 178 | 179 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /docs/js/slider.min.js: -------------------------------------------------------------------------------- 1 | !function(t){return t?(t.Unslider=function(n,e){var i=this;return i._="unslider",i.defaults={autoplay:!1,delay:3e3,speed:750,easing:"swing",keys:{prev:37,next:39},nav:!0,arrows:{prev:'',next:''},animation:"horizontal",selectors:{container:"ul:first",slides:"li"},animateHeight:!1,activeClass:i._+"-active",swipe:!0,swipeThreshold:.2},i.$context=n,i.options={},i.$parent=null,i.$container=null,i.$slides=null,i.$nav=null,i.$arrows=[],i.total=0,i.current=0,i.prefix=i._+"-",i.eventSuffix="."+i.prefix+~~(2e3*Math.random()),i.interval=null,i.init=function(n){return i.options=t.extend({},i.defaults,n),i.$container=i.$context.find(i.options.selectors.container).addClass(i.prefix+"wrap"),i.$slides=i.$container.children(i.options.selectors.slides),i.setup(),t.each(["nav","arrows","keys","infinite"],function(n,e){i.options[e]&&i["init"+t._ucfirst(e)]()}),jQuery.event.special.swipe&&i.options.swipe&&i.initSwipe(),i.options.autoplay&&i.start(),i.calculateSlides(),i.$context.trigger(i._+".ready"),i.animate(i.options.index||i.current,"init")},i.setup=function(){i.$context.addClass(i.prefix+i.options.animation).wrap('
    '),i.$parent=i.$context.parent("."+i._);var t=i.$context.css("position");"static"===t&&i.$context.css("position","relative"),i.$context.css("overflow","hidden")},i.calculateSlides=function(){if(i.total=i.$slides.length,"fade"!==i.options.animation){var t="width";"vertical"===i.options.animation&&(t="height"),i.$container.css(t,100*i.total+"%").addClass(i.prefix+"carousel"),i.$slides.css(t,100/i.total+"%")}},i.start=function(){return i.interval=setTimeout(function(){i.next()},i.options.delay),i},i.stop=function(){return clearTimeout(i.interval),i},i.initNav=function(){var n=t('');i.$slides.each(function(e){var o=this.getAttribute("data-nav")||e+1;t.isFunction(i.options.nav)&&(o=i.options.nav.call(i.$slides.eq(e),e,o)),n.children("ol").append('
  • '+o+"
  • ")}),i.$nav=n.insertAfter(i.$context),i.$nav.find("li").on("click"+i.eventSuffix,function(){var n=t(this).addClass(i.options.activeClass);n.siblings().removeClass(i.options.activeClass),i.animate(n.attr("data-slide"))})},i.initArrows=function(){i.options.arrows===!0&&(i.options.arrows=i.defaults.arrows),t.each(i.options.arrows,function(n,e){i.$arrows.push(t(e).insertAfter(i.$context).on("click"+i.eventSuffix,i[n]))})},i.initKeys=function(){i.options.keys===!0&&(i.options.keys=i.defaults.keys),t(document).on("keyup"+i.eventSuffix,function(n){t.each(i.options.keys,function(e,o){n.which===o&&t.isFunction(i[e])&&i[e].call(i)})})},i.initSwipe=function(){var t=i.$slides.width();"fade"!==i.options.animation&&i.$container.on({movestart:function(t){return t.distX>t.distY&&t.distX<-t.distY||t.distX-t.distY?!!t.preventDefault():void i.$container.css("position","relative")},move:function(n){i.$container.css("left",-(100*i.current)+100*n.distX/t+"%")},moveend:function(n){Math.abs(n.distX)/t>i.options.swipeThreshold?i[n.distX<0?"next":"prev"]():i.$container.animate({left:-(100*i.current)+"%"},i.options.speed/2)}})},i.initInfinite=function(){var n=["first","last"];t.each(n,function(t,e){i.$slides.push.apply(i.$slides,i.$slides.filter(':not(".'+i._+'-clone")')[e]().clone().addClass(i._+"-clone")["insert"+(0===t?"After":"Before")](i.$slides[n[~~!t]]()))})},i.destroyArrows=function(){t.each(i.$arrows,function(t,n){n.remove()})},i.destroySwipe=function(){i.$container.off("movestart move moveend")},i.destroyKeys=function(){t(document).off("keyup"+i.eventSuffix)},i.setIndex=function(t){return 0>t&&(t=i.total-1),i.current=Math.min(Math.max(0,t),i.total-1),i.options.nav&&i.$nav.find('[data-slide="'+i.current+'"]')._active(i.options.activeClass),i.$slides.eq(i.current)._active(i.options.activeClass),i},i.animate=function(n,e){if("first"===n&&(n=0),"last"===n&&(n=i.total),isNaN(n))return i;i.options.autoplay&&i.stop().start(),i.setIndex(n),i.$context.trigger(i._+".change",[n,i.$slides.eq(n)]);var o="animate"+t._ucfirst(i.options.animation);return t.isFunction(i[o])&&i[o](i.current,e),i},i.next=function(){var t=i.current+1;return t>=i.total&&(t=0),i.animate(t,"next")},i.prev=function(){return i.animate(i.current-1,"prev")},i.animateHorizontal=function(t){var n="left";return"rtl"===i.$context.attr("dir")&&(n="right"),i.options.infinite&&i.$container.css("margin-"+n,"-100%"),i.slide(n,t)},i.animateVertical=function(t){return i.options.animateHeight=!0,i.options.infinite&&i.$container.css("margin-top",-i.$slides.outerHeight()),i.slide("top",t)},i.slide=function(t,n){if(i.options.animateHeight&&i._move(i.$context,{height:i.$slides.eq(n).outerHeight()},!1),i.options.infinite){var e;n===i.total-1&&(e=i.total-3,n=-1),n===i.total-2&&(e=0,n=i.total-2),"number"==typeof e&&(i.setIndex(e),i.$context.on(i._+".moved",function(){i.current===e&&i.$container.css(t,-(100*e)+"%").off(i._+".moved")}))}var o={};return o[t]=-(100*n)+"%",i._move(i.$container,o)},i.animateFade=function(t){var n=i.$slides.eq(t).addClass(i.options.activeClass);i._move(n.siblings().removeClass(i.options.activeClass),{opacity:0}),i._move(n,{opacity:1},!1)},i._move=function(t,n,e,o){return e!==!1&&(e=function(){i.$context.trigger(i._+".moved")}),t._move(n,o||i.options.speed,i.options.easing,e)},i.init(e)},t.fn._active=function(t){return this.addClass(t).siblings().removeClass(t)},t._ucfirst=function(t){return(t+"").toLowerCase().replace(/^./,function(t){return t.toUpperCase()})},t.fn._move=function(){return this.stop(!0,!0),t.fn[t.fn.velocity?"velocity":"animate"].apply(this,arguments)},void(t.fn.unslider=function(n){return this.each(function(){var e=t(this);if("string"==typeof n&&e.data("unslider")){n=n.split(":");var i=e.data("unslider")[n[0]];if(t.isFunction(i))return i.apply(e,n[1]?n[1].split(","):null)}return e.data("unslider",new t.Unslider(e,n))})})):console.warn("Unslider needs jQuery")}(window.jQuery); -------------------------------------------------------------------------------- /docs/js/stickup.min.js: -------------------------------------------------------------------------------- 1 | jQuery(function(t){"use strict";var e=function(){},o={},n=0;e.prototype={dataProperty:"data-menu",itemClass:"",itemHover:"",marginTop:0,beforeStick:null,afterStick:null,beforeUnstick:null,afterUnstick:null,region:"top",_selector:"",_jqDom:null,_menuItems:[],_height:0,_parentMarginTop:0,_top:0,_marginBottom:0,onScroll:function(e,o){var n=null,r=null,i=this;if(i._menuItems&&i._menuItems.length>0)for(var a=null,s=0,m=null,l=0;ls-50&&s+50>o&&(i._jqDom.find("."+i.itemClass).removeClass(i.itemHover),i._jqDom.find("."+i.itemClass+":eq("+l+")").addClass(i.itemHover)),"up"==e&&(n=.4*m.height(),r=s-n,o>r?(i._jqDom.find("."+i.itemClass).removeClass(i.itemHover),i._jqDom.find("."+i.itemClass+":eq("+l+")").addClass(i.itemHover)):50>o&&(i._jqDom.find("."+i.itemClass).removeClass(i.itemHover),i._jqDom.find("."+i.itemClass+":eq(0)").addClass(i.itemHover)));i._top0?a.marginTop=parseInt(r.topMargin.replace("px","")):isNaN(parseInt(r.topMargin))?(console.log("incorrect argument, ignored."),a.marginTop=0):a.marginTop=parseInt(r.topMargin):a.marginTop=0,a.itemClass=r.itemClass,a.itemHover=r.itemHover):console.log("warm:needs arguments"),a.dataProperty=r.dataProperty||a.dataProperty,a.region=r.region||a.region,a._height=parseInt(a._jqDom.height()),a._marginBottom=parseInt(a._jqDom.css("margin-bottom")),a._parentMarginTop=parseInt(a._jqDom.next().closest("div").css("margin-top")),a._top=parseInt(a._jqDom.offset().top),o[a._selector]=a},t.fn.stickUp=function(t){o[this.selector]||e._init_(this,t)},t(document).on("scroll",function(){var e=parseInt(t(document).scrollTop()),r=e>n?"down":"up";for(var i in o)o[i].onScroll(r,e);n=e})}); -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/favicon.ico -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | import gulp from 'gulp'; 2 | import browserify from 'browserify'; 3 | import source from 'vinyl-source-stream'; 4 | import buffer from 'vinyl-buffer'; 5 | import babelify from 'babelify'; 6 | import uglify from 'gulp-uglify'; 7 | import rimraf from 'rimraf'; 8 | import sourcemaps from 'gulp-sourcemaps'; 9 | import cleancss from 'gulp-clean-css'; 10 | import imagemin from 'gulp-imagemin'; 11 | import pngquant from 'imagemin-pngquant'; 12 | import runSequence from 'run-sequence'; 13 | import ghPages from 'gulp-gh-pages'; 14 | import concatCss from 'gulp-concat-css'; 15 | 16 | const paths = { 17 | bundle: 'app.js', 18 | entry: 'src/js/Index.js', 19 | srcCss: 'src/styles/*.css', 20 | srcImg: 'src/images/**', 21 | srcLint: ['src/**/*.js', 'test/**/*.js'], 22 | distCss: 'dist/css', 23 | distJs: 'dist/js', 24 | distImg: 'dist/img' 25 | }; 26 | 27 | 28 | gulp.task('clean', cb => { 29 | rimraf('dist', cb); 30 | }); 31 | 32 | 33 | gulp.task('browserify', () => { 34 | browserify(paths.entry, {debug: false}) 35 | .transform(babelify) 36 | .bundle() 37 | .pipe(source(paths.bundle)) 38 | .pipe(buffer()) 39 | .pipe(uglify()) 40 | .pipe(gulp.dest(paths.distJs)); 41 | }); 42 | 43 | gulp.task('browserify_debug', () => { 44 | browserify(paths.entry, {debug: true}) 45 | .transform(babelify) 46 | .bundle() 47 | .pipe(source(paths.bundle)) 48 | .pipe(buffer()) 49 | .pipe(sourcemaps.init({loadMaps: true})) 50 | .pipe(uglify()) 51 | .pipe(sourcemaps.write('.')) 52 | .pipe(gulp.dest(paths.distJs)); 53 | }); 54 | 55 | gulp.task('styles', () => { 56 | gulp.src(paths.srcCss) 57 | .pipe(concatCss("main.css")) 58 | .pipe(cleancss({advanced: false})) 59 | .pipe(gulp.dest(paths.distCss)) 60 | }); 61 | 62 | gulp.task('images', () => { 63 | gulp.src(paths.srcImg) 64 | .pipe(imagemin({ 65 | progressive: true, 66 | svgoPlugins: [{removeViewBox: false}], 67 | use: [pngquant()] 68 | })) 69 | .pipe(gulp.dest(paths.distImg)); 70 | }); 71 | 72 | gulp.task('watchTask', () => { 73 | gulp.watch(paths.srcCss, ['styles']); 74 | }); 75 | 76 | 77 | gulp.task('build', cb => { 78 | process.env.NODE_ENV = 'production'; 79 | runSequence('clean', ['browserify', 'styles', 'images', 'copyAssets'], cb); 80 | }); 81 | 82 | 83 | gulp.task('source', cb=> { 84 | process.env.NODE_ENV = 'debug'; 85 | runSequence('clean', ['browserify_debug', 'styles', 'images', 'copyAssets'], cb); 86 | }); 87 | 88 | 89 | gulp.task('copyAssets', ()=> { 90 | gulp.src([ 91 | 'assets/js/*' 92 | ]).pipe(buffer()) 93 | .pipe(uglify()) 94 | .pipe(gulp.dest('dist/js')); 95 | 96 | gulp.src([ 97 | 'assets/css/*' 98 | ]).pipe(cleancss({advanced: false})) 99 | .pipe(gulp.dest('dist/css')); 100 | 101 | gulp.src([ 102 | 'favicon.ico' 103 | ]).pipe(gulp.dest('dist')); 104 | 105 | gulp.src([ 106 | 'src/index.html' 107 | ]).pipe(gulp.dest('dist')); 108 | }); 109 | 110 | 111 | gulp.task('deploy', () => { 112 | gulp.src('dist/**/**') 113 | .pipe(ghPages()); 114 | }); 115 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bilibili-html5", 3 | "version": "2.1.0", 4 | "description": "", 5 | "main": "app.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/WhiteBlue/bilibili-html5" 9 | }, 10 | "keywords": [ 11 | "gulp", 12 | "react", 13 | "browserify", 14 | "ES6", 15 | "ES2015", 16 | "babel" 17 | ], 18 | "scripts": { 19 | "build":"gulp build" 20 | }, 21 | "author": "WhiteBlue", 22 | "license": "MIT", 23 | "homepage": "https://bilibilih5.club", 24 | "dependencies": { 25 | "history": "^1.17.0", 26 | "react": "0.14.8", 27 | "react-dom": "0.14.0", 28 | "react-router": "1.0.3", 29 | "reqwest": "^2.0.5", 30 | "video.js": "^5.11.9" 31 | }, 32 | "devDependencies": { 33 | "autoprefixer": "^6.0.0", 34 | "babel": "^6.3.13", 35 | "babel-core": "6.3.17", 36 | "babel-preset-es2015": "^6.3.13", 37 | "babel-preset-react": "^6.3.13", 38 | "babel-register": "^6.6.5", 39 | "babelify": "^7.2.0", 40 | "browserify": "^13.0.0", 41 | "gulp": "^3.9.0", 42 | "gulp-clean-css": "^3.0.4", 43 | "gulp-concat-css": "^2.3.0", 44 | "gulp-gh-pages": "^0.5.4", 45 | "gulp-imagemin": "^2.4.0", 46 | "gulp-rename": "^1.2.2", 47 | "gulp-sourcemaps": "^1.5.2", 48 | "gulp-uglify": "^1.4.0", 49 | "imagemin-pngquant": "^4.2.0", 50 | "lodash": "^4.6.0", 51 | "rimraf": "^2.4.3", 52 | "run-sequence": "^1.1.2", 53 | "tap-nyan": "0.0.2", 54 | "vinyl-buffer": "^1.0.0", 55 | "vinyl-source-stream": "^1.1.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # bilibili-html5 2 | 3 | An unofficial website of [bilibili.com](http://bilibili.com). 4 | 5 | 6 | ![bh5](docs/bh5.png) 7 | 8 | 9 | ## Features 10 | 11 | 12 | * html5 video player support "Danmu" 13 | * video part/quality select 14 | 15 | ## Build 16 | 17 | 18 | ``` 19 | npm install 20 | 21 | npm run build 22 | 23 | ``` 24 | 25 | ## Others 26 | 27 | * Video player 28 | * video.js - [https://github.com/videojs/video.js](https://github.com/videojs/video.js) 29 | * Danmaku support 30 | * edited from CommentCoreLibrary - [https://github.com/jabbany/CommentCoreLibrary](https://github.com/jabbany/CommentCoreLibrary) 31 | 32 | ## License 33 | 34 | MIT License 35 | 36 | Copyright (c) 2016 Castaway Consulting LLC 37 | 38 | Permission is hereby granted, free of charge, to any person obtaining a copy 39 | of this software and associated documentation files (the "Software"), to deal 40 | in the Software without restriction, including without limitation the rights 41 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 42 | copies of the Software, and to permit persons to whom the Software is 43 | furnished to do so, subject to the following conditions: 44 | 45 | The above copyright notice and this permission notice shall be included in all 46 | copies or substantial portions of the Software. 47 | 48 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 49 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 50 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 51 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 52 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 53 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 54 | SOFTWARE. 55 | -------------------------------------------------------------------------------- /src/images/bg0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/src/images/bg0.png -------------------------------------------------------------------------------- /src/images/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WhiteBlue/bilibili-html5/701a5d991400c23739a5a5f811111c2f4bf60bb8/src/images/bg1.jpg -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BH-5 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 |
    19 |
    20 | 119 |
    120 |
    121 |
    122 | 123 | 124 |
    125 |
    126 |
    127 | 128 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 178 | 179 | 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/js/App.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var IndexPage = require("./page/IndexPage"); 4 | 5 | var IndexContent = React.createClass({ 6 | componentDidMount(){ 7 | document.title = "BH5 | 首页"; 8 | }, 9 | render: function () { 10 | return
    11 | 12 |
    ; 13 | } 14 | } 15 | ); 16 | 17 | 18 | module.exports = React.createClass({ 19 | render(){ 20 | return
    {this.props.children || }
    ; 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /src/js/Config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // base_url: "http://localhost:8080/", 3 | base_url: "http://123.206.255.148", 4 | routes: { 5 | INDEX_RANK: "top/", 6 | TOP_RANK: "toprank", 7 | BANNER: "banner", 8 | VIDEO_INFO: "view/", 9 | VIDEO_URL: "video/", 10 | SORT_VIDEOS: "sort/", 11 | BANGUMI_INFO: "bangumiinfo/", 12 | BANGUMI_INDEX: "bangumiindex", 13 | BANGUMI_LIST: "bangumi", 14 | SEARCH: "search", 15 | SEARCH_BY_TYPE: "searchbytype", 16 | GET_COMMENT: "http://comment.bilibili.cn/" 17 | }, 18 | index_sorts: [24, 33, 31, 20, 17, 36, 119], 19 | 20 | sort_tags: { 21 | 1: "动画", 22 | 24: "MAD·AMV", 23 | 25: "MMD·3D", 24 | 47: "短片·手书·配音", 25 | 27: "综合", 26 | 27 | 28 | 13: "番剧", 29 | 33: "连载动画", 30 | 32: "完结动画", 31 | 152: "官方延伸", 32 | 153: "国产动画", 33 | 34 | 35 | 3: "音乐", 36 | 31: "翻唱", 37 | 30: "VOCALOID·UTAU", 38 | 29: "三次元音乐", 39 | 28: "同人音乐", 40 | 54: "OP/ED/OST", 41 | 130: "音乐选集", 42 | 43 | 44 | 129: "舞蹈", 45 | 20: "宅舞", 46 | 154: "三次元舞蹈", 47 | 156: "舞蹈教程", 48 | 49 | 50 | 17: "单机联机", 51 | 65: "网游·电竞", 52 | 136: "音游", 53 | 19: "Mugen", 54 | 121: "GMV", 55 | 56 | 57 | 36: "科技", 58 | 37: "纪录片", 59 | 124: "趣味科普人文", 60 | 122: "野生技术协会", 61 | 39: "演讲·公开课", 62 | 95: "数码", 63 | 98: "机械", 64 | 65 | 66 | 163: "搞笑", 67 | 138: "生活", 68 | 75: "动物圈", 69 | 76: "美食圈", 70 | 71: "综艺", 71 | 137: "娱乐圈", 72 | 131: "Korea相关", 73 | 74 | 75 | 119: "鬼畜", 76 | 22: "鬼畜调教", 77 | 126: "人力VOCALOID", 78 | 26: "音MAD", 79 | 127: "教程演示" 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /src/js/Index.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | import {Router, Route} from 'react-router'; 3 | var render = require('react-dom').render; 4 | 5 | var App = require('./App'); 6 | var ViewPage = require('./page/ViewPage'); 7 | var SortPage = require('./page/SortPage'); 8 | var SearchPage = require('./page/SearchPage'); 9 | var BangumiInfoPage = require('./page/BangumiInfoPage'); 10 | var BangumiIndexPage = require('./page/BangumiIndexPage'); 11 | 12 | 13 | 14 | render( 15 | ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ), document.getElementById('root') 24 | ); 25 | -------------------------------------------------------------------------------- /src/js/components/AllRank.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Config = require('../Config'); 3 | var reqwest = require('reqwest'); 4 | 5 | const VideoItem = React.createClass({ 6 | getDefaultProps(){ 7 | return { 8 | data: { 9 | title: "", 10 | cover: "", 11 | param: "", 12 | name: "", 13 | play: "", 14 | reply: "", 15 | danmaku: "", 16 | favourite: "" 17 | } 18 | }; 19 | }, 20 | getInitialState(){ 21 | return { 22 | hrefStr: "#/view/" 23 | }; 24 | }, 25 | render() { 26 | var linkUrl = this.state.hrefStr + this.props.data.param; 27 | return
    28 | 29 | 30 | 31 |
    弹幕: {this.props.data.danmaku}
    32 | {this.props.data.title} 33 |
    34 |
    35 | 播放: {this.props.data.play} 36 |
    37 |
    38 | 回复: {this.props.data.reply} 39 |
    40 |
    41 |
    ; 42 | } 43 | }); 44 | 45 | 46 | const VideoBlock = React.createClass({ 47 | _loadData(tid){ 48 | var _this = this; 49 | 50 | var labelName = Config.sort_tags[tid]; 51 | 52 | reqwest({ 53 | url: Config.base_url + Config.routes.INDEX_RANK + tid 54 | , type: 'json' 55 | , method: 'get' 56 | , crossOrigin: true 57 | , error: function (err) { 58 | console.log('error'); 59 | } 60 | , success: function (resp) { 61 | _this.setState({ 62 | videoList: resp.videos, 63 | labelName: labelName 64 | }); 65 | } 66 | }); 67 | }, 68 | getInitialState(){ 69 | return { 70 | videoList: [], 71 | labelName: '' 72 | }; 73 | }, 74 | componentDidMount(){ 75 | this._loadData(this.props.tid); 76 | }, 77 | render(){ 78 | var renderVideos = []; 79 | for (var i in this.state.videoList) { 80 | renderVideos.push(); 81 | if (i == 9) { 82 | break; 83 | } 84 | } 85 | return
    86 |
    87 | 95 |
    96 | {renderVideos} 97 |
    98 |
    99 |
    100 |
    ; 101 | } 102 | }); 103 | 104 | module.exports = React.createClass({ 105 | render(){ 106 | var renderList = []; 107 | for (var i in Config.index_sorts) { 108 | var id = Config.index_sorts[i]; 109 | renderList.push() 110 | } 111 | return
    {renderList}
    ; 112 | } 113 | }); 114 | -------------------------------------------------------------------------------- /src/js/components/BangumiIndex.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Config = require('../Config'); 3 | var reqwest = require('reqwest'); 4 | 5 | var Loading = require('./Loading'); 6 | 7 | const BangumiBanner = React.createClass({ 8 | _loadBanner(){ 9 | $('.block-banner').unslider({ 10 | animation: 'horizontal', 11 | autoplay: true, 12 | arrows: false, 13 | keys: false 14 | }); 15 | }, 16 | getDefaultProps(){ 17 | return { 18 | list: [] 19 | } 20 | }, 21 | componentDidMount(){ 22 | this._loadBanner(); 23 | }, 24 | render(){ 25 | var banners = []; 26 | 27 | for (var i = 0; i < this.props.list.length; i++) { 28 | banners.push(
  • 29 | 30 | {this.props.list[i].title} 31 |
  • 32 | ); 33 | } 34 | 35 | return
    36 |
    37 |
      38 | {banners} 39 |
    40 |
    41 |
    ; 42 | } 43 | }); 44 | 45 | const VideoItem = React.createClass({ 46 | getDefaultProps(){ 47 | return { 48 | data: { 49 | aid: "", 50 | author: "", 51 | coins: 0, 52 | comment: 0, 53 | create: "", 54 | description: "", 55 | duration: "", 56 | favorites: 0, 57 | mid: 0, 58 | pic: "", 59 | play: 0, 60 | review: 0, 61 | title: "", 62 | typeid: 0, 63 | typename: "", 64 | video_review: 0 65 | } 66 | }; 67 | }, 68 | getInitialState(){ 69 | return { 70 | hrefStr: "#/view/" 71 | }; 72 | }, 73 | render() { 74 | var linkUrl = this.state.hrefStr + this.props.data.aid; 75 | return
    76 | 77 | 78 | 79 |
    {this.props.data.duration}
    80 | 81 | 标题: {this.props.data.title} 82 | UP主: {this.props.data.author} 83 | 84 |
    85 |
    86 | 播放: {this.props.data.play} 87 |
    88 |
    89 | 弹幕: {this.props.data.video_review} 90 |
    91 |
    92 |
    ; 93 | } 94 | }); 95 | 96 | 97 | module.exports = React.createClass({ 98 | _loadData(){ 99 | var _this = this; 100 | reqwest({ 101 | url: Config.base_url + Config.routes.BANGUMI_INDEX 102 | , type: 'json' 103 | , method: 'get' 104 | , data: {} 105 | , crossOrigin: true 106 | , error: function (err) { 107 | console.log('error:' + err); 108 | } 109 | , success: function (data) { 110 | _this.setState({ 111 | indexData: data, 112 | loading: false 113 | }); 114 | } 115 | }); 116 | }, 117 | getInitialState(){ 118 | return { 119 | loading: true, 120 | indexData: null 121 | } 122 | }, 123 | componentDidMount(){ 124 | this._loadData(); 125 | }, 126 | render(){ 127 | var recommend_videos = []; 128 | 129 | if (this.state.indexData != null) { 130 | 131 | for (var i = 0; i < this.state.indexData.recommends.length; i++) { 132 | recommend_videos.push(); 133 | } 134 | } 135 | 136 | return
    137 |
    138 | { 139 | (this.state.loading) ? : 140 | } 141 |
    142 |
    143 |
    144 |
    145 |

    视频推荐

    146 |
    147 | { 148 | (this.state.loading) ? : 149 |
    150 | {recommend_videos} 151 |
    152 |
    153 | } 154 |
    155 |
    156 |
    157 | } 158 | }); 159 | -------------------------------------------------------------------------------- /src/js/components/BangumiInfo.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Config = require('../Config'); 3 | var reqwest = require('reqwest'); 4 | 5 | var Loading = require('./Loading'); 6 | 7 | 8 | var videoHrefStr = "#/view/"; 9 | const bangumiHrefStr = "#/bangumi/"; 10 | 11 | 12 | const BangumiItem = React.createClass({ 13 | getDefaultProps(){ 14 | return { 15 | data: { 16 | title: "", 17 | cover: "", 18 | is_finish: "", 19 | season_id: "", 20 | total_count: "" 21 | } 22 | } 23 | }, 24 | render(){ 25 | return
    26 |
    27 | 28 |
    29 |
    30 | {this.props.data.title} 31 |
    32 |
    状态: {this.props.data.is_finish == "1" ? "已完结" : "连载中"}
    33 |
    集数: {this.props.data.total_count}
    34 |
    35 |
    36 |
    ; 37 | } 38 | }); 39 | 40 | 41 | module.exports = React.createClass({ 42 | _seasonId: "", 43 | _loadData(){ 44 | var _this = this; 45 | reqwest({ 46 | url: Config.base_url + Config.routes.BANGUMI_INFO + _this._seasonId 47 | , type: 'json' 48 | , method: 'get' 49 | , data: {} 50 | , crossOrigin: true 51 | , error: function (err) { 52 | console.log('error:' + err); 53 | } 54 | , success: function (data) { 55 | _this.props.cb(data.title); 56 | _this.setState({ 57 | bangumiInfo: data, 58 | loading: false 59 | }); 60 | } 61 | }); 62 | }, 63 | getDefaultProps(){ 64 | return { 65 | seasonId: 0, 66 | cb: function (content) { 67 | } 68 | } 69 | }, 70 | getInitialState(){ 71 | return { 72 | loading: true, 73 | bangumiInfo: null 74 | } 75 | }, 76 | componentDidMount(){ 77 | this._seasonId = this.props.seasonId; 78 | this._loadData(); 79 | }, 80 | componentWillReceiveProps(nextProps){ 81 | this._seasonId = nextProps.seasonId; 82 | this.setState({ 83 | loading: true 84 | }); 85 | this._loadData(); 86 | }, 87 | render(){ 88 | var seasons = []; 89 | var actors = ""; 90 | var videos = []; 91 | 92 | if (this.state.bangumiInfo != null) { 93 | for (var i = 0; i < this.state.bangumiInfo.seasons.length; i++) { 94 | seasons.push(); 95 | } 96 | 97 | for (i = 0; i < this.state.bangumiInfo.actor.length; i++) { 98 | actors += this.state.bangumiInfo.actor[i].actor + "、 "; 99 | } 100 | 101 | for (i = 0; i < this.state.bangumiInfo.episodes.length; i++) { 102 | videos.push(
  • 103 | 104 | {this.state.bangumiInfo.episodes[i].index + "." + this.state.bangumiInfo.episodes[i].index_title} 106 |
  • ); 107 | } 108 | } 109 | 110 | return
    111 | {(this.state.loading) ? : 112 |
    113 |
    114 |

    {this.state.bangumiInfo.title}

    115 |
    116 | 117 |
    118 |
    119 |

    声优:{actors}

    120 |
    121 |

    {this.state.bangumiInfo.evaluate}

    122 |
    123 |

    播放数:{this.state.bangumiInfo.play_count} | 124 | 弹幕数:{this.state.bangumiInfo.danmaku_count}

    125 |
    126 |
    127 |
    128 | {seasons} 129 |
    130 |
    131 |
    132 |

    剧集:

    133 |
      134 | {videos} 135 |
    136 |
    137 |
    138 |
    139 | } 140 |
    ; 141 | } 142 | }); 143 | -------------------------------------------------------------------------------- /src/js/components/BangumiList.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Config = require('../Config'); 3 | var reqwest = require('reqwest'); 4 | 5 | var Loading = require('./Loading'); 6 | 7 | 8 | const bangumiHrefStr = "#/bangumi/"; 9 | 10 | const BangumiItem = React.createClass({ 11 | getDefaultProps(){ 12 | return { 13 | data: { 14 | title: "", 15 | cover: "", 16 | is_finish: "", 17 | season_id: "", 18 | attention: "" 19 | } 20 | } 21 | }, 22 | render(){ 23 | return 24 |
    25 |
    26 | 27 |
    28 |
    29 |

    {this.props.data.title}

    30 |

    关注: {this.props.data.attention}

    31 |
    32 |
    33 |
    34 |
    ; 35 | } 36 | }); 37 | 38 | 39 | module.exports = React.createClass({ 40 | _showMore(){ 41 | $('.list-bangumi-block').animate({ 42 | maxHeight: '2800px' 43 | }); 44 | }, 45 | _loadData(){ 46 | var _this = this; 47 | reqwest({ 48 | url: Config.base_url + Config.routes.BANGUMI_LIST 49 | , type: 'json' 50 | , method: 'get' 51 | , data: {} 52 | , crossOrigin: true 53 | , error: function (err) { 54 | console.log('error:' + err); 55 | } 56 | , success: function (data) { 57 | _this.setState({ 58 | data: data 59 | }); 60 | } 61 | }); 62 | }, 63 | getInitialState(){ 64 | return { 65 | data: null 66 | } 67 | }, 68 | componentDidMount(){ 69 | this._loadData(); 70 | }, 71 | render(){ 72 | var bangumiList = []; 73 | 74 | if (this.state.data != null) { 75 | for (var i = 0; i < this.state.data.list.length; i++) { 76 | bangumiList.push(); 77 | } 78 | } 79 | 80 | return
    81 |
    82 |
    83 |

    番剧列表

    84 |
    85 |
    86 | 87 | {bangumiList} 88 | 89 |
    90 |
    91 | 92 |
    93 | 显示/隐藏 94 |
    95 | 96 |
    97 |
    ; 98 | } 99 | }); 100 | -------------------------------------------------------------------------------- /src/js/components/IndexBanner.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var reqwest = require('reqwest'); 3 | 4 | var Config = require('../Config'); 5 | 6 | 7 | const Banner = React.createClass({ 8 | getDefaultProps(){ 9 | return { 10 | bannerList: [] 11 | }; 12 | }, 13 | render(){ 14 | var renderBanner = []; 15 | for (var i = 0; i < this.props.bannerList.length; i++) { 16 | var data = this.props.bannerList[i]; 17 | renderBanner.push(
  • 18 | 19 |
  • ); 20 | } 21 | return
    22 |
    23 |
      24 | {renderBanner} 25 |
    26 |
    27 |
    28 |
    ; 29 | } 30 | }); 31 | 32 | const BannerBlock = React.createClass({ 33 | _loadBanner(){ 34 | $('.block-banner').unslider({ 35 | animation: 'horizontal', 36 | autoplay: true, 37 | arrows: false, 38 | keys: false 39 | }); 40 | }, 41 | getDefaultProps(){ 42 | return { 43 | bannerList: [] 44 | }; 45 | }, 46 | componentDidMount(){ 47 | this._loadBanner(); 48 | }, 49 | render(){ 50 | return ; 51 | } 52 | }); 53 | 54 | module.exports = React.createClass({ 55 | _loadData(){ 56 | var _this = this; 57 | reqwest({ 58 | url: Config.base_url + Config.routes.BANNER 59 | , type: 'json' 60 | , method: 'get' 61 | , crossOrigin: true 62 | , error: function (err) { 63 | console.log('error'); 64 | } 65 | , success: function (data) { 66 | _this.setState({ 67 | banners: data 68 | }); 69 | } 70 | }); 71 | }, 72 | componentDidMount(){ 73 | this._loadData(); 74 | }, 75 | getInitialState(){ 76 | return { 77 | banners: [] 78 | } 79 | }, 80 | render(){ 81 | return (this.state.banners.length !== 0) ? :
    ; 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /src/js/components/IndexHot.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Config = require('../Config'); 3 | var reqwest = require('reqwest'); 4 | 5 | const VideoItem = React.createClass({ 6 | getDefaultProps(){ 7 | return { 8 | data: { 9 | title: "", 10 | cover: "", 11 | param: "", 12 | name: "", 13 | play: "", 14 | reply: "", 15 | danmaku: "", 16 | favourite: "" 17 | } 18 | }; 19 | }, 20 | getInitialState(){ 21 | return { 22 | hrefStr: "#/view/" 23 | }; 24 | }, 25 | render() { 26 | var linkUrl = this.state.hrefStr + this.props.data.param; 27 | return ; 41 | } 42 | }); 43 | 44 | module.exports = React.createClass({ 45 | _loadData(){ 46 | var _this = this; 47 | reqwest({ 48 | url: Config.base_url + Config.routes.TOP_RANK 49 | , type: 'json' 50 | , method: 'get' 51 | , crossOrigin: true 52 | , error: function (err) { 53 | console.log('error'); 54 | } 55 | , success: function (data) { 56 | _this.setState({ 57 | top: data 58 | }); 59 | } 60 | }); 61 | }, 62 | componentDidMount(){ 63 | this._loadData(); 64 | }, 65 | getInitialState(){ 66 | return { 67 | top: [] 68 | } 69 | }, 70 | render(){ 71 | var renderVideos = []; 72 | var count = 0; 73 | for (var i in this.state.top) { 74 | if (count < 6) { 75 | if (i != 'num') { 76 | renderVideos.push(); 77 | } 78 | } else { 79 | break; 80 | } 81 | count++; 82 | } 83 | return
    84 | {renderVideos} 85 |
    86 |
    ; 87 | } 88 | }); 89 | -------------------------------------------------------------------------------- /src/js/components/Loading.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | 4 | module.exports = React.createClass({ 5 | render(){ 6 | return
    7 |
    8 |
    9 |
    10 |
    11 |
    12 |
    13 |
    14 |
    15 |
    16 |
    ; 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/js/components/Pager.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | _pageChange(page){ 5 | if (page < 1) { 6 | page = 1; 7 | } 8 | if (page > this.props.allPage) { 9 | page = this.props.allPage; 10 | } 11 | this.props.pageCallBack(page); 12 | }, 13 | getDefaultProps(){ 14 | return { 15 | nowPage: 1, 16 | allPage: 0, 17 | showPageCount: 8, 18 | pageCallBack: function (page) { 19 | } 20 | }; 21 | }, 22 | render(){ 23 | var start = this.props.nowPage - this.props.showPageCount / 2; 24 | if (start < 1) { 25 | start = 1; 26 | } 27 | var renderList = []; 28 | 29 | for (var i = 0; i < this.props.showPageCount; i++) { 30 | if (i >= this.props.allPage && this.props.allPage >= 0) { 31 | break; 32 | } 33 | var index = start + i; 34 | if (index === this.props.nowPage) { 35 | renderList.push({index}) 36 | } else { 37 | renderList.push({index}) 39 | } 40 | } 41 | return (this.props.allPage <= 1 ?
    :
    42 | 首页 43 | 上一页 44 | {renderList} 45 | 下一页 46 |
    ); 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /src/js/components/Player.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var reqwest = require('reqwest'); 3 | 4 | var Config = require('../Config'); 5 | var Loading = require('./Loading'); 6 | 7 | var videojs = require("video.js"); 8 | 9 | require("../danmaku/VideojsPlugin"); 10 | require("../utils/VideojsHotKeys"); 11 | 12 | var _oldPlayer = null; //Video.js的dispose()不能放在componentWillUnmount事件之前(和React.js自身清理相冲突) 13 | 14 | const VideoBlock = React.createClass({ 15 | _player: null, 16 | _loadVideoJs(commentUrl, videoUrl){ 17 | if (_oldPlayer != null) { 18 | _oldPlayer.dispose(); 19 | _oldPlayer = null; 20 | } 21 | if (this._player == null) { 22 | this._player = videojs('danmu_player', { 23 | controls: true 24 | }, function () { 25 | this.initDanmaku(); 26 | this.hotkeys({ 27 | volumeStep: 0.1, 28 | seekStep: 5, 29 | //音量键(M) 30 | enableMute: true, 31 | //滚轮调节音量 32 | enableVolumeScroll: false, 33 | //全屏(F) 34 | enableFullscreen: true, 35 | //数字选择分P 36 | enableNumbers: false, 37 | alwaysCaptureHotkeys: false 38 | }); 39 | this.danmu.load(commentUrl); 40 | this.src(videoUrl); 41 | }); 42 | } else { 43 | this._player.src(videoUrl); 44 | this._player.danmu.load(commentUrl); 45 | } 46 | 47 | }, 48 | getDefaultProps(){ 49 | return { 50 | url: "", 51 | pic: "", 52 | commentUrl: "" 53 | }; 54 | }, 55 | componentDidMount(){ 56 | this._loadVideoJs(this.props.commentUrl, this.props.urlList.url); 57 | }, 58 | componentWillReceiveProps(nextProps){ 59 | if (this._player != null) { 60 | //避免初次加载调用 61 | this._loadVideoJs(nextProps.commentUrl, nextProps.urlList.url); 62 | } 63 | }, 64 | componentWillUnmount(){ 65 | _oldPlayer = this._player; 66 | this.player = null; 67 | }, 68 | render(){ 69 | return ; 78 | } 79 | }); 80 | 81 | 82 | module.exports = React.createClass({ 83 | _cid: null, 84 | _quality: 4, 85 | _selectParts(partStr){ 86 | var cid = null; 87 | if (this.props.parts.length !== 0) { 88 | cid = this.props.parts[partStr].cid; 89 | } 90 | this._cid = cid; 91 | this._loadVideoData(); 92 | this.setState({ 93 | nowPlay: partStr 94 | }); 95 | }, 96 | _loadVideoData(){ 97 | this.setState({ 98 | loading: true 99 | }); 100 | if (this._cid !== null) { 101 | var _this = this; 102 | reqwest({ 103 | url: Config.base_url + Config.routes.VIDEO_URL + this._cid + "?quality=" + this._quality 104 | , type: 'json' 105 | , method: 'get' 106 | , crossOrigin: true 107 | , error: function (err) { 108 | console.log('error'); 109 | } 110 | , success: function (data) { 111 | _this.setState({ 112 | loading: false, 113 | data: data 114 | }); 115 | } 116 | }); 117 | } 118 | }, 119 | componentDidMount(){ 120 | //初始化cid 121 | this._selectParts("0"); 122 | }, 123 | getDefaultProps(){ 124 | return { 125 | parts: [], 126 | pic: "" 127 | } 128 | }, 129 | getInitialState(){ 130 | return { 131 | loading: true, 132 | data: null, 133 | nowPlay: "0" 134 | } 135 | }, 136 | render(){ 137 | var partList = []; 138 | var display = "none"; 139 | if (this.props.parts.hasOwnProperty("1")) { 140 | for (var i in this.props.parts) { 141 | if (this.props.parts.hasOwnProperty(i)) { 142 | var active = ""; 143 | if (i === this.state.nowPlay) { 144 | active = "active"; 145 | } 146 | partList.push({this.props.parts[i].part}); 148 | } 149 | } 150 | display = "block"; 151 | } 152 | 153 | var commentUrl = Config.routes.GET_COMMENT + this._cid + ".xml"; 154 | 155 | return
    156 |
    157 |
    158 | {partList} 159 |
    160 |
    161 | {this.state.loading ? : 162 | } 164 |
    165 |
    ; 166 | } 167 | }); 168 | -------------------------------------------------------------------------------- /src/js/components/SearchContent.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var reqwest = require('reqwest'); 3 | 4 | var Config = require('../Config'); 5 | var Pager = require('./Pager'); 6 | var Loading = require('./Loading'); 7 | 8 | 9 | const videoHrefStr = "#/view/"; 10 | const bangumiHrefStr = "#/bangumi/"; 11 | 12 | const VideoItem = React.createClass({ 13 | getDefaultProps(){ 14 | return { 15 | data: { 16 | title: "", 17 | cover: "", 18 | param: "", 19 | desc: "", 20 | author: "", 21 | duration: "", 22 | play: 0, 23 | danmaku: 0 24 | } 25 | }; 26 | }, 27 | render() { 28 | var date = new Date(); 29 | date.setTime(this.props.data.pubdate * 1000); 30 | 31 | var linkUrl = videoHrefStr + this.props.data.param; 32 | return
    33 | 34 |
    35 | 36 | {this.props.data.duration} 37 |
    38 |
    39 |
    40 | {this.props.data.title} 41 |
    42 |
    播放: {this.props.data.play}
    43 |
    弹幕:{this.props.data.danmaku}
    44 |
    Up: {this.props.data.author}
    45 |
    46 |
    47 |
    ; 48 | } 49 | }); 50 | 51 | 52 | const UserItem = React.createClass({ 53 | getDefaultProps(){ 54 | return { 55 | data: { 56 | title: "", 57 | cover: "", 58 | param: "", 59 | fans: "", 60 | sign: "" 61 | } 62 | }; 63 | }, 64 | render() { 65 | var date = new Date(); 66 | date.setTime(this.props.data.pubdate * 1000); 67 | 68 | var linkUrl = '#'; 69 | return
    70 | 71 |
    72 | 73 |
    74 |
    75 |
    76 | {this.props.data.title} 77 |
    78 |
    粉丝: {this.props.data.fans}
    79 |
    {this.props.data.sign}
    80 |
    81 |
    82 |
    ; 83 | } 84 | }); 85 | 86 | 87 | const BangumiItem = React.createClass({ 88 | getDefaultProps(){ 89 | return { 90 | data: { 91 | title: "", 92 | cover: "", 93 | param: "", 94 | cat_desc: "", 95 | total_count: 0 96 | } 97 | } 98 | }, 99 | render(){ 100 | return
    101 |
    102 | 103 |
    104 |
    105 | {this.props.data.title} 106 |

    {this.props.data.cat_desc}

    107 |
    108 |
    集数: {this.props.data.total_count}
    109 |
    110 |
    111 |
    ; 112 | } 113 | }); 114 | 115 | 116 | const Topic = React.createClass({ 117 | getDefaultProps(){ 118 | return { 119 | data: { 120 | arcurl: "", 121 | author: "", 122 | click: 0, 123 | cover: "", 124 | description: "", 125 | favourite: 0, 126 | keyword: "", 127 | mid: 0, 128 | pubdate: 0, 129 | review: 0, 130 | title: "", 131 | tp_id: 0, 132 | tp_type: 0, 133 | update: 0 134 | } 135 | } 136 | }, 137 | render(){ 138 | return
    ; 139 | } 140 | }); 141 | 142 | const SearchBlock = React.createClass({ 143 | _handleClick(){ 144 | var value = this.refs.search_content.value; 145 | this.props.searchCallBack(value); 146 | }, 147 | componentDidUpdate(prevProps, prevState){ 148 | //input value 149 | this.refs.search_content.setAttribute('value', this.props.keyword); 150 | }, 151 | getDefaultProps(){ 152 | return { 153 | keyword: "", 154 | searchCallBack: function (keyword) { 155 | console.log(keyword); 156 | } 157 | } 158 | }, 159 | render(){ 160 | return
    161 |
    162 | 163 |
    164 |
    搜索 165 |
    166 |
    167 |
    ; 168 | } 169 | }); 170 | 171 | 172 | module.exports = React.createClass({ 173 | _searchType: "video", 174 | _page: 1, 175 | _order: "totalrank", 176 | _keyword: "", 177 | _loadData(){ 178 | this.setState({ 179 | loading: true 180 | }); 181 | var _this = this; 182 | if (this._searchType == "video") { 183 | reqwest({ 184 | url: Config.base_url + Config.routes.SEARCH 185 | , type: 'json' 186 | , method: 'get' 187 | , data: { 188 | content: _this._keyword, 189 | page_size: 20, 190 | page: _this._page, 191 | order: _this._order 192 | } 193 | , crossOrigin: true 194 | , error: function (err) { 195 | console.log('error:' + err); 196 | } 197 | , success: function (data) { 198 | _this.props.cb(_this._keyword); 199 | _this.setState({ 200 | list: data.items.archive, 201 | all_page: 500, 202 | loading: false 203 | }); 204 | } 205 | }); 206 | } else { 207 | reqwest({ 208 | url: Config.base_url + Config.routes.SEARCH_BY_TYPE 209 | , type: 'json' 210 | , method: 'get' 211 | , data: { 212 | content: _this._keyword, 213 | page_size: 20, 214 | page: _this._page, 215 | type: _this._searchType 216 | } 217 | , crossOrigin: true 218 | , error: function (err) { 219 | console.log('error:' + err); 220 | } 221 | , success: function (data) { 222 | _this.props.cb(_this._keyword); 223 | _this.setState({ 224 | list: data.items, 225 | all_page: data.pages, 226 | loading: false 227 | }); 228 | } 229 | }); 230 | } 231 | }, 232 | _getSearch(content){ 233 | this._keyword = content; 234 | this._loadData(); 235 | }, 236 | _changeOrder(order){ 237 | this._order = order; 238 | this._loadData(); 239 | }, 240 | _changePage(page){ 241 | this._page = page; 242 | this._loadData(); 243 | $('body,html').animate({scrollTop: 0}, 700); 244 | }, 245 | _changeType(type){ 246 | this._searchType = type; 247 | this._page = 1; 248 | this._loadData(); 249 | }, 250 | getInitialState(){ 251 | return { 252 | list: [], 253 | all_page: 0, 254 | loading: true 255 | } 256 | }, 257 | getDefaultProps(){ 258 | return { 259 | keyword: "", 260 | allPage: 20, 261 | cb: function (content) { 262 | } 263 | } 264 | }, 265 | componentDidMount(){ 266 | this._keyword = this.props.keyword; 267 | this._loadData(); 268 | }, 269 | componentWillReceiveProps(nextProps){ 270 | this._keyword = nextProps.keyword; 271 | this._loadData(); 272 | this.setState({ 273 | loading: true 274 | }); 275 | }, 276 | render(){ 277 | var renderArr = []; 278 | 279 | for (var i = 0; i < this.state.list.length; i++) { 280 | switch (this._searchType) { 281 | case "video": { 282 | renderArr.push(); 283 | break; 284 | } 285 | case "bangumi": { 286 | renderArr.push(); 287 | break; 288 | } 289 | case "user": { 290 | renderArr.push(); 291 | } 292 | } 293 | } 294 | 295 | 296 | return
    297 |
    298 | 299 |
    300 |
      301 |
    • 视频 303 |
    • 304 |
    • 番剧 306 |
    • 307 |
    • UP主 309 |
    • 310 |
    311 |
    312 |
    313 | 314 | {(this._searchType == "video") ?
    315 |
      316 |
    • 综合排序 318 |
    • 319 |
    • 最多点击 321 |
    • 322 |
    • 最新发布 324 |
    • 325 |
    • 弹幕 327 |
    • 328 |
    329 |
    330 |
    :
    } 331 |
    332 | 333 | {(this.state.loading) ? : 334 |
    335 |
    336 | {renderArr} 337 |
    338 |
    339 | 340 |
    341 |
    342 |
    } 343 |
    ; 344 | } 345 | }); 346 | -------------------------------------------------------------------------------- /src/js/components/SortHot.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var Config = require('../Config'); 3 | var reqwest = require('reqwest'); 4 | 5 | const VideoItem = React.createClass({ 6 | getDefaultProps(){ 7 | return { 8 | data: { 9 | title: "", 10 | cover: "", 11 | param: "", 12 | name: "", 13 | play: "", 14 | reply: "", 15 | danmaku: "", 16 | favourite: "" 17 | } 18 | }; 19 | }, 20 | getInitialState(){ 21 | return { 22 | hrefStr: "#/view/" 23 | }; 24 | }, 25 | render() { 26 | var linkUrl = this.state.hrefStr + this.props.data.param; 27 | return
    28 |
    29 | 30 |
    31 |
    32 | {this.props.data.title} 33 | {this.props.data.name} 34 |
    35 |
    播放: {this.props.data.play}
    36 |
    弹幕: {this.props.data.danmaku}
    37 |
    38 |
    39 |
    ; 40 | } 41 | }); 42 | 43 | 44 | module.exports = React.createClass({ 45 | _order: 'hot', 46 | _loadData(){ 47 | var _this = this; 48 | reqwest({ 49 | url: Config.base_url + Config.routes.TOP_RANK 50 | , type: 'json' 51 | , method: 'get' 52 | , crossOrigin: true 53 | , error: function (err) { 54 | console.log('error'); 55 | } 56 | , success: function (data) { 57 | _this.setState({ 58 | videoList: data 59 | }); 60 | } 61 | }); 62 | }, 63 | getDefaultProps(){ 64 | return { 65 | tid: 0 66 | } 67 | }, 68 | getInitialState(){ 69 | return { 70 | videoList: [] 71 | } 72 | }, 73 | componentDidMount(){ 74 | this._loadData(); 75 | }, 76 | render(){ 77 | var renderList = []; 78 | 79 | for (var i in this.state.videoList) { 80 | if (i != "num") { 81 | renderList.push(); 82 | } 83 | } 84 | 85 | return
    86 |
    87 |

    热门排行

    88 |
    89 |
    90 | {renderList} 91 |
    92 |
    ; 93 | } 94 | }); 95 | -------------------------------------------------------------------------------- /src/js/components/SortRank.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var reqwest = require('reqwest'); 3 | 4 | var Config = require('../Config'); 5 | var Pager = require('./Pager'); 6 | var Loading = require('./Loading'); 7 | 8 | const VideoItem = React.createClass({ 9 | getDefaultProps(){ 10 | return { 11 | data: { 12 | title: "", 13 | cover: "", 14 | param: "", 15 | name: "", 16 | play: "", 17 | reply: "", 18 | danmaku: "", 19 | favourite: "" 20 | } 21 | }; 22 | }, 23 | getInitialState(){ 24 | return { 25 | hrefStr: "#/view/" 26 | }; 27 | }, 28 | render() { 29 | var linkUrl = this.state.hrefStr + this.props.data.param; 30 | return
    31 |
    32 |
    33 | 34 |
    35 | 36 |
    37 |
    38 |
    39 |
    播放: {this.props.data.play}
    40 |
    弹幕: {this.props.data.danmaku}
    41 |
    42 |
    43 | 48 |
    49 |
    ; 50 | } 51 | }); 52 | 53 | const VideoBlock = React.createClass({ 54 | getDefaultProps(){ 55 | return { 56 | videoList: [] 57 | } 58 | }, 59 | render(){ 60 | var renderList = []; 61 | 62 | for (var i in this.props.videoList) { 63 | if (i != "num") { 64 | renderList.push(); 65 | } 66 | } 67 | 68 | return
    69 | {renderList} 70 |
    71 |
    ; 72 | } 73 | }); 74 | 75 | 76 | module.exports = React.createClass({ 77 | _page: 1, 78 | _order: 'view', 79 | //排序方式 80 | _changeSort(sort){ 81 | this._order = sort; 82 | this._page = 1; 83 | this._loadData(this.props.tid); 84 | }, 85 | //分页 86 | _changePage(page){ 87 | //翻页后大传送术 88 | $('body,html').animate({scrollTop: 0}, 700); 89 | this._page = page; 90 | this._loadData(this.props.tid); 91 | }, 92 | _loadData(tid){ 93 | this.setState({ 94 | loading: true 95 | }); 96 | let _this = this; 97 | let labelName = Config.sort_tags[tid]; 98 | 99 | reqwest({ 100 | url: Config.base_url + Config.routes.SORT_VIDEOS + tid + "?page=" + _this._page + "&order=" + this._order 101 | , type: 'json' 102 | , method: 'get' 103 | , crossOrigin: true 104 | , error: function (err) { 105 | console.log('error:' + err); 106 | } 107 | , success: function (data) { 108 | _this.props.cb(labelName); 109 | _this.setState({ 110 | videoList: data, 111 | allPage: 999, 112 | title: labelName, 113 | loading: false 114 | }); 115 | } 116 | }); 117 | }, 118 | getDefaultProps(){ 119 | return { 120 | tid: 0, 121 | cb: function (content) { 122 | } 123 | } 124 | }, 125 | getInitialState(){ 126 | return { 127 | videoList: [], 128 | allPage: 0, 129 | title: "", 130 | loading: true 131 | } 132 | }, 133 | componentWillReceiveProps(nextProps){ 134 | this._loadData(nextProps.tid); 135 | }, 136 | componentDidMount(){ 137 | this._loadData(this.props.tid); 138 | }, 139 | render(){ 140 | var _this = this; 141 | 142 | return
    143 |
    144 |

    {this.state.title}

    145 |
    146 | 147 |
    148 | 点击数 150 | 151 | 发布时间 153 | 154 | 弹幕数 156 |
    157 | 158 | {(this.state.loading) ? : 159 |
    160 | 161 |
    162 | 163 | 164 |
    165 | } 166 |
    ; 167 | } 168 | }); 169 | -------------------------------------------------------------------------------- /src/js/components/Video.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | var reqwest = require('reqwest'); 3 | var _ = require('lodash'); 4 | 5 | var Config = require('../Config'); 6 | var Player = require('./Player'); 7 | var Loading = require('./Loading'); 8 | 9 | const Tags = React.createClass({ 10 | getDefaltProps(){ 11 | return { 12 | str: "" 13 | }; 14 | }, 15 | render(){ 16 | var tags = _.split(this.props.str, ","); 17 | var renderTags = []; 18 | for (var i in tags) { 19 | renderTags.push(
  • {tags[i]}
  • ) 20 | } 21 | return
      22 | {renderTags} 23 |
      24 |
    ; 25 | } 26 | }); 27 | 28 | const VideoInfo = React.createClass({ 29 | getDefaltProps(){ 30 | return { 31 | data: { 32 | author: "", 33 | coins: "", 34 | created_at: "", 35 | description: "", 36 | face: "", 37 | favorites: "", 38 | list: {}, 39 | mid: "", 40 | pages: 1, 41 | pic: "", 42 | play: "", 43 | review: "", 44 | tag: "", 45 | tid: 0, 46 | title: "", 47 | typename: "", 48 | video_review: "" 49 | } 50 | }; 51 | }, 52 | render(){ 53 | return
    54 |
    55 |
    56 |
    57 |
    58 |
    59 |

    {this.props.data.title}

    60 |
    61 |
    62 |
    分类: {this.props.data.typename}
    63 |
    时间: {this.props.data.created_at}
    64 |
    65 |
    66 |
    67 |
    播放: {this.props.data.play}
    68 |
    弹幕: {this.props.data.video_review}
    69 |
    硬币: {this.props.data.coins}
    70 |
    71 |
    72 |
    73 |
    74 |
    75 | 76 | 77 |
    78 |
    79 | {this.props.data.author} 80 |
    收藏: {this.props.data.favorites}
    81 |
    82 |
    83 |
    84 |
    85 |
    86 |
    87 | 88 | {this.props.data.list ? : } 89 | 90 |
    91 |
    92 |
    93 | 94 | 95 | 96 |
    97 |
    {this.props.data.description}
    98 | 99 |
    100 | 评论 101 |

    (施工中)

    102 |
    103 |
    104 |
    105 |
    106 |
    ; 107 | } 108 | }); 109 | 110 | module.exports = React.createClass({ 111 | _loadData(){ 112 | if (this.props.aid !== null) { 113 | var _this = this; 114 | reqwest({ 115 | url: Config.base_url + Config.routes.VIDEO_INFO + this.props.aid 116 | , type: 'json' 117 | , method: 'get' 118 | , crossOrigin: true 119 | , error: function (err) { 120 | console.log('error'); 121 | } 122 | , success: function (data) { 123 | _this.props.cb(data.title); 124 | _this.setState({ 125 | videoInfo: data 126 | }); 127 | } 128 | }); 129 | } 130 | }, 131 | componentDidMount(){ 132 | this._loadData(); 133 | }, 134 | getDefaltProps(){ 135 | return { 136 | aid: null, 137 | cb: function (content) { 138 | } 139 | } 140 | }, 141 | getInitialState(){ 142 | return { 143 | videoInfo: {} 144 | } 145 | }, 146 | render(){ 147 | return ; 148 | } 149 | }); 150 | -------------------------------------------------------------------------------- /src/js/danmaku/BilibiliParser.js: -------------------------------------------------------------------------------- 1 | function BilibiliParser(xmlDoc) { 2 | var elements = xmlDoc.getElementsByTagName('d'); 3 | 4 | var danmakuList = []; 5 | for (var i = 0; i < elements.length; i++) { 6 | if (elements[i].getAttribute('p') != null) { 7 | var opt = elements[i].getAttribute('p').split(','); 8 | if (!elements[i].childNodes[0]) 9 | continue; 10 | var text = elements[i].childNodes[0].nodeValue; 11 | var obj = {}; 12 | obj.stime = Math.round(parseFloat(opt[0]) * 1000); 13 | obj.mode = parseInt(opt[1]); 14 | obj.size = parseInt(opt[2]); 15 | obj.color = parseInt(opt[3]); 16 | obj.date = parseInt(opt[4]); 17 | obj.pool = parseInt(opt[5]); 18 | obj.position = "absolute"; 19 | if (opt[7] != null) 20 | obj.dbid = parseInt(opt[7]); 21 | obj.hash = opt[6]; 22 | obj.border = false; 23 | if (obj.mode < 7) { 24 | obj.text = text.replace(/(\/n|\\n|\n|\r\n)/g, "\n"); 25 | } 26 | if (obj.text != null) 27 | obj.text = obj.text.replace(/\u25a0/g, "\u2588"); 28 | danmakuList.push(obj); 29 | } 30 | } 31 | return danmakuList; 32 | } 33 | 34 | module.exports = BilibiliParser; 35 | 36 | -------------------------------------------------------------------------------- /src/js/danmaku/CommentManager.js: -------------------------------------------------------------------------------- 1 | var StaticComment = require('./StaticComment'); 2 | var ScrollComment = require('./ScrollComment'); 3 | 4 | 5 | class CommentManager { 6 | constructor(stage) { 7 | this.commentLine = []; 8 | this.nowLine = []; 9 | this.position = 0; 10 | this.options = { 11 | indexOffset: 0, 12 | className: "cmt", 13 | margin: 1, 14 | global: { 15 | opacity: 1, 16 | scale: 1 17 | }, 18 | scroll: { 19 | opacity: 1, 20 | scale: 1 21 | }, 22 | limit: 0 23 | }; 24 | this.stage = stage; 25 | this.width = stage.offsetWidth; 26 | this.height = stage.offsetHeight; 27 | } 28 | 29 | startTimer() { 30 | for (var i = 0; i < this.nowLine.length; i++) { 31 | if (this.nowLine[i].control) { 32 | this.nowLine[i].start(); 33 | } 34 | } 35 | } 36 | 37 | stopTimer() { 38 | for (var i = 0; i < this.nowLine.length; i++) { 39 | if (this.nowLine[i].control) { 40 | this.nowLine[i].stop(); 41 | } 42 | } 43 | } 44 | 45 | nowLinePush(pushCmt) { 46 | if (this.nowLine.length === 0) { 47 | this.nowLine.push(pushCmt); 48 | return; 49 | } 50 | if (this.nowLine[this.nowLine.length - 1].offsetY() <= pushCmt.offsetY()) { 51 | this.nowLine.push(pushCmt); 52 | return; 53 | } 54 | if (this.nowLine[0].offsetY() >= pushCmt.offsetY()) { 55 | this.nowLine.unshift(pushCmt); 56 | return; 57 | } 58 | var low = 0; 59 | var high = this.nowLine.length - 1; 60 | 61 | var i = 0; 62 | var insertIndex = 0; 63 | while (low < high) { 64 | i = Math.floor((high + low + 1) / 2); 65 | if (this.nowLine[i - 1].offsetY() <= pushCmt.offsetY() && this.nowLine[i].offsetY() >= pushCmt.offsetY()) { 66 | insertIndex = i; 67 | break; 68 | } 69 | if (this.nowLine[i - 1].offsetY() > pushCmt.offsetY()) { 70 | high = i - 1; 71 | } else { 72 | low = i; 73 | } 74 | } 75 | this.nowLine.splice(insertIndex, 0, pushCmt); 76 | } 77 | 78 | nowLineRemove(removeCmt) { 79 | var index = this.nowLine.indexOf(removeCmt); 80 | if (index >= 0) { 81 | this.nowLine.splice(index, 1); 82 | } 83 | } 84 | 85 | setBounds() { 86 | this.width = this.stage.offsetWidth; 87 | this.height = this.stage.offsetHeight; 88 | }; 89 | 90 | init() { 91 | this.setBounds(); 92 | this.position = 0; 93 | }; 94 | 95 | 96 | //插入弹幕 97 | send(data) { 98 | var cmt; 99 | if (data.mode === 5 || data.mode === 4) { 100 | cmt = new StaticComment(this, data); 101 | } else if (data.mode === 1 || data.mode === 2 || data.mode == 6) { 102 | cmt = new ScrollComment(this, data); 103 | } else { 104 | console.log('不支持的弹幕'); 105 | return; 106 | } 107 | cmt.init(); 108 | this.stage.appendChild(cmt.dom); 109 | cmt.layout(); 110 | this.nowLinePush(cmt); 111 | }; 112 | 113 | seek(locateTime) { 114 | this.position = 0; 115 | this.position = this.locate(locateTime); 116 | }; 117 | 118 | locate(time) { 119 | for (var i = this.position; i < this.commentLine.length; i++) { 120 | var cm = this.commentLine[i]; 121 | if (cm.stime >= time) { 122 | return i; 123 | } 124 | } 125 | return this.commentLine.length; 126 | }; 127 | 128 | time(nowTime) { 129 | if (this.nowLine.length === 0 && this.position === this.commentLine.length) { 130 | return; 131 | } 132 | 133 | nowTime -= 1; 134 | 135 | if (this.position < this.commentLine.length) { 136 | var end = this.locate(nowTime); 137 | 138 | for (; this.position < end; this.position++) { 139 | this.send(this.commentLine[this.position]); 140 | } 141 | this.position = end; 142 | } 143 | 144 | var length = this.nowLine.length; 145 | for (var i = 0; i < length; i++) { 146 | var cmt = this.nowLine[i]; 147 | if (!cmt.checkTime(nowTime)) { 148 | this.remove(cmt); 149 | length--; 150 | } 151 | } 152 | }; 153 | 154 | load(timeLine) { 155 | this.commentLine = timeLine; 156 | this.position = 0; 157 | this.commentLine.sort(function (a, b) { 158 | if (a.stime >= b.stime) { 159 | return 1; 160 | } else { 161 | return -1; 162 | } 163 | }); 164 | }; 165 | 166 | remove(rmObj) { 167 | this.nowLineRemove(rmObj); 168 | this.stage.removeChild(rmObj.dom); 169 | }; 170 | 171 | clear() { 172 | while (this.nowLine.length > 0) { 173 | this.remove(this.nowLine[0]); 174 | } 175 | }; 176 | } 177 | 178 | module.exports = CommentManager; 179 | -------------------------------------------------------------------------------- /src/js/danmaku/CommentObject.js: -------------------------------------------------------------------------------- 1 | class CommentObject { 2 | 3 | constructor(manager, init) { 4 | this.align = 0; 5 | this.index = 0; 6 | this.mode = 1; 7 | this.stime = 0; 8 | this.text = ""; 9 | this._size = 20; 10 | this._color = 0xffffff; 11 | this.control = false; 12 | this._border = false; 13 | 14 | this.lifeTime = 4000 * manager.options.global.scale; 15 | this.manager = manager; 16 | 17 | if (init.hasOwnProperty("align")) { 18 | this.align = init["align"]; 19 | } 20 | if (init.hasOwnProperty("stime")) { 21 | this.stime = init["stime"]; 22 | } 23 | if (init.hasOwnProperty("text")) { 24 | this.text = init["text"]; 25 | } 26 | if (init.hasOwnProperty("mode")) { 27 | this.mode = init["mode"]; 28 | } 29 | if (init.hasOwnProperty("color")) { 30 | this._color = init["color"]; 31 | } 32 | if (init.hasOwnProperty("size")) { 33 | this._size = init["size"]; 34 | } 35 | if (init.hasOwnProperty("x")) { 36 | this._x = init["x"]; 37 | } 38 | if (init.hasOwnProperty("y")) { 39 | this._y = init["y"]; 40 | } 41 | if (init.hasOwnProperty("border")) { 42 | this._border = init["border"]; 43 | } 44 | } 45 | 46 | offsetX(x) { 47 | if (x === null || x === undefined) { 48 | if (this._x === null || this._x === undefined) { 49 | if (this.align % 2 === 0) { 50 | this._x = this.dom.offsetLeft - this.manager.stage.offsetLeft; 51 | } else { 52 | this._x = this.manager.stage.offsetWidth - (this.dom.offsetLeft - this.manager.stage.offsetLeft + this.dom.offsetWidth); 53 | } 54 | } 55 | return this._x; 56 | } else { 57 | this._x = x; 58 | if (this.align % 2 === 0) { 59 | this.dom.style.right = this._x + "px"; 60 | } else { 61 | this.dom.style.left = this._x + "px"; 62 | } 63 | } 64 | }; 65 | 66 | 67 | offsetY(y) { 68 | if (y === null || y === undefined) { 69 | if (this._y === null || this._y === undefined) { 70 | if (this.align < 2) { 71 | this._y = this.dom.offsetTop; 72 | } else { 73 | this._y = this.manager.stage.offsetHeight - (this.dom.offsetTop + this.dom.offsetHeight); 74 | } 75 | } 76 | return this._y; 77 | } else { 78 | this._y = y; 79 | if (this.align < 2) { 80 | this.dom.style.top = this._y + "px"; 81 | } else { 82 | this.dom.style.top = (this.manager.stage.offsetHeight - y - this.dom.offsetHeight) + "px"; 83 | } 84 | } 85 | }; 86 | 87 | 88 | Color(c) { 89 | if (c === null || c === undefined) { 90 | return this._color; 91 | } else { 92 | this._color = c; 93 | var color = c.toString(16); 94 | color = color.length >= 6 ? color : new Array(6 - color.length + 1).join("0") + color; 95 | if (color.indexOf('#') !== 0) { 96 | color = '#'.concat(color); 97 | } 98 | this.dom.style.color = color; 99 | if (this._color === 0) { 100 | this.dom.className = this.manager.options.className + " rshadow"; 101 | } 102 | } 103 | }; 104 | 105 | 106 | Width(w) { 107 | if (w === null || w === undefined) { 108 | if (this._width === null || this._width === undefined) { 109 | this._width = this.dom.offsetWidth; 110 | } 111 | return this._width; 112 | } else { 113 | this._width = w; 114 | this.dom.style.width = this._width + "px"; 115 | } 116 | }; 117 | 118 | 119 | Height(h) { 120 | if (h === null || h === undefined) { 121 | if (this._height === null || this._height === undefined) { 122 | this._height = this.dom.offsetHeight; 123 | } 124 | return this._height; 125 | } else { 126 | this._height = h; 127 | this.dom.style.height = this._height + "px"; 128 | } 129 | }; 130 | 131 | 132 | Size(s) { 133 | if (s === null || s === undefined) { 134 | return this._size; 135 | } else { 136 | this._size = s; 137 | this.dom.style.fontSize = this._size + "px"; 138 | } 139 | }; 140 | 141 | 142 | Border(b) { 143 | if (b === null || b === undefined) { 144 | return this._border; 145 | } else { 146 | this._border = b; 147 | this.dom.style.border = b; 148 | } 149 | }; 150 | 151 | 152 | init() { 153 | var dom = document.createElement("div"); 154 | dom.className = this.manager.options.className; 155 | 156 | dom.appendChild(document.createTextNode(this.text)); 157 | 158 | dom.textContent = this.text; 159 | dom.innerText = this.text; 160 | 161 | this.dom = dom; 162 | 163 | if (this._border) { 164 | dom.style.border = "2px solid red"; 165 | } 166 | 167 | this.Color(this._color); 168 | this.Size(this._size); 169 | }; 170 | 171 | checkTime(nowTime) { 172 | return (this.stime + this.lifeTime) >= nowTime; 173 | }; 174 | 175 | 176 | layout() { 177 | }; 178 | 179 | 180 | stop() { 181 | }; 182 | 183 | start() { 184 | }; 185 | 186 | } 187 | 188 | 189 | module.exports = CommentObject; 190 | -------------------------------------------------------------------------------- /src/js/danmaku/ScrollComment.js: -------------------------------------------------------------------------------- 1 | var CommentObject = require('./CommentObject'); 2 | 3 | class ScrollComment extends CommentObject { 4 | constructor(manager, init) { 5 | super(manager, init); 6 | switch (this.mode) { 7 | case 1: 8 | this.align = 0; 9 | break; 10 | case 2: 11 | this.align = 2; 12 | break; 13 | case 6: 14 | this.align = 1; 15 | } 16 | this.follow = false; 17 | this.control = true; 18 | this.lifeTime *= manager.options.scroll.scale; 19 | this.moving = false; 20 | 21 | //容差 22 | this.timeOffset = 700; 23 | } 24 | 25 | layout() { 26 | var index = 0; 27 | var channel = this.Size() + 2 * this.manager.options.margin; 28 | var offset = 0; 29 | var insertY = -1; 30 | var width = this.Width(); 31 | 32 | while (insertY < 0) { 33 | if (index > 1000) { 34 | return; 35 | } 36 | insertY = this._findOffsetY(index, channel, offset); 37 | index++; 38 | offset += this.manager.options.indexOffset; 39 | } 40 | this.index = index - 1; 41 | this.offsetY(insertY); 42 | this.dom.style.right = "-" + width + "px"; 43 | this.dom.style.left = "-" + width + "px"; 44 | 45 | this.moveAnimation(); 46 | }; 47 | 48 | _findOffsetY(index, channel, offset) { 49 | var cmObj; 50 | var preY = offset; 51 | for (var i = 0; i < this.manager.nowLine.length; i++) { 52 | cmObj = this.manager.nowLine[i]; 53 | if (cmObj.mode === this.mode && cmObj.index === index) { 54 | if (cmObj.offsetY() - preY >= channel) { 55 | return preY; 56 | } 57 | if ((!cmObj.follow) && (cmObj.timeLeft <= (cmObj.lifeTime * 0.5))) { 58 | cmObj.follow = true; 59 | return cmObj.offsetY(); 60 | } 61 | preY = cmObj.offsetY() + cmObj.Height(); 62 | } 63 | } 64 | if (preY + channel <= this.manager.stage.offsetHeight) { 65 | return preY; 66 | } 67 | return -1; 68 | }; 69 | 70 | 71 | transform(trans) { 72 | this.dom.style.transform = trans; 73 | this.dom.style["webkitTransform"] = trans; 74 | this.dom.style["msTransform"] = trans; 75 | this.dom.style["oTransform"] = trans; 76 | }; 77 | 78 | moveTransform() { 79 | if (!this.moving) { 80 | var dx = -(this.manager.width + this.Width()); 81 | this.transform("translateX(" + dx + "px)"); 82 | this.dom.style.transition = "transform " + this.lifeTime + "ms linear"; 83 | this.moving = true; 84 | } 85 | }; 86 | 87 | moveAnimation() { 88 | var locate = this.align % 2 == 0 ? '-left ' : '-right '; 89 | var animation = "cmt-move" + locate + this.lifeTime / 1000 + "s linear"; 90 | this.dom.style.animation = animation; 91 | this.dom.style["-webkit-animation"] = animation; 92 | this.dom.style["-moz-animation"] = animation; 93 | this.dom.style["-o-animation"] = animation; 94 | }; 95 | 96 | start() { 97 | this.dom.style["animation-play-state"] = "running"; 98 | }; 99 | 100 | stop() { 101 | this.dom.style["animation-play-state"] = "paused"; 102 | }; 103 | 104 | 105 | checkTime(nowTime) { 106 | return this.stime + this.lifeTime + this.timeOffset >= nowTime; 107 | }; 108 | 109 | } 110 | 111 | module.exports = ScrollComment; 112 | 113 | -------------------------------------------------------------------------------- /src/js/danmaku/StaticComment.js: -------------------------------------------------------------------------------- 1 | var CommentObject = require('./CommentObject'); 2 | 3 | 4 | class StaticComment extends CommentObject { 5 | constructor(manager, init) { 6 | super(manager, init); 7 | this.align = (this.mode == 4) ? 3 : 0; 8 | } 9 | 10 | _findOffsetY(index, channel, offset) { 11 | //取得起始位置(区别对齐方式) 12 | var preY = offset; 13 | for (var i in this.manager.nowLine) { 14 | var cmObj = this.manager.nowLine[i]; 15 | //弹幕同类型同层 16 | if (cmObj.mode === this.mode && cmObj.index === index) { 17 | if (cmObj.offsetY() - preY >= channel) { 18 | return preY; 19 | } else { 20 | preY = cmObj.offsetY() + cmObj.Height(); 21 | } 22 | } 23 | } 24 | if (preY + channel <= this.manager.stage.offsetHeight) { 25 | return preY; 26 | } 27 | return -1; 28 | }; 29 | 30 | layout() { 31 | var index = 0; 32 | var channel = this.Height() + 2 * this.manager.options.margin; 33 | var offset = 0; 34 | var insertY = -1; 35 | 36 | while (insertY < 0) { 37 | insertY = this._findOffsetY(index, channel, offset); 38 | index++; 39 | offset += this.manager.options.indexOffset; 40 | } 41 | 42 | this.index = index - 1; 43 | this.offsetX(this.manager.stage.offsetLeft + (this.manager.stage.offsetWidth - this.Width()) / 2); 44 | this.offsetY(insertY); 45 | }; 46 | } 47 | 48 | 49 | module.exports = StaticComment; 50 | 51 | -------------------------------------------------------------------------------- /src/js/danmaku/VideojsPlugin.js: -------------------------------------------------------------------------------- 1 | var videojs = require("video.js"); 2 | var reqwest = require('reqwest'); 3 | 4 | var CommentManager = require("./CommentManager"); 5 | var BilibiliParser = require("./BilibiliParser"); 6 | 7 | videojs.plugin('initDanmaku', function () { 8 | function Danmu(ele) { 9 | var _this = this; 10 | this.danmuDiv = document.createElement('div'); 11 | this.danmuDiv.className = 'vjs-danmu'; 12 | ele.el().insertBefore(this.danmuDiv, ele.el().getElementsByClassName('vjs-poster')[0]); 13 | 14 | this.danmuShowControl = document.createElement('div'); 15 | this.danmuShowControl.className = 'vjs-danmu-control vjs-menu-button vjs-control'; 16 | this.danmuShowControlContent = document.createElement('span'); 17 | this.danmuShowControlContent.className = 'vjs-danmu-switch'; 18 | this.danmuShowControlContent.innerHTML = '弹幕'; 19 | this.danmuShowControl.appendChild(this.danmuShowControlContent); 20 | ele.el().getElementsByClassName('vjs-control-bar')[0].appendChild(this.danmuShowControl); 21 | 22 | //CCL init 23 | this.cmManager = new CommentManager(this.danmuDiv); 24 | //弹幕播放时间 25 | this.cmManager.options.scroll.scale = 3; 26 | this.cmManager.init(); 27 | this.cmManager.clear(); 28 | 29 | this.cmManager.display = true; 30 | 31 | //弹幕控制绑定 32 | var video = ele.el().children[0]; 33 | var lastPosition = 0; 34 | video.addEventListener("progress", function () { 35 | if (lastPosition == video.currentTime) { 36 | video.hasStalled = true; 37 | _this.cmManager.stopTimer(); 38 | } else 39 | lastPosition = video.currentTime; 40 | }); 41 | //时间轴更新 42 | video.addEventListener("timeupdate", function () { 43 | if (_this.cmManager.display === false) return; 44 | if (video.hasStalled) { 45 | _this.cmManager.startTimer(); 46 | video.hasStalled = false; 47 | } 48 | _this.cmManager.time(Math.floor(video.currentTime * 1000)); 49 | }); 50 | 51 | video.addEventListener("play", function () { 52 | _this.cmManager.startTimer(); 53 | }); 54 | 55 | video.addEventListener("pause", function () { 56 | _this.cmManager.stopTimer(); 57 | }); 58 | 59 | video.addEventListener("waiting", function () { 60 | _this.cmManager.stopTimer(); 61 | }); 62 | 63 | video.addEventListener("playing", function () { 64 | _this.cmManager.startTimer(); 65 | }); 66 | 67 | video.addEventListener("seeked", function () { 68 | _this.cmManager.seek(Math.floor(video.currentTime * 1000)); 69 | _this.cmManager.clear(); 70 | }); 71 | 72 | if (window) { 73 | window.addEventListener("resize", function () { 74 | _this.cmManager.setBounds(); 75 | }); 76 | } 77 | this.danmuShowControl.addEventListener("click", function () { 78 | if (_this.cmManager.display === true) { 79 | _this.cmManager.clear(); 80 | _this.cmManager.display = false; 81 | _this.danmuShowControlContent.innerHTML = "无"; 82 | } else { 83 | _this.cmManager.display = true; 84 | _this.danmuShowControlContent.innerHTML = "弹幕"; 85 | } 86 | }); 87 | 88 | 89 | this.load = function (url) { 90 | reqwest({ 91 | url: url 92 | , type: 'xml' 93 | , method: 'get' 94 | , crossOrigin: true 95 | , error: function (err) { 96 | console.log('error:', err); 97 | } 98 | , success: function (data) { 99 | if (data != null) { 100 | _this.cmManager.load(BilibiliParser(data)); 101 | } 102 | } 103 | }); 104 | }; 105 | 106 | return this; 107 | } 108 | 109 | this.danmu = new Danmu(this); 110 | }); 111 | 112 | -------------------------------------------------------------------------------- /src/js/page/BangumiIndexPage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var BangumiIndex = require('../components/BangumiIndex'); 4 | var BangumiList = require('../components/BangumiList'); 5 | 6 | 7 | module.exports = React.createClass({ 8 | componentDidMount(){ 9 | document.title = "BH5 | 番剧"; 10 | }, 11 | render(){ 12 | return
    13 | 14 | 15 |
    ; 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /src/js/page/BangumiInfoPage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var BangumiInfo = require('../components/BangumiInfo'); 4 | 5 | 6 | function titleCallback(content) { 7 | document.title = "BH5 | 番剧:" + content; 8 | } 9 | 10 | module.exports = React.createClass({ 11 | render(){ 12 | return
    13 |
    ; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /src/js/page/IndexPage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var IndexBanner = require('../components/IndexBanner'); 4 | var IndexHot = require('../components/IndexHot'); 5 | var AllRank = require('../components/AllRank'); 6 | 7 | module.exports = React.createClass({ 8 | render(){ 9 | return
    10 |
    11 |
    12 |
    13 | 14 |
    15 | 16 |
    17 |
    18 | 19 |
    20 |
    ; 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /src/js/page/SearchPage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var SearchContent = require('../components/SearchContent'); 4 | 5 | 6 | function titleCallback(content) { 7 | document.title = "BH5 | 搜索:" + content; 8 | } 9 | 10 | module.exports = React.createClass({ 11 | render(){ 12 | return
    13 |
    ; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /src/js/page/SortPage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var SortRank = require('../components/SortRank'); 4 | var SortHot = require('../components/SortHot'); 5 | 6 | function titleCallback(content) { 7 | document.title = "BH5 | 分类:" + content; 8 | } 9 | 10 | module.exports = React.createClass({ 11 | render(){ 12 | return
    13 |
    14 |
    15 | 16 | 17 |
    18 |
    19 |
    20 |
    ; 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /src/js/page/ViewPage.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | var Video = require('../components/Video'); 4 | 5 | 6 | function titleCallback(content) { 7 | document.title = "BH5 | " + content; 8 | } 9 | 10 | 11 | module.exports = React.createClass({ 12 | render(){ 13 | return
    ; 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /src/js/parts/Error.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | getDefaultProps(){ 5 | return { 6 | message: "加载失败...." 7 | }; 8 | }, 9 | render() { 10 | return
    11 |

    {this.props.message}

    12 |
    ; 13 | } 14 | }); -------------------------------------------------------------------------------- /src/js/parts/Footer.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | getInitialState(){ 5 | return { 6 | data: null 7 | }; 8 | }, 9 | componentDidMount(){ 10 | this.setState({ 11 | data: {} 12 | }); 13 | }, 14 | render() { 15 | return
    16 |
    17 |
    18 |
    19 |
    20 | 圣基团新闻组
    21 | 新闻组长 Saber 网站技术 mowangsk
    22 | 联系邮箱 mowangsk@missevan.cn 23 |
    24 |
    25 |
    26 |
    ; 27 | } 28 | }); -------------------------------------------------------------------------------- /src/js/parts/Loading.js: -------------------------------------------------------------------------------- 1 | var React = require('react'); 2 | 3 | module.exports = React.createClass({ 4 | render() { 5 | return
    6 |

    读条中....

    7 |
    ; 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /src/js/utils/VideojsHotKeys.js: -------------------------------------------------------------------------------- 1 | /* 2 | * use Video.js Hotkeys by Chris Dougherty 3 | * 4 | * https://github.com/ctd1500/videojs-hotkeys 5 | */ 6 | 7 | var videojs = require("video.js"); 8 | 9 | videojs.plugin('hotkeys', function (options) { 10 | var player = this; 11 | var pEl = player.el(); 12 | var doc = document; 13 | var def_options = { 14 | volumeStep: 0.1, 15 | seekStep: 5, 16 | enableMute: true, 17 | enableVolumeScroll: true, 18 | enableFullscreen: true, 19 | enableNumbers: true, 20 | enableJogStyle: false, 21 | alwaysCaptureHotkeys: false, 22 | enableModifiersForNumbers: true, 23 | playPauseKey: playPauseKey, 24 | rewindKey: rewindKey, 25 | forwardKey: forwardKey, 26 | volumeUpKey: volumeUpKey, 27 | volumeDownKey: volumeDownKey, 28 | muteKey: muteKey, 29 | fullscreenKey: fullscreenKey, 30 | customKeys: {} 31 | }; 32 | 33 | var cPlay = 1, 34 | cRewind = 2, 35 | cForward = 3, 36 | cVolumeUp = 4, 37 | cVolumeDown = 5, 38 | cMute = 6, 39 | cFullscreen = 7; 40 | 41 | // Use built-in merge function from Video.js v5.0+ or v4.4.0+ 42 | var mergeOptions = videojs.mergeOptions || videojs.util.mergeOptions; 43 | options = mergeOptions(def_options, options || {}); 44 | 45 | var volumeStep = options.volumeStep, 46 | seekStep = options.seekStep, 47 | enableMute = options.enableMute, 48 | enableVolumeScroll = options.enableVolumeScroll, 49 | enableFull = options.enableFullscreen, 50 | enableNumbers = options.enableNumbers, 51 | enableJogStyle = options.enableJogStyle, 52 | alwaysCaptureHotkeys = options.alwaysCaptureHotkeys, 53 | enableModifiersForNumbers = options.enableModifiersForNumbers; 54 | 55 | // Set default player tabindex to handle keydown and doubleclick events 56 | if (!pEl.hasAttribute('tabIndex')) { 57 | pEl.setAttribute('tabIndex', '-1'); 58 | } 59 | 60 | // Remove player outline to fix video performance issue 61 | pEl.style.outline = "none"; 62 | 63 | if (alwaysCaptureHotkeys || !player.autoplay()) { 64 | player.one('play', function () { 65 | pEl.focus(); // Fixes the .vjs-big-play-button handing focus back to body instead of the player 66 | }); 67 | } 68 | 69 | player.on('userinactive', function () { 70 | // When the control bar fades, re-apply focus to the player if last focus was a control button 71 | var cancelFocusingPlayer = function () { 72 | clearTimeout(focusingPlayerTimeout); 73 | }; 74 | var focusingPlayerTimeout = setTimeout(function () { 75 | player.off('useractive', cancelFocusingPlayer); 76 | if (doc.activeElement.parentElement == pEl.querySelector('.vjs-control-bar')) { 77 | pEl.focus(); 78 | } 79 | }, 10); 80 | 81 | player.one('useractive', cancelFocusingPlayer); 82 | }); 83 | 84 | player.on('play', function () { 85 | // Fix allowing the YouTube plugin to have hotkey support. 86 | var ifblocker = pEl.querySelector('.iframeblocker'); 87 | if (ifblocker && ifblocker.style.display === '') { 88 | ifblocker.style.display = "block"; 89 | ifblocker.style.bottom = "39px"; 90 | } 91 | }); 92 | 93 | var keyDown = function keyDown(event) { 94 | var ewhich = event.which, curTime; 95 | var ePreventDefault = event.preventDefault; 96 | // When controls are disabled, hotkeys will be disabled as well 97 | if (player.controls()) { 98 | 99 | // Don't catch keys if any control buttons are focused, unless alwaysCaptureHotkeys is true 100 | var activeEl = doc.activeElement; 101 | if (alwaysCaptureHotkeys || 102 | activeEl == pEl || 103 | activeEl == pEl.querySelector('.vjs-tech') || 104 | activeEl == pEl.querySelector('.vjs-control-bar') || 105 | activeEl == pEl.querySelector('.iframeblocker')) { 106 | 107 | switch (checkKeys(event, player)) { 108 | // Spacebar toggles play/pause 109 | case cPlay: 110 | ePreventDefault(); 111 | if (alwaysCaptureHotkeys) { 112 | // Prevent control activation with space 113 | event.stopPropagation(); 114 | } 115 | 116 | if (player.paused()) { 117 | player.play(); 118 | } else { 119 | player.pause(); 120 | } 121 | break; 122 | 123 | // Seeking with the left/right arrow keys 124 | case cRewind: // Seek Backward 125 | ePreventDefault(); 126 | curTime = player.currentTime() - seekStep; 127 | // The flash player tech will allow you to seek into negative 128 | // numbers and break the seekbar, so try to prevent that. 129 | if (player.currentTime() <= seekStep) { 130 | curTime = 0; 131 | } 132 | player.currentTime(curTime); 133 | break; 134 | case cForward: // Seek Forward 135 | ePreventDefault(); 136 | player.currentTime(player.currentTime() + seekStep); 137 | break; 138 | 139 | // Volume control with the up/down arrow keys 140 | case cVolumeDown: 141 | ePreventDefault(); 142 | if (!enableJogStyle) { 143 | player.volume(player.volume() - volumeStep); 144 | } else { 145 | curTime = player.currentTime() - 1; 146 | if (player.currentTime() <= 1) { 147 | curTime = 0; 148 | } 149 | player.currentTime(curTime); 150 | } 151 | break; 152 | case cVolumeUp: 153 | ePreventDefault(); 154 | if (!enableJogStyle) { 155 | player.volume(player.volume() + volumeStep); 156 | } else { 157 | player.currentTime(player.currentTime() + 1); 158 | } 159 | break; 160 | 161 | // Toggle Mute with the M key 162 | case cMute: 163 | if (enableMute) { 164 | player.muted(!player.muted()); 165 | } 166 | break; 167 | 168 | // Toggle Fullscreen with the F key 169 | case cFullscreen: 170 | if (enableFull) { 171 | if (player.isFullscreen()) { 172 | player.exitFullscreen(); 173 | } else { 174 | player.requestFullscreen(); 175 | } 176 | } 177 | break; 178 | 179 | default: 180 | // Number keys from 0-9 skip to a percentage of the video. 0 is 0% and 9 is 90% 181 | if ((ewhich > 47 && ewhich < 59) || (ewhich > 95 && ewhich < 106)) { 182 | // Do not handle if enableModifiersForNumbers set to false and keys are Ctrl, Cmd or Alt 183 | if (enableModifiersForNumbers || !(event.metaKey || event.ctrlKey || event.altKey)) { 184 | if (enableNumbers) { 185 | var sub = 48; 186 | if (ewhich > 95) { 187 | sub = 96; 188 | } 189 | var number = ewhich - sub; 190 | ePreventDefault(); 191 | player.currentTime(player.duration() * number * 0.1); 192 | } 193 | } 194 | } 195 | 196 | // Handle any custom hotkeys 197 | for (var customKey in options.customKeys) { 198 | var customHotkey = options.customKeys[customKey]; 199 | // Check for well formed custom keys 200 | if (customHotkey && customHotkey.key && customHotkey.handler) { 201 | // Check if the custom key's condition matches 202 | if (customHotkey.key(event)) { 203 | ePreventDefault(); 204 | customHotkey.handler(player, options); 205 | } 206 | } 207 | } 208 | } 209 | } 210 | } 211 | }; 212 | 213 | var doubleClick = function doubleClick(event) { 214 | // When controls are disabled, hotkeys will be disabled as well 215 | if (player.controls()) { 216 | 217 | // Don't catch clicks if any control buttons are focused 218 | var activeEl = event.relatedTarget || event.toElement || doc.activeElement; 219 | if (activeEl == pEl || 220 | activeEl == pEl.querySelector('.vjs-tech') || 221 | activeEl == pEl.querySelector('.iframeblocker')) { 222 | 223 | if (enableFull) { 224 | if (player.isFullscreen()) { 225 | player.exitFullscreen(); 226 | } else { 227 | player.requestFullscreen(); 228 | } 229 | } 230 | } 231 | } 232 | }; 233 | 234 | var mouseScroll = function mouseScroll(event) { 235 | // When controls are disabled, hotkeys will be disabled as well 236 | if (player.controls()) { 237 | var activeEl = event.relatedTarget || event.toElement || doc.activeElement; 238 | if (alwaysCaptureHotkeys || 239 | activeEl == pEl || 240 | activeEl == pEl.querySelector('.vjs-tech') || 241 | activeEl == pEl.querySelector('.iframeblocker') || 242 | activeEl == pEl.querySelector('.vjs-control-bar')) { 243 | 244 | if (enableVolumeScroll) { 245 | event = window.event || event; 246 | var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); 247 | event.preventDefault(); 248 | 249 | if (delta == 1) { 250 | player.volume(player.volume() + volumeStep); 251 | } else if (delta == -1) { 252 | player.volume(player.volume() - volumeStep); 253 | } 254 | } 255 | } 256 | } 257 | }; 258 | 259 | var checkKeys = function checkKeys(e, player) { 260 | // Allow some modularity in defining custom hotkeys 261 | 262 | // Play/Pause check 263 | if (options.playPauseKey(e, player)) { 264 | return cPlay; 265 | } 266 | 267 | // Seek Backward check 268 | if (options.rewindKey(e, player)) { 269 | return cRewind; 270 | } 271 | 272 | // Seek Forward check 273 | if (options.forwardKey(e, player)) { 274 | return cForward; 275 | } 276 | 277 | // Volume Up check 278 | if (options.volumeUpKey(e, player)) { 279 | return cVolumeUp; 280 | } 281 | 282 | // Volume Down check 283 | if (options.volumeDownKey(e, player)) { 284 | return cVolumeDown; 285 | } 286 | 287 | // Mute check 288 | if (options.muteKey(e, player)) { 289 | return cMute; 290 | } 291 | 292 | // Fullscreen check 293 | if (options.fullscreenKey(e, player)) { 294 | return cFullscreen; 295 | } 296 | }; 297 | 298 | function playPauseKey(e) { 299 | // Space bar or MediaPlayPause 300 | return (e.which === 32 || e.which === 179); 301 | } 302 | 303 | function rewindKey(e) { 304 | // Left Arrow or MediaRewind 305 | return (e.which === 37 || e.which === 177); 306 | } 307 | 308 | function forwardKey(e) { 309 | // Right Arrow or MediaForward 310 | return (e.which === 39 || e.which === 176); 311 | } 312 | 313 | function volumeUpKey(e) { 314 | // Up Arrow 315 | return (e.which === 38); 316 | } 317 | 318 | function volumeDownKey(e) { 319 | // Down Arrow 320 | return (e.which === 40); 321 | } 322 | 323 | function muteKey(e) { 324 | // M key 325 | return (e.which === 77); 326 | } 327 | 328 | function fullscreenKey(e) { 329 | // F key 330 | return (e.which === 70); 331 | } 332 | 333 | player.on('keydown', keyDown); 334 | player.on('dblclick', doubleClick); 335 | player.on('mousewheel', mouseScroll); 336 | player.on("DOMMouseScroll", mouseScroll); 337 | 338 | return this; 339 | }); 340 | 341 | -------------------------------------------------------------------------------- /src/styles/bangumi.css: -------------------------------------------------------------------------------- 1 | /*index info*/ 2 | .bangumi-banner-block { 3 | margin: 20px auto; 4 | width: 640px; 5 | height: 200px; 6 | 7 | position: relative; 8 | overflow: hidden; 9 | } 10 | 11 | .bangumi-banner { 12 | width: 640px; 13 | height: 200px; 14 | } 15 | 16 | .area-banner > h2 { 17 | margin: 20px 10px; 18 | font-size: 25px; 19 | font-weight: bold; 20 | color: #666; 21 | } 22 | 23 | .bangumi_item { 24 | width: 200px; 25 | height: 120px; 26 | margin: 10px 20px; 27 | 28 | border-radius: 4px; 29 | border: 1px solid #f6f6f6;; 30 | overflow: hidden; 31 | 32 | -webkit-transition: border .2s ease; 33 | -moz-transition: border .2s ease; 34 | -ms-transition: border .2s ease; 35 | -o-transition: border .2s ease; 36 | transition: border .2s ease; 37 | } 38 | 39 | .bangumi_item:hover { 40 | border: 1px solid #666; 41 | box-shadow: 0 0 5px rgb(179, 178, 178); 42 | } 43 | 44 | .bangumi_item:hover > .info > .title { 45 | color: #e04270; 46 | } 47 | 48 | .bangumi_item > .img-small { 49 | width: 99px; 50 | height: 119px; 51 | overflow: hidden; 52 | } 53 | 54 | .bangumi_item > .info { 55 | width: 99px; 56 | height: 119px; 57 | padding: 0 5px; 58 | overflow: hidden; 59 | } 60 | 61 | .bangumi_item > .info > .title { 62 | display: block; 63 | 64 | padding: 5px 5px; 65 | position: relative; 66 | font-size: 13px; 67 | font-weight: bold; 68 | color: #000; 69 | 70 | height: 60px; 71 | overflow: hidden; 72 | } 73 | 74 | .bangumi_item > .info > .other { 75 | display: block; 76 | 77 | padding: 10px 5px; 78 | position: relative; 79 | font-size: 11px; 80 | font-weight: bold; 81 | color: #666; 82 | 83 | max-height: 50px; 84 | overflow: hidden; 85 | } 86 | 87 | .img-small > img { 88 | width: 100%; 89 | } 90 | 91 | .list-bangumi-block { 92 | width: 960px; 93 | max-height: 140px; 94 | overflow: hidden; 95 | margin: 20px auto; 96 | } 97 | 98 | .toggle-btn { 99 | width: 100px; 100 | height: 30px; 101 | margin: 10px auto; 102 | 103 | border-radius: 4px; 104 | border: 1px solid #666; 105 | } 106 | 107 | .toggle-btn > a { 108 | display: block; 109 | text-align: center; 110 | margin: 5px auto; 111 | font-size: 13px; 112 | font-weight: bold; 113 | color: #555; 114 | } 115 | 116 | .toggle-btn > a:hover { 117 | color: #e04270; 118 | } 119 | 120 | /*bangumi\sp info*/ 121 | 122 | .bangumi-header { 123 | width: 1000px; 124 | min-height: 400px; 125 | margin: 20px auto; 126 | background-color: #fff; 127 | } 128 | 129 | .bangumi-header > h2 { 130 | display: block; 131 | position: relative; 132 | left: 150px; 133 | top: 25px; 134 | width: 600px; 135 | 136 | color: #525659; 137 | overflow: hidden; 138 | line-height: 24px; 139 | text-overflow: ellipsis; 140 | font-weight: 400; 141 | white-space: nowrap; 142 | font-size: 20px 143 | } 144 | 145 | .bangumi-header-img { 146 | float: left; 147 | position: relative; 148 | left: 50px; 149 | top: 25px; 150 | width: 200px; 151 | height: 293px; 152 | } 153 | 154 | .bangumi-header-img > img { 155 | width: 200px; 156 | } 157 | 158 | .bangumi-header-info { 159 | float: left; 160 | position: relative; 161 | left: 150px; 162 | top: 80px; 163 | width: 600px; 164 | min-height: 250px; 165 | color: #666; 166 | font-weight: bold; 167 | } 168 | 169 | .bangumi-header-info > p { 170 | font-size: 14px; 171 | line-height: 150%; 172 | font-weight: 100; 173 | } 174 | 175 | .bangumi-header-info > span { 176 | font-size: 14px; 177 | line-height: 150%; 178 | font-weight: 100; 179 | } 180 | 181 | .bangumi-part { 182 | background-color: #fff; 183 | width: 1000px; 184 | min-height: 200px; 185 | } 186 | 187 | .bangumi-part > ul { 188 | margin: 0 0; 189 | position: relative; 190 | top: 40px; 191 | left: 35px; 192 | width: 90%; 193 | } 194 | 195 | .bangumi-part > p { 196 | position: relative; 197 | top: 20px; 198 | left: 30px; 199 | font-size: 25px; 200 | font-weight: bold; 201 | color: #666; 202 | } 203 | 204 | .bangumi-part > ul > li { 205 | display: block; 206 | margin: 5px 10px; 207 | min-width: 64px; 208 | padding: 0 5px; 209 | 210 | height: 32px; 211 | border: 1px solid #999; 212 | border-radius: 4px; 213 | 214 | -webkit-transition: background-color .2s ease; 215 | -moz-transition: background-color .2s ease; 216 | -ms-transition: background-color .2s ease; 217 | -o-transition: background-color .2s ease; 218 | transition: background-color .2s ease; 219 | } 220 | 221 | .bangumi-part > ul > li > a { 222 | display: block; 223 | height: 32px; 224 | text-decoration: none; 225 | text-align: center; 226 | } 227 | 228 | .bangumi-part > ul > li:hover { 229 | background-color: #aeb7bf; 230 | } 231 | 232 | .bangumi-part-span { 233 | text-decoration: none; 234 | color: #999; 235 | position: relative; 236 | top: 6px; 237 | } 238 | 239 | .bangumi-part > ul > li:hover .video_more_span { 240 | color: #fff; 241 | } 242 | 243 | .bangumi-season { 244 | background-color: #fff; 245 | margin: 20px auto; 246 | padding: 0 10px; 247 | width: 1000px; 248 | } 249 | -------------------------------------------------------------------------------- /src/styles/player.css: -------------------------------------------------------------------------------- 1 | .video-js .vjs-danmu { 2 | overflow: hidden; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | bottom: 0; 7 | right: 0; 8 | pointer-events: none; 9 | -webkit-touch-callout: none; 10 | -webkit-user-select: none; 11 | -khtml-user-select: none; 12 | -moz-user-select: none; 13 | -ms-user-select: none; 14 | user-select: none; 15 | } 16 | 17 | .video-js .vjs-danmu .cmt { 18 | white-space: nowrap; 19 | padding: 3px 0 0 0; 20 | margin: 0; 21 | line-height: 100%; 22 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 23 | font-weight: 400; 24 | text-decoration: none; 25 | letter-spacing: 0; 26 | position: absolute; 27 | word-break: keep-all; 28 | text-wrap: none; 29 | border: 0 !important; 30 | -webkit-text-size-adjust: none; 31 | text-size-adjust: none; 32 | } 33 | 34 | .video-js .vjs-danmu .cmt.noshadow { 35 | text-shadow: none !important; 36 | } 37 | 38 | .video-js .vjs-danmu .cmt.rshadow { 39 | text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white !important; 40 | } 41 | 42 | .vjs-danmu-control .vjs-menu-icon:before { 43 | content: '\f110'; 44 | font-family: VideoJS; 45 | font-weight: normal; 46 | font-style: normal; 47 | font-size: 1.8em; 48 | line-height: 1.67em; 49 | 50 | } 51 | 52 | .vjs-danmu-control .vjs-danmu-switch { 53 | font-size: 1em; 54 | line-height: 3em; 55 | position: absolute; 56 | top: 0; 57 | left: 0; 58 | width: 100%; 59 | height: 100%; 60 | text-align: center; 61 | box-sizing: inherit; 62 | } 63 | 64 | /* animation */ 65 | .cmt { 66 | will-change: transform; 67 | } 68 | 69 | @keyframes cmt-move-left { 70 | from { 71 | transform: translateX(100%); 72 | } 73 | } 74 | 75 | @keyframes cmt-move-right { 76 | to { 77 | transform: translateX(-100%); 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | --------------------------------------------------------------------------------