# video实现简易版弹幕视频

认识两个HTML5标签,分别是video和source。

# video标签属性

  • autoplay:表示视频是否就绪后马上播放,值为autoplay
  • controls:表示是否向用户显示控件,值为controls
  • height:表示视频播放器高度
  • width:表示视频播放器宽度
  • loop:表示当媒介文件完成播放后是否再次开始播放,值为loop
  • preload:表示视频是否在页面加载时进行加载,并预备播放,若使用autoplay,则忽略该属性,值为preload
  • poster:指定视频加载时显示的图片url
  • src:指定要播放的视频url

# source标签属性

  • src:指定要链接的媒介资源URL
  • type:表示资源的MIME类型。比如在video标签中支持类型有video/mp4、video/ogg和video/webm

# 如何制作一个简易版弹幕视频?

  • HTML部分

    内容分为屏幕层和弹幕层,屏幕层由视频、输入框和按钮组成,比较简单。

    <!--屏幕层-->
    <div class="screen">
        <!--视频-->
        <video class="screen-video" autoplay loop>
            <source src="./video/happy.mp4" type="video/mp4">
            您的浏览器不支持 video 标签。
        </video>
        <!--输入框-->
        <input type="text" class="screen-input">
        <!--发送按钮-->
        <input type="button" value="发送" class="screen-button" onclick="send()">
    </div>
    <!--弹幕层-->
    <div class="barrage"></div>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  • CSS部分

    布局结合百分比布局和REM布局,使网页响应式,体验稍好。

    .screen-video {    
        width:80%;
        margin: 1rem 10%;
        border: 1px solid white; 
        
        /*填充父元素,IE和Edge不支持,真头疼!!!*/
        object-fit: fill;      
    }
    
    .screen-input {
        position: absolute;
        left:10%;
    
        height: 1.5rem;
    
        line-height: 1.5rem;
        font-size: 1rem;
    }
    
    .screen-button {
        position: absolute;
        right: 10%;
    
        height: 1.5rem;
    
        line-height: 1.5rem;
        letter-spacing: 0.2em;
        font-size: 0.8rem;
    
        cursor: pointer;
    }
    
    .barrage {
        position: absolute;
        left:10%;
    
        width: 80%;
        
        /*x轴方向上超过则隐藏内容*/
        overflow-x: hidden;
        
        z-index: 999;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
  • JavaScript部分

    第一步,设置事件监听

    //窗口加载时初始化
    window.addEventListener("load",init,false);
    //窗口大小变化时重新初始化
    window.addEventListener("resize",init,false);
    //输入框回车时发送弹幕文字
    content.addEventListener("keyup",function(event) {
        var e = event||window.event;
        if (e.which == "13"|| e.keyCode =="13") {
            send();
        }
    },false);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    第二步,事件处理

    • 初始化

      function init(){
      
          // 实现REM布局需要根据窗口大小来控制根字体大小
          // 浏览器默认字体大小是16px,最小字体是12px
          html.style.fontSize = parseInt(doc.clientWidth /45)+"px";
          if(parseInt(html.style.fontSize)<12){
              html.style.fontSize = "12px";
          }
          
          //设置video的高度
          video.style.height =  parseInt(video.offsetWidth/16*9)+"px";
          //设置input的宽度
          content.style.width = parseInt(video.offsetWidth - btn_send.offsetWidth)+"px";
          //设置button的位置
          btn_send.style.top = (video.offsetHeight + video.offsetTop *2)+"px";
          //设置barrage的位置和高度
          barrage.style.top =video.offsetTop +"px";
          barrage.style.height = video.offsetHeight +"px"; 
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19

      以前通过获取样式对象进行初始化,如下,

      var sstyle = screen.currentStyle||window.getComputedStyle(screen);
      var vstyle = video.currentStyle||window.getComputedStyle(video);
      
      1
      2

      之后放弃使用这种方式,一是有兼容性,比如获取margin,在Chrome和FF正常,但是到了IE和FF则不支持了,获取marginTop、marginLeft等则正常,虽然可以解决,但是为了这点问题,折腾半天,实在没必要。二是JS需要关注CSS是否应用了该样式,如果没有,那么获取的是默认值,比如margin默认值是auto,但是我想获取具体的数值,这就不好处理了。总之,涉及BOM操作的还是少用比较好。

    • 发送弹幕内容

      function send() {
          //发送内容
          var val = trim(content.value);
          //创建的节点
          var span;
          //偏移量
          var offset = 0;
          //帧ID
          var id;
          if (val != "") {
              //创建节点
              span = document.createElement("span");
              //设置文本
              span.innerHTML = val;
              //设置样式
              span.className = "barrage-span";
              //添加
              barrage.appendChild(span);
              //设置初始位置,注意添加元素后宽度和高度才生效。
              span.style.top = getRandomNumber(parseInt(video.offsetHeight-span.offsetHeight)) + "px";
              span.style.left =  video.offsetWidth+"px";
              span.style.color = getRandomColor();
              //设置监听事件
              span.addEventListener("mouseover", stop, false);
              span.addEventListener("mouseout", scroll, false);
              //默认初始文本滚动
              scroll();
          }
          content.value = "";
          
          function scroll() {
      
              offset += 2;
      
              span.style.transform = "translateX(-" + offset + "px)";
              span.style.transition = "transform 0s linear";
      
              if (offset >= parseInt(video.offsetWidth+span.offsetWidth)) {
                  stop();
                  barrage.removeChild(span);
              }else{    
                  id = window.requestAnimationFrame(scroll);
              }  
          }
      
          function stop(){
              window.cancelAnimationFrame(id);
          }
      
          //去除两边空格
          function trim(s) {
              return s.replace(/(^\s*)|(\s*$)/g, "");
          }
      
          //指定范围内获取随机数
          function getRandomNumber(value) {
              return parseInt(Math.random() * value);
          }
      
          //获取任意颜色
          function getRandomColor() {
              var color = "#";
              var j;
              for (var i = 0; i < 6; i++) {
                  j = getRandomNumber(16);
                  color += str.substring(j, j + 1);
              }
              return color;
          }
      }
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70

      在处理发送弹幕时,发现文字不时会超出弹幕层,从而出现滚动条,结果发现是下面这句代码出现了问题,

      span.style.top = getRandomNumber(parseInt(video.offsetHeight-span.offsetHeight)) + "px";
      
      1

      span.offsetHeight打印结果总为0,后来把这句代码放在父元素添加span元素之后,即下面这句代码就可以了。这说明了要使元素的offsetWidth、offsetHeight等属性值生效,需要注意元素是否已经渲染到浏览器上,单单创建一个元素对象,但是没有添加到文档中,只是存放在内存,还没有被浏览器渲染。先记录下来,避免以后填坑,哈哈。

      barrage.appendChild(span);
      
      1

# 附上源码:

https://github.com/muzhidong/frontend-demo/tree/master/barrage-video (opens new window)