弹幕的实现

一、前言

  今天浏览某网站看到一个活动页有内嵌的弹幕模块(图一),但是看到移动的弹幕重叠很多,不忍直视啊。突然想起很久之前自己写写过类似的弹幕,就翻出来看了一下,呵,也是不忍直视的,最后再附上当年的效果以及代码;

二、大话几点

1、弹幕应用场景,视频中,直播中,微信墙等;

2、弹幕增加了大家的互动性,不再是单纯的傻白甜的看着视频,还可以吐吐槽,增加趣味性,但对于密集恐惧症的我,每当一大波弹幕来袭,我习惯性的cut off;

3、现在视频类的弹幕页面一般内嵌到视频中,微信墙等互动弹幕页面一般用h5实现,呈现的方式都差不多,有的弹幕可以每条进行操作,有的只能看。有的弹幕每条弹幕还要根据用户的相关等级,然后展示不同的效果,满足不同玩家的诉求,各种玩;

4、好的弹幕应该至少满足:弹幕分布密度,出现时机,速度,颜色,字体大小等都要做的相得益彰,至少能吸引大家进行互动。这就要考虑要一定的算法了,后面说。比如b站的弹幕就做的很好;

5、弹幕的后台实现可以通过websocket实现,当然也可以借助node实现。当用户输入弹幕,弹幕需经过特殊处理,比如经过第三方(数美等)过滤敏感关键字等,最后再把内容下发,觜最后展示;

三、思考:怎么做到弹幕均匀排布,不会重叠呢?还有速度控制问题。

1、可以想象把弹幕面板分成几个管道,每条弹幕随机分配(当然不是简单的随机,应该加上点概率论的知识)到每条管道中,当前方有弹幕时候,该弹幕的位置就要与前方弹幕保持一定计算的距离;

2、每条弹幕的速速要结合当前弹幕的长度和移动的位置去动态的添加初始速度,当然运行速度可以可以有linear ease-in ease-out ...等把控;

四、最后

很久前写的弹幕,没有对弹幕分布和速度进行把控,当然也没有后台,纯粹前端展示了,我就不折腾了。

  -------------------------------------------------

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>barrage</title>
  <style>
    .box{
      width: 800px;
      height: 500px;
      margin:0 auto;
    }
    .barrage-container-wrap{
        width: 100%;
        height: 500px;
        position: relative;
        overflow: hidden;
        background: url(‘./img/bg.jpg‘) no-repeat;
        background-size: 100% 100%;
    }
    .barrage-container{
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 30px;
        cursor: default;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
    }
    .barrage-item{
        position:absolute;
        top:0;
        left: 100%;
        white-space: nowrap;
        cursor: pointer;
        color:#fff;
    }
    .barrage-item .barrage-tip{
        display: none;
        position: absolute;
        top:-26px;
        padding: 7px 15px;
        line-height: 12px;
        font-size: 12px;
        color: #f20606;
        background-color: #fff;
        white-space: nowrap;
        border: 1px solid #ddd;
        border-radius: 8px;
        -webkit-box-shadow: 0 0 10px 1px rgba(0,0,0,.1);
        box-shadow: 0 0 10px 1px rgba(0,0,0,.1);
        -webkit-transform-origin: 15px 100%;
        -ms-transform-origin: 15px 100%;
        transform-origin: 15px 100%;
        webkit-animation: tipScale cubic-bezier(.22,.58,.12,.98) .4s;
        animation: tipScale cubic-bezier(.22,.58,.12,.98) .4s;
      }
    .send-wrap{
      margin-top: 20px;
    }
    .input{
        width: 300px;
        height: 30px;
        line-height: 30px;
        outline: none;
        -webkit-appearance: none;
        border-radius: 5px;
        padding:0;
        padding-left: 10px;
    }
    .send-btn{
        height: 38px;
        line-height: 38px;
        text-align: center;
        font-weight: bold;
        color: #fff;
        background: #93d0ea;
        text-shadow:1px 1px 1px #333;
        border-radius: 5px;
        margin:0 20px 20px 0;
        position: relative;
        overflow: hidden;
        cursor: pointer;
        padding:5px 15px;
    }
    @-webkit-keyframes tipScale{
        0{
          -webkit-transform: scale(0);
           transform: scale(0);
        }
        50% {
           -webkit-transform: scale(1.1);
           transform: scale(1.1);
        }
        100% {
          -webkit-transform: scale(1);
          transform: scale(1);
        }
    }
  </style>
</head>
<body>
<div class="box">
   <div class="barrage-container-wrap clearfix">
    <div class="barrage-container">
    </div>
  </div>
  <div class="send-wrap">
    <input type="text" class="input" placeholder="弹幕发送">
    <span class="send-btn">发送</span>
  </div>
</div>
</body>
<script>
;(function(){
  var barrageArray = [
          {
            url: ‘用户头像‘,
            text: ‘秋天爱美丽‘,
            level: 10
          },
          {
            url: ‘用户头像‘,
            text: ‘今天很开心啊‘,
            level: 10
          },
          {
            url: ‘用户头像‘,
            text: ‘winter has come‘,
            level: 10
          },
          {
            url: ‘‘,
            text: ‘土耳其现在形势‘,
            level: 10
          },
          {
            url: ‘‘,
            text: ‘没事早点回家吃饭啊‘,
            level: 10
          },
           {
            url: ‘‘,
            text: ‘这主角真实醉了,不会回啊‘,
            level: 10
          },
          {
            url: ‘‘,
            text: ‘背景音乐真好听啊‘,
            level: 10
          },
          {
            url: ‘‘,
            text: ‘背景音乐是***‘,
            level: 10
          },
          {
            url: ‘‘,
            text: ‘经费在燃烧啊‘,
            level: 10
          },
          {
            url: ‘‘,
            text: ‘国产良心剧‘,
            level: 10
          },
      ];
  var barrageColorArray = [
    ‘#0099CC‘,‘#333333‘, ‘#009966‘,‘#FFFF66‘,‘#9933FF‘,‘#FFFF99‘,‘#CCCCFF‘,‘#CC9933‘,‘#FFFF66‘
  ];
  var barrageTipWidth = 50; //提示语的长度

  var barrageBoxWrap = document.querySelector(‘.barrage-container-wrap‘);;
  var barrageBox = document.querySelector(‘.barrage-container‘);
  var inputBox = document.querySelector(‘.input‘);
  var sendBtn = document.querySelector(‘.send-btn‘);

  //容器的宽高度
  var barrageWidth = ~~window.getComputedStyle(barrageBoxWrap).width.replace(‘px‘,‘‘);
  var barrageHeight = ~~window.getComputedStyle(barrageBoxWrap).height.replace(‘px‘,‘‘);

  //发送
  function sendMsg(){
    var inputValue = inputBox.value;
    inputValue .replace(/\ +/g, "");

    if (inputValue.length <= 0) {
        alert(‘请输入‘);
        return false;
    }

    //生成弹幕
    createBarrage(inputValue,true);
    inputBox.value = ‘‘;
  }

  //创建弹幕
  function createBarrage(msg, isSendMsg){
    var divNode = document.createElement(‘div‘);
    var spanNode = document.createElement(‘span‘);

    divNode.innerHTML = msg;
    divNode.classList.add(‘barrage-item‘);
    barrageBox.appendChild(divNode);

    spanNode.innerHTML = ‘举报‘;
    spanNode.classList.add(‘barrage-tip‘);
    divNode.appendChild(spanNode);

    barrageOffsetLeft = getRandom(barrageWidth, barrageWidth*2);
    barrageOffsetLeft = isSendMsg ? barrageWidth : barrageOffsetLeft
    barrageOffsetTop = getRandom(10, barrageHeight-10);
    barrageColor = barrageColorArray[Math.floor(Math.random()*(barrageColorArray.length))];

    //执行初始化滚动
    initBarrage.call(divNode,{
      left : barrageOffsetLeft,
      top : barrageOffsetTop,
      color : barrageColor
    });
  }

  //初始化弹幕移动(速度,延迟)
  function initBarrage(obj) {
    //初始化
    obj.top = obj.top || 0;
    obj.class = obj.color || ‘#fff‘;
    this.style.left = obj.left + ‘px‘;
    this.style.top = obj.top + ‘px‘;
    this.style.color = obj.color;   

    //添加属性
    this.distance = 0;
    this.width = ~~window.getComputedStyle(this).width.replace(‘px‘,‘‘);
    this.offsetLeft = obj.left;
    this.timer = null;

    //弹幕子节点
    var barrageChileNode = this.children[0];
    barrageChileNode.style.left = (this.width-barrageTipWidth)/2 + ‘px‘;

    //运动
    barrageAnimate(this);

    //停止
    this.onmouseenter = function(){
      barrageChileNode.style.display= ‘block‘;
      cancelAnimationFrame(this.timer);
    };

    this.onmouseleave = function(){
      barrageChileNode.style.display = ‘none‘;
      barrageAnimate(this);
    };

    //举报
    barrageChileNode.onclick = function(){
      alert(‘举报成功‘);
    }
  }

  //弹幕动画
  function barrageAnimate(obj){
    move(obj);

    if(Math.abs(obj.distance) < obj.width+obj.offsetLeft){
      obj.timer = requestAnimationFrame(function(){
        barrageAnimate(obj);
      });
    }else{
      cancelAnimationFrame(obj.timer);
      //删除节点
      obj.parentNode.removeChild(obj);
    }
  }

  //移动
  function move(obj){
    obj.distance--;
    obj.style.transform = ‘translateX(‘+obj.distance+‘px)‘;
    obj.style.webkitTransform = ‘translateX(‘+obj.distance+‘px)‘;
  }

  //随机获取高度
  function getRandom(start, end){
    return start +(Math.random() * (end - start));
  }

  /*******初始化事件**********/
  //系统数据
  barrageArray.forEach(function(item,index){
    createBarrage(item.text, false);
  });

  //点击发送
  sendBtn.onclick = sendMsg;   //点击发送

  //回车
  inputBox.onkeydown = function(e){
    e = e|| window.event;
    if(e.keyCode == 13){
      send();
    }
  }

})()

//兼容写法
(function() {
    var lastTime = 0;
    var vendors = [‘webkit‘, ‘moz‘];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + ‘RequestAnimationFrame‘];
        window.cancelAnimationFrame = window[vendors[x] + ‘CancelAnimationFrame‘] ||    // Webkit中此取消方法的名字变了
                                      window[vendors[x] + ‘CancelRequestAnimationFrame‘];
    }

    if (!window.requestAnimationFrame) {
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16.7 - (currTime - lastTime));
            var id = window.setTimeout(function() {
                callback(currTime + timeToCall);
            }, timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };
    }
    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
    }
}());
</script>
</html>

  

  

原文地址:https://www.cnblogs.com/leaf930814/p/8427461.html

时间: 2024-10-21 04:25:34

弹幕的实现的相关文章

jQuery实现评论弹幕、弹幕漂浮、滚动代码

1.html代码和jquery代码: <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>document</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&g

2、微信小程序之弹幕的实现(无后台)

对弹幕功能主要利用环信来实现的,读者也许对环信这个东西很陌生,请先自行了解这环信再来看这文章. 环信开发文档:http://docs.easemob.com/im/400webimintegration/10webimintro 请大家看文档看WebIM这个模块,环信官网也提供了小程序的demo,建议读者先自行下载个demo.,了解环信的基础功能. 环信小程序demo下载地址:https://github.com/easemob/webim-weixin-xcx 弹幕功能主要是利用了聊天室来实现

用WPF实现一个弹幕播放器

最近写了一系列关于WPF二维动画的Blog,今天准备写个程序来实战一下.由于手头上确实没有什么好的例子,便写了个离线的弹幕播放器,效果如下: 实际上,用WPF实现一个弹幕播放器还是比较简单的,主要分为如下几个步骤: 下载离线弹幕文件,并解析为程序比较方便的识别格式 加载弹幕数据,并生成弹幕动画TimeLine,这里我将每一个弹幕用一个TextBlock表示,用一个Canvas来承载这些弹幕.用关键帧动画来表示弹幕,并将动画和TextBlock关联起来. 使用MediaElement加载弹幕,使用

android 弹幕评论效果

纯粹依照自己的想法仿照b站的弹幕写的一个demo,不知道正确的姿势怎么样的. demo下载地址 首先.一条弹幕就是一个textview public abstract class Danmu extends TextView{ private Context context; private int position;//弹幕的位置,在屏幕哪一行 public Danmu(Context context) { super(context); this.context=context; setSi

marquee-:模拟弹幕

marquee:基本已被弃用!!1 可以模拟弹幕效果 1.方向:direction up  right   left  down 2.behavior 3.scrollamountsc --> <marquee direction="left" scrollamount="100" scrolldelay="1" loop="2"> <div> <p>哈哈哈</p> <

使用swoole和websocket结合来制造弹幕

在知乎上无意中看到了一篇有关这个的话题https://zhuanlan.zhihu.com/p/23992890,刚好没事也好久没弄swoole了就自己按照知乎上的那篇文站实操了一下 那个试验中有几个点需要注意一下: 1.首先得安装php的swoole扩展 pecl install swoole  用php -m检查扩展是否安好 2.jquery包和弹幕所需要的包最好用他那个dist中的文件,不然的话有可能页面js会报错 3. liunx发行版安装php5.3+并且编译swoole扩展,在php

JS原生实现视频弹幕Demo(仿)

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Js原生弹幕</title> <link rel="stylesheet" href=""&

那些H5用到的技术(4)——弹幕

前言思路实现模式无限循环模式时间线模式停止显示弹幕 前言 以前玩卷轴射击游戏的时候,大量的BOSS子弹让我们无路可逃的时候,让我见识到了真正弹幕的威力,可自从A站B站火了之后,大量评论留言参与到了视频的播放中,也让我见识到了"弹幕"的威力,压根视频就没法看了--全看评论去了,就是那么好玩. 现在没有弹幕功能都不好意思说是做视频or直播网站的.而我们也不能落后呐,产品提需求了,活动H5里面弄个弹幕留言,看起来就高大上有木有啊,以前的静态留言形势都太古板啦,弹幕才能用户high起来啊!好吧

弹幕效果

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>仿弹幕效果</title> <style> *{margin: 0;padding: 0;list-style: none;} html,body{width: 100%;height: 100%;} body{background-color:

Android 教你亲手打造酷炫的弹幕效果

公司的新产品上线需要添加的弹幕功能,于是花了一天时间写了一个Demo. 效果实现如下: 一开始的思路是: 1.首先实现一个自定义的Layout,在其中获得需要展示的弹幕数组,每个弹幕数组的项包括弹幕文本以及图片Url地址. 2.在Layout内部使用Handler或者计时线程循环发送弹幕. 3.弹幕实现采用自定义弹幕View,配合动画实现滚屏呈现. 总结之后发现主要的难点还是在弹幕的出现位置选择以及弹幕如何确保及时销毁上(我会说一开始调试的时候出现满屏弹幕的华丽场景么..),以及如何实现组件的复