精华内容
下载资源
问答
  • Barrage- 弹幕

    2017-11-08 22:05:30
    大家喜欢的弹幕!基于Qt开发的视频弹幕,类似于bilibili视频网站的那种,主题框架采用qt的动画机制模式
  • Web实时弹幕原理分析

    2021-03-24 08:51:24
    原理其他原理挺简单的,就是将大家的留言实时展示出来。注意点:实时性、动画效果、数据存储。实时性首先我们会想到,Ajax轮循 简单,粗暴。客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。客户端会...

    废话不多说,首先上效果图。

    效果图

    bVGo0P

    用途

    搞活动、年会的时候,在大屏幕上实时显示留言、吐槽。

    在视频网站上,将大家的吐槽实时展示出来。

    ...

    原理

    其他原理挺简单的,就是将大家的留言实时展示出来。

    注意点:实时性、动画效果、数据存储。

    实时性

    首先我们会想到,Ajax轮循 简单,粗暴。

    客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。

    客户端会轮询,判断有没有新消息。

    这种方式连接数会很多,一个接受,一个发送。

    而且每次发送请求都会有Http的Header,会很耗流量,也会消耗CPU的利用率。

    所以,这个方案是不可取的。

    可以通过长连接,socket.io 来实现。

    Socket.IO 支持 WebSocket 协议、用于实时通信和跨平台的框架。

    Socket.IO 设计的目标是构建能够在不同浏览器和移动设备上良好运行的实时应用。

    如实时分析系统、二进制流数据处理应用、在线聊天室、在线客服系统、评论系统、WebIM等。

    目前,Socket.IO 已经支持主流PC浏览器(IE、Safari、Chrome、Firefox、Opera等)。

    移动平台上的浏览器(iOS平台下的Safari、Android平台下的基于Webkit的浏览器等)。

    Socket.IO 实现了实时、双向、基于事件的通讯机制,它解决了实时的通信问题,并统一了服务端与客户端的编程方式。

    启动了Socket以后,就像建立了一条客户端与服务端的管道,两边可以信息互通。

    利用Socket.IO 与 Workerman 结合 ,双剑合璧。

    动画效果

    jquery.barrager.js

    Jquery.barrager.js 是一款优雅的网页弹幕插件,支持显示图片,文字以及超链接。

    支持速度、高度、颜色、数量等自定义

    大家可以浏览上面提供的Demo,根据自己的实际需求进行修改,优化。

    数据存储

    切记不要每次发送数据的时候实时插入的数据库中,可以利用异步处理。

    首先将数据存储到缓存中,异步将缓存的数据同步到数据库中。

    效果图的实现方法:

    Socket.IO + Workerman + jquery.barrager.js

    大家可以关注微信公众号,回复 “弹幕源码”,即可获取源码。

    推荐阅读

    一起学习

    展开全文
  • Barrage 弹幕实现原理

    千次阅读 2019-05-08 12:00:50
    给自己提出一个挑战。用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。 每个小程序都会给出详细的教程,保证每个新手都能看懂(不出意外的话每天都会更新) ...第七天、讲一下 弹幕 的实现原理。 ...

    给自己提出一个挑战。用纯 JavaScript 来写东西,不借助框架和库,也不使用编译器和引用。
    每个小程序都会给出详细的教程,保证每个新手都能看懂(不出意外的话每天都会更新)
    项目地址: cwyaml/JavaScript-Programs
    教程目录: JavaScript 每天一个小程序
    如果喜欢的话 请给个** star** 非常感谢!!

    第七天、讲一下 弹幕 的实现原理。

    说明:弹幕分两种:
    1、像优酷、爱奇艺等,记录用户发送弹幕时该视频播放的位置,其他人播放这个视频时到了这个点就显示弹幕。

    2、像斗鱼、熊猫这样的直播网站,用户发送弹幕直接显示在屏幕上,以后不需要再显示了。

    我们就来做第二种!!!

    实现效果

    项目分析

    1、获取用户输入信息;

    2、在页面中创建一个 <span></span> 来放获取到的文本,并添加一些样式(字体大小、颜色等);

    3、给这个 span 添加一个从右向左移动的动画;

    4、动画结束后,移除这个 <span>

    思路很清楚了,就来动手实现一下。。

    布局

    页面中要有一个输入框让用户输入信息,还要一个盒子用来显示弹幕。为了美观,我多加了一些东西。

    <div class="box" id="box">
      <div id="dm"></div>
      <div class="idDom" id="idDom">
        <div id="content">
          <p class="title">吐槽:</p>
          <input type="text" class="text" id="text" placeholder="请输入你想说的话~~"/>
          <button type="button" class="btn" id="btn">发射!</button>
        </div>
      </div>
    </div>
    

    1、#dm 是显示弹幕的区域,input 供用户输入信息(其中 placeholder 属性规定输入框中默认显示内容)。

    2、其它元素都是为了美观和布局。

    样式

    跟以前一样,先给出代码然后再讲解。

    html,body{
      margin: 0;
      padding: 0;
      font-size: 10px;
      overflow: hidden;
    }
    #box{
      width: 100%;
      height: 100%;
    }
    #dm{
      width: 100%;
      height: 90vh;
      background: #E8F1F5;
    }
    #dm span{
      width: auto;
      height: 3rem;
      font-size: 2rem;
      line-height: 2rem;
      position: absolute;
      white-space: nowrap;
    }
    #idDom{
      width: 100%;
      height: 10vh;
      background: #666;
      position: absolute;
      bottom: 0;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    #content{
      width: 50rem;
      height: 10vh;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .title{
      font-size: 2.2rem;
      color: #fff;
      line-height: #ccc
    }
    .text{
      width: 30rem;
      height: 2.5rem;
      border: none;
      border-radius: .5rem;
      font-size: 1.4rem;
      padding: 0 1rem;
      margin: 0 .5rem;    /*三个元素的间距*/
    }
    .btn{
      width: 6rem;
      height: 3rem;
      border: none;
      background: #f00;
      color: #fff;
    }
    

    1、首先还是格式化浏览器默认的 marginpaddingoverflow 属性设置超出屏幕的部分隐藏,这样就不会出现下拉和水平的滚动条了。

    2、#dm 用来显示弹幕,我们把它的高度设置为 90vhvh 是 CSS3 中新增的长度单位,表示相对于视口的高度。视口高度被均分为100单位的vh,90vh 就表示当前浏览器可视区域高度的 90%。(vw 就是视口宽度)

    感觉 CSS3 中新增的内容使用起来很顺手,很有必要了解一下

    3、#dm span 是每条弹幕的样式。你可以修改为你喜欢的样子,不过一定要设置 position 定位属性。

    4、然后就是下面的输入部分了。使用了 flex布局 (也是CSS3 中新增的内容,太方便了)。只需要 align-items:center;justify-content:center; 这两句就可以使其中的子元素在水平和垂直方向居中。所以 #idDom#content 都使用了。

    5、然后就是 输入框、按钮的样式了,没什么好说的,应该都能看懂。

    JS部分

    先看看代码吧。。

    var btn = document.getElementById('btn');
    btn.onclick = function(){
      addBarrage();
    }
    document.onkeydown = function(evt){
      var event = evt || window.event;
      if(event.keyCode == 13){
        addBarrage();
      }
    }
    var colors = ['#2C3E50','#FF0000','#1E87F0','#7AC84B','#FF7F00','#9B39F4','#FF69B4'];
    function addBarrage(){
      clearInterval(timer);
      var text = document.getElementById('text').value;  //获取用户输入的值
      document.getElementById('text').value = '';
      //parseInt(Math.random()*(n-m+1)+m);//生成一个从 m - n 之间的随机整数
      var index = parseInt(Math.random() * 7);  //生成一个0~6的随机数
      var screenW = window.innerWidth;
      var screenH = dm.offsetHeight;   
      var max = Math.floor(screenH / 40);
      var height = 10 + 40 * (parseInt(Math.random() * (max + 1)) - 1);
      var span = document.createElement('span');
      span.style.left = screenW +'px';
      span.style.top = height +'px';
      span.style.color = colors[index];
      span.innerHTML = text;
      var dmDom = document.getElementById('dm');
      dmDom.appendChild(span);
      timer = setInterval(move,10);
    }
    function move(){
      var arr= [];
      var oSpan = document.getElementsByTagName('span');
      for(var i = 0;i < oSpan.length;i++){
        arr.push(oSpan[i].offsetLeft);
        arr[i] -= 2
        oSpan[i].style.left = arr[i]+'px';
        if (arr[i] < -oSpan[i].offsetWidth) {
          var dmDom = document.getElementById('dm');
          dmDom.removeChild(dmDom.childNodes[0]);
        }
      }
    }
    

    1、说明:向页面添加弹幕使用 addBarrage() 函数,添加动画用 move() 函数。(把一种功能封装为一个函数是一个好习惯)

    2、首先思考一下弹幕的触发事件。应该有两个:点击“发射”按钮、按下 Enter 按键。所以分别监听点击 和 键盘事件。

    var btn = document.getElementById('btn');
    btn.onclick = function(){
      addBarrage();
    }
    document.onkeydown = function(evt){
      var event = evt || window.event;  //兼容IE
      if(event.keyCode == 13){
        addBarrage();
      }
    }
    

    3、然后就要思考怎么向页面添加弹幕了?

    • 先得到用户输入的信息
    var text = document.getElementById('text').value;
    
    • 然后在页面中创建一个 <span></span> ,把得到的文本放进去
    var span = document.createElement('span');
    span.innerHTML = text;
    
    • 添加到 #dm 这个盒子中:
    var dmDom = document.getElementById('dm');
    dmDom.appendChild(span);
    

    这样就可以了吗?? 当然不是的。。

    4、每条弹幕应该有不同的颜色,这样才炫酷。思路就是把预先的颜色放进一个数组,使用的时候用随机的下标,这样就获得了随机颜色。(借鉴斗鱼的 7 中颜色)

    var colors = ['#2C3E50','#FF0000','#1E87F0','#7AC84B','#FF7F00','#9B39F4','#FF69B4'];
    var index = parseInt(Math.random() * 7);  //生成一个0~6的随机数
    span.style.color = colors[index];
    

    这样每条弹幕就有不同的颜色了。

    生成一个从 m ~ n 之间的随机整数的公式
    parseInt(Math.random()*(n-m+1)+m);

    5、每条弹幕在页面上还要有不同的位置(高度),也就是不同的 top 值。我的想法是,虽然要有不同的 top ,但也不能太随意。就像下面这样:(一行是一行的)

    我的思路是:先判断页面可以放多少行?

    var screenH = dm.offsetHeight;
    var max = Math.floor(screenH / 40);
    

    然后计算可以有的 top 值:(加 10 是为了不至于紧挨着屏幕顶部,最后的 -1 是为了不至于太靠下 )

    var height = 10 + 40 * (parseInt(Math.random() * (max + 1)) - 1);
    

    你可以自己理解一下我的这种计算方式。。

    然后应用给 <span> 即可:

    span.style.top = height +'px';
    

    6、对了,弹幕应该添加到页面的什么位置呢?因为弹幕要从右往左移动,所以应该添加到屏幕的右侧,left 值为浏览器页面的宽度。这时候就知道为什么前面要设置 body 的 overflow: hidden; 了吧!!

    var screenW = window.innerWidth;
    span.style.left = screenW +'px';
    

    动画

    弹幕(也就是<span>)被添加到页面中了,我们要让他动起来。

    思路就是写一个函数,减少 <span> 的 left 值。每隔几毫秒执行一次这个函数,我们看起来这个 <span> 元素就动起来了。

    7、但是有几个问题:

    • 页面中有多少个弹幕(<span>)?
      因为页面中只有 弹幕 使用的是 <span> 标签,所以这样就可以获取所有 <span>nodeList(类似数组但不是数组,可以使用下标索引访问):
    var oSpan = document.getElementsByTagName('span');
    
    • 怎样记录每条弹幕的 left 值?
      获取到所有的 <span> 后,用一个 for 循环将每一个 <span> 的 left 值放进一个数组:
    var arr = [];
    for(var i = 0;i < oSpan.length;i++){
      arr.push(oSpan[i].offsetLeft);
    }
    

    这样 arr[] 就保存了所有弹幕的 left 值。

    8、接下来我们就逐个减少每条弹幕的 left 值:

    arr[i] -= 2
    oSpan[i].style.left = arr[i]+'px';
    

    9、最后判断如果弹幕已经移出了页面的左边,就把这条弹幕删除了吧。

    if (arr[i] < -oSpan[i].offsetWidth) {
      var dmDom = document.getElementById('dm');
      dmDom.removeChild(dmDom.childNodes[0]);
    }
    

    我们可以在开发工具中看一下这个过程:

    弹幕的 left 一直减小,移出页面后 #dm 中就没有这个 <span> 了。

    10、最后处理一个小细节吧!当我们点击按钮或按回车后,输入框中的文字会保留,影响我们下次输入,所以只要获取到了用户输入的内容,就把输入框清空吧!

    document.getElementById('text').value = '';
    

    到此,弹幕就实现了。。。

    文章中如果有错误欢迎指出,非常乐意和大家一起交流。

    完整代码可到 JavaScript-Programs 下载

    展开全文
  • Android - 弹幕实现原理(附Demo源码)

    千次阅读 2018-05-30 15:11:39
    二、弹幕原理的简单解析 1.我们先来做些准备工作。 (1)我们可能会需要一个视频(我在这里找了一个mp4格式的视频,并放在了res/raw目录下面,因为音频和视频文件放在其它目录(例如assets资源目录)下会导致无法...
    转载请注明出处:https://blog.csdn.net/mythmayor/article/details/80510449

    一、首先给不愿看博客的同学附上Demo源码的链接:

    点击此处下载安卓弹幕Demo

    二、弹幕原理的简单解析

    1.我们先来做些准备工作。

    (1)我们可能会需要一个视频(我在这里找了一个mp4格式的视频,并放在了res/raw目录下面,因为音频和视频文件放在其它目录(例如assets资源目录)下会导致无法使用,对这一部分有兴趣的同学可以去查资料了解一下)。
    这里写图片描述
    (2)需要将界面设置为横屏,并对它进行一些配置。下面是清单文件的代码。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.mythmayor.a1805danmudemo">
    
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:name=".MainActivity"
                android:configChanges="orientation|keyboardHidden|screenLayout|screenSize"
                android:screenOrientation="landscape">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    (3)我在这里用到了哔哩哔哩开源的弹幕效果库DanmakuFlameMaster(这个库的功能也比较强大,但本篇文章中可能只用到其基本功能,有兴趣的同学可以到其GitHub上进行学习)。需要配置到build.gradle的dependencies中。DanmakuFlameMaster库的项目主页地址是:https://github.com/Bilibili/DanmakuFlameMaster

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:26.0.0-alpha1'
        testCompile 'junit:junit:4.12'
        compile 'com.github.ctiao:DanmakuFlameMaster:0.9.25'
    }

    2.在观看直播或视频的时候,我们经常能看到弹幕的效果。首先我们从布局上讲一下,其实非常简单,布局最下层是播放器视图,中间那层一般则是弹幕视图层,最上层是操作界面的视图层。这样一说大家的心里是不是就有一个很清晰的结构了。那么下面就直接上布局的代码了。

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        tools:context="com.mythmayor.a1805danmudemo.MainActivity">
    
        <VideoView
            android:id="@+id/video_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerInParent="true" />
    
        <master.flame.danmaku.ui.widget.DanmakuView
            android:id="@+id/danmaku_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    
        <LinearLayout
            android:id="@+id/operation_layout"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:background="#fff"
            android:visibility="gone">
    
            <EditText
                android:id="@+id/edit_text"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1" />
    
            <Button
                android:id="@+id/send"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:text="Send" />
        </LinearLayout>
    </RelativeLayout>
    

    3.核心代码就要来了。在这里有几点是需要说明的。

    (1)首先播放视频的话这里用到的是VideoView,使用起来也非常简单,先要设置一个视频文件的路径:String uri = “android.resource://” + getPackageName() + “/” + R.raw.danmu;然后调用start方法即可播放视频了。
    (2)关于弹幕库的使用,可参考下面代码进行理解。我们需要创建一个DanmakuContext的实例和一个弹幕的解析器(这里直接创建了一个全局的BaseDanmakuParser),创建完成后就可以调用DanmakuView的prepare()方法了,调用这一方法后会自动调用回调函数中的prepared()方法,这个方法中调用了start方法,弹幕就此开始工作了。
    (3)需要在onPause()、onResume()、onDestroy()方法中执行一些操作,以保证DanmakuView的资源可以得到释放。
    (4)下面附上完整代码。

    package com.mythmayor.a1805danmudemo;
    
    import android.app.Activity;
    import android.graphics.Color;
    import android.os.Build;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.LinearLayout;
    import android.widget.VideoView;
    
    import java.util.Random;
    
    import master.flame.danmaku.controller.DrawHandler;
    import master.flame.danmaku.danmaku.model.BaseDanmaku;
    import master.flame.danmaku.danmaku.model.DanmakuTimer;
    import master.flame.danmaku.danmaku.model.IDanmakus;
    import master.flame.danmaku.danmaku.model.android.DanmakuContext;
    import master.flame.danmaku.danmaku.model.android.Danmakus;
    import master.flame.danmaku.danmaku.parser.BaseDanmakuParser;
    import master.flame.danmaku.ui.widget.DanmakuView;
    
    public class MainActivity extends Activity {
    
        private boolean showDanmaku;
        private DanmakuView danmakuView;
        private DanmakuContext danmakuContext;
        private BaseDanmakuParser parser = new BaseDanmakuParser() {
            @Override
            protected IDanmakus parse() {
                return new Danmakus();
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            VideoView videoview = (VideoView) findViewById(R.id.video_view);
            String uri = "android.resource://" + getPackageName() + "/" + R.raw.danmu;
            videoview.setVideoPath(uri);
            //videoview.setVideoURI(Uri.parse(uri));
            videoview.start();
            danmakuView = (DanmakuView) findViewById(R.id.danmaku_view);
            danmakuView.enableDanmakuDrawingCache(true);
            danmakuView.setCallback(new DrawHandler.Callback() {
                @Override
                public void prepared() {
                    showDanmaku = true;
                    danmakuView.start();
                    generateSomeDanmaku();
                }
    
                @Override
                public void updateTimer(DanmakuTimer timer) {
    
                }
    
                @Override
                public void danmakuShown(BaseDanmaku danmaku) {
    
                }
    
                @Override
                public void drawingFinished() {
    
                }
            });
            danmakuContext = DanmakuContext.create();
            danmakuView.prepare(parser, danmakuContext);
    
            final LinearLayout operationLayout = (LinearLayout) findViewById(R.id.operation_layout);
            Button send = (Button) findViewById(R.id.send);
            final EditText editText = (EditText) findViewById(R.id.edit_text);
            danmakuView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (operationLayout.getVisibility() == View.GONE) {
                        operationLayout.setVisibility(View.VISIBLE);
                    } else {
                        operationLayout.setVisibility(View.GONE);
                    }
                }
            });
            send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String content = editText.getText().toString();
                    if (!TextUtils.isEmpty(content)) {
                        addDanmaku(content, true, Color.GREEN);
                        editText.setText("");
                    }
                }
            });
            getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
                @Override
                public void onSystemUiVisibilityChange(int visibility) {
                    if (visibility == View.SYSTEM_UI_FLAG_VISIBLE) {
                        onWindowFocusChanged(true);
                    }
                }
            });
        }
    
        /**
         * 向弹幕View中添加一条弹幕
         *
         * @param content    弹幕的具体内容
         * @param withBorder 弹幕是否有边框
         */
        private void addDanmaku(String content, boolean withBorder) {
            BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
            danmaku.text = content;
            danmaku.padding = 5;
            danmaku.textSize = sp2px(20);
            danmaku.textColor = Color.WHITE;
            danmaku.setTime(danmakuView.getCurrentTime());
            if (withBorder) {
                danmaku.borderColor = Color.GREEN;
            }
            danmakuView.addDanmaku(danmaku);
        }
    
        /**
         * 弹幕View中添加一条弹幕
         *
         * @param content    弹幕的具体内容
         * @param withBorder 弹幕是否有边框
         * @param textColor  弹幕字体颜色
         */
        private void addDanmaku(String content, boolean withBorder, int textColor) {
            BaseDanmaku danmaku = danmakuContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
            danmaku.text = content;
            danmaku.padding = 5;
            danmaku.textSize = sp2px(20);
            danmaku.textColor = textColor;
            danmaku.setTime(danmakuView.getCurrentTime());
            if (withBorder) {
                danmaku.borderColor = Color.GREEN;
            }
            danmakuView.addDanmaku(danmaku);
        }
    
        /**
         * 随机生成一些弹幕内容以供测试
         */
        private void generateSomeDanmaku() {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (showDanmaku) {
                        int time = new Random().nextInt(300);
                        String content = "" + time + time;
                        addDanmaku(content, false);
                        try {
                            Thread.sleep(time);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    
        /**
         * sp转px的方法。
         */
        public int sp2px(float spValue) {
            final float fontScale = getResources().getDisplayMetrics().scaledDensity;
            return (int) (spValue * fontScale + 0.5f);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            if (danmakuView != null && danmakuView.isPrepared()) {
                danmakuView.pause();
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (danmakuView != null && danmakuView.isPrepared() && danmakuView.isPaused()) {
                danmakuView.resume();
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            showDanmaku = false;
            if (danmakuView != null) {
                danmakuView.release();
                danmakuView = null;
            }
        }
    
        /**
         * 沉浸式状态栏效果
         */
        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            if (hasFocus && Build.VERSION.SDK_INT >= 19) {
                View decorView = getWindow().getDecorView();
                decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
            }
        }
    }
    
    展开全文
  • 最近在做大作业的时候需要做一个弹幕播放器,今天小编给大家分享基于HTML使用canvas实现弹幕功能,需要的的朋友参考下吧
  • Android弹幕实现:基于B站弹幕开源系统(2)在附录1的基础上,模拟实现一种实际开发的应用场景:从网络中不间断的周期取弹幕数据,这些弹幕数据往往是批量的,然后把这些从网络中取到的批量数据逐个的显示出来。...

    ??

    Android弹幕实现:基于B站弹幕开源系统(2)

    在附录1的基础上,模拟实现一种实际开发的应用场景:从网络中不间断的周期取弹幕数据,这些弹幕数据往往是批量的,然后把这些从网络中取到的批量数据逐个的显示出来。注意本例中的Handler和线程安全队列ConcurrentLinkedQueue的使用。

    Java代码:

    package zhangphil.danmaku;

    import android.app.Activity;

    import android.graphics.Color;

    import android.os.Bundle;

    import android.os.Handler;

    import android.os.Message;

    import android.support.annotation.NonNull;

    import android.text.TextUtils;

    import android.util.Log;

    import android.view.View;

    import android.widget.Button;

    import java.util.ArrayList;

    import java.util.HashMap;

    import java.util.concurrent.ConcurrentLinkedQueue;

    import java.util.concurrent.ScheduledThreadPoolExecutor;

    import java.util.concurrent.TimeUnit;

    import master.flame.danmaku.danmaku.model.BaseDanmaku;

    import master.flame.danmaku.danmaku.model.DanmakuTimer;

    import master.flame.danmaku.danmaku.model.IDisplayer;

    import master.flame.danmaku.danmaku.model.android.DanmakuContext;

    import master.flame.danmaku.ui.widget.DanmakuView;

    public class MainActivity extends Activity {

    private DanmakuView mDanmakuView;

    private DanmakuContext mContext;

    private AcFunDanmakuParser mParser;

    private ScheduledThreadPoolExecutor mScheduledThreadPoolExecutor = null;

    private ConcurrentLinkedQueue mQueue = null;

    private final int WHAT_GET_LIST_DATA = 0xffa01;

    private final int WHAT_DISPLAY_SINGLE_DANMAKU = 0xffa02;

    private final int[] colors = {Color.RED, Color.YELLOW, Color.BLUE, Color.GREEN, Color.CYAN, Color.DKGRAY};

    private Handler handler = new Handler() {

    @Override

    public void handleMessage(Message msg) {

    super.handleMessage(msg);

    switch (msg.what) {

    case WHAT_GET_LIST_DATA:

    handler.removeMessages(WHAT_GET_LIST_DATA);

    ArrayList lists = (ArrayList) msg.obj;

    if (lists != null && !lists.isEmpty()) {

    mQueue.addAll(lists);

    if (!mQueue.isEmpty())

    handler.sendEmptyMessage(WHAT_DISPLAY_SINGLE_DANMAKU);

    }

    break;

    case WHAT_DISPLAY_SINGLE_DANMAKU:

    handler.removeMessages(WHAT_DISPLAY_SINGLE_DANMAKU);

    displayDanmaku();

    break;

    }

    }

    };

    private void displayDanmaku() {

    String s = mQueue.poll();

    if (!TextUtils.isEmpty(s)) {

    addDanmaku(s, true);

    }

    if (!mQueue.isEmpty())

    handler.sendEmptyMessageDelayed(WHAT_DISPLAY_SINGLE_DANMAKU, (long) (Math.random() * 400) + 100);

    }

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    mQueue = new ConcurrentLinkedQueue<>();

    mDanmakuView = (DanmakuView) findViewById(R.id.danmakuView);

    init();

    mScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);

    GetDanmakuMessageTask mTask = new GetDanmakuMessageTask();

    //延迟0秒执行,每隔若干秒周期执行一次任务

    mScheduledThreadPoolExecutor.scheduleAtFixedRate(mTask, 0, 5, TimeUnit.SECONDS);

    Button show = (Button) findViewById(R.id.show);

    Button hide = (Button) findViewById(R.id.hide);

    Button sendText = (Button) findViewById(R.id.sendText);

    Button pause = (Button) findViewById(R.id.pause);

    Button resume = (Button) findViewById(R.id.resume);

    Button clear = (Button) findViewById(R.id.clear);

    show.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

    mDanmakuView.show();

    }

    });

    hide.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

    mDanmakuView.hide();

    }

    });

    sendText.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

    //每点击一次按钮发送一条弹幕

    sendTextMessage();

    }

    });

    pause.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

    mDanmakuView.pause();

    }

    });

    resume.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

    mDanmakuView.resume();

    }

    });

    clear.setOnClickListener(new View.OnClickListener() {

    @Override

    public void onClick(View v) {

    clearDanmaku();

    }

    });

    }

    /**

    * 假设该线程任务模拟的就是从网络中取弹幕数据的耗时操作

    *

    */

    private class GetDanmakuMessageTask implements Runnable {

    @Override

    public void run() {

    try {

    Thread.sleep((long) (Math.random() * 1000));

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    ArrayList danmakuLists = new ArrayList<>();

    int count = (int) (Math.random() * 100);

    for (int i = 0; i < count; i++) {

    danmakuLists.add("批量数据" + i + " - " + Math.random());

    }

    if (!danmakuLists.isEmpty()) {

    Message msg = handler.obtainMessage();

    msg.what = WHAT_GET_LIST_DATA;

    msg.obj = danmakuLists;

    handler.sendMessage(msg);

    }

    }

    }

    private void clearDanmaku() {

    mQueue.clear();

    mDanmakuView.clearDanmakusOnScreen();

    }

    private void init() {

    mContext = DanmakuContext.create();

    // 设置最大显示行数

    HashMap maxLinesPair = new HashMap<>();

    maxLinesPair.put(BaseDanmaku.TYPE_SCROLL_RL, 8); // 滚动弹幕最大显示5行

    // 设置是否禁止重叠

    HashMap overlappingEnablePair = new HashMap<>();

    overlappingEnablePair.put(BaseDanmaku.TYPE_SCROLL_RL, true);

    overlappingEnablePair.put(BaseDanmaku.TYPE_FIX_TOP, true);

    mContext.setDanmakuStyle(IDisplayer.DANMAKU_STYLE_STROKEN, 10) //描边的厚度

    .setDuplicateMergingEnabled(false)

    .setScrollSpeedFactor(1.2f) //弹幕的速度。注意!此值越小,速度越快!值越大,速度越慢。// by phil

    .setScaleTextSize(1.2f) //缩放的值

    //.setCacheStuffer(new SpannedCacheStuffer(), mCacheStufferAdapter) // 图文混排使用SpannedCacheStuffer

    // .setCacheStuffer(new BackgroundCacheStuffer()) // 绘制背景使用BackgroundCacheStuffer

    .setMaximumLines(maxLinesPair)

    .preventOverlapping(overlappingEnablePair);

    mParser = new AcFunDanmakuParser();

    mDanmakuView.prepare(mParser, mContext);

    //mDanmakuView.showFPS(true);

    mDanmakuView.enableDanmakuDrawingCache(true);

    if (mDanmakuView != null) {

    mDanmakuView.setCallback(new master.flame.danmaku.controller.DrawHandler.Callback() {

    @Override

    public void updateTimer(DanmakuTimer timer) {

    }

    @Override

    public void drawingFinished() {

    }

    @Override

    public void danmakuShown(BaseDanmaku danmaku) {

    Log.d("弹幕文本", "danmakuShown text=" + danmaku.text);

    }

    @Override

    public void prepared() {

    mDanmakuView.start();

    }

    });

    }

    }

    private void sendTextMessage() {

    addDanmaku("zhangphil @ csdn : " + System.currentTimeMillis(), true);

    }

    private void addDanmaku(CharSequence txt, boolean islive) {

    BaseDanmaku danmaku = mContext.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);

    if (danmaku == null || mDanmakuView == null) {

    return;

    }

    danmaku.text = txt;

    danmaku.padding = 5;

    danmaku.priority = 0; // 可能会被各种过滤器过滤并隐藏显示

    danmaku.isLive = islive;

    danmaku.setTime(mDanmakuView.getCurrentTime() + 1200);

    danmaku.textSize = 20f * (mParser.getDisplayer().getDensity() - 0.6f); //文本弹幕字体大小

    danmaku.textColor = getRandomColor(); //文本的颜色

    danmaku.textShadowColor = getRandomColor(); //文本弹幕描边的颜色

    //danmaku.underlineColor = Color.DKGRAY; //文本弹幕下划线的颜色

    danmaku.borderColor = getRandomColor(); //边框的颜色

    mDanmakuView.addDanmaku(danmaku);

    }

    @Override

    protected void onPause() {

    super.onPause();

    if (mDanmakuView != null && mDanmakuView.isPrepared()) {

    mDanmakuView.pause();

    }

    }

    @Override

    protected void onResume() {

    super.onResume();

    if (mDanmakuView != null && mDanmakuView.isPrepared() && mDanmakuView.isPaused()) {

    mDanmakuView.resume();

    }

    }

    @Override

    protected void onDestroy() {

    super.onDestroy();

    if (mDanmakuView != null) {

    // dont forget release!

    mDanmakuView.release();

    mDanmakuView = null;

    }

    if (mScheduledThreadPoolExecutor != null)

    mScheduledThreadPoolExecutor.shutdown();

    }

    /**

    * 从一系列颜色中随机选择一种颜色

    *

    * @return

    */

    private int getRandomColor() {

    int i = ((int) (Math.random() * 10)) % colors.length;

    return colors[i];

    }

    }

    代码运行结果:

    b28f204b41398d81d091847e3e2fcd32.png

    原文:http://blog.csdn.net/zhangphil/article/details/68114226

    展开全文
  • bilibili弹幕定位

    2021-06-04 10:25:43
    // ==UserScript==// @name bilibili弹幕定位// @namespace http://tampermonkey.net/// @version 1.3// @description 在进度条上定位弹幕// @author mscststs// @match www.bilibili.com/video/*/...
  • 本文实例为大家分享了Android实现弹幕效果的具体代码,供大家参考,具体内容如下 首先分析一下,他是由三层布局来共同完成的,第一层视频布局,第二层字幕布局,第三层输入框布局,要想让这三个布局在同一页面上,...
  • C++实现弹幕

    2021-08-24 15:16:39
    #define WINVER 0x0501 #define _WIN32_WINNT 0x0501 #include <windows.h> #include <tchar.h> //全局变量 HINSTANCE hInst; UINT nDesktopWidth; UINT nDesktopHeight;... TCHA..
  • demo包括视频播放,点击屏幕输入文字,弹幕字体大小缩放,文字颜色设置以及弹出软键盘和屏幕的比例问题等
  • java桌面弹幕代码

    2018-01-27 23:14:48
    java实现桌面弹幕原理很简单,只有一个类就不发工程了。使用时要往你的工程导入rt.jar包。这个包不需要下载,在你的jdk/ lib目录下就有。直接copy到你的工程导入就行。
  • iOS弹幕(源码)实现原理解析

    千次阅读 2016-07-20 15:47:50
    弹幕,国内流行于视频网站A站和B站。网上关于弹幕的实现方法有很多,目前Android平台已经有比较成熟的解决方案...本文将介绍弹幕的大致实现原理。 看过DanmakuFlameMaster源码的朋友都知道,弹幕
  • 待项目成熟应进行弹幕框架封装; ... ... 前端的坑还会继续一个个地去踩; 在路上, 会一个一个把坑尽量看清楚些, 看透彻了, 为了下一次快掉进去之前, 不抽自己嘴巴子, 而是昂首跨过去, “来哇,互相伤害哇”!
  • 现在很多线上视频都有文字弹幕的效果,这里简单介绍一下如何实现
  • Android仿网络直播弹幕功能的实现

    千次阅读 2016-11-06 15:48:25
    首先来分析一下,这个弹幕功能是怎么实现的,首先在最下面肯定是一个游戏界面View,然后游戏界面上有弹幕View,弹幕的View必须要做成完全透明的,这样即使覆盖在游戏界面的上方也不会影响到游戏的正常观看,只有当有...
  • B站智能防挡弹幕的一种python实现

    千次阅读 2019-01-16 14:23:11
    某天代码写得老眼昏花,去B站上摸鱼,突然发现奇怪的现象:哟呵,B站竟然做了视频前景提取,把弹幕藏到画面人物的后面。识别效果还意外地不错呢。然后又翻了下,发现这是个叫做“智能防挡弹幕”的功能,我只在部分...
  • 来源:AI前线本文约2019字,建议阅读5分钟。本文为你揭秘B 站推出的一种“不挡脸”的黑科技弹幕以及背后的故事。[ 导读 ]不久前,B 站发布一条官方消息,为了更好的提...
  • bilibili 高并发实时弹幕系统的实现

    万次阅读 2017-03-30 16:57:16
    高并发实时弹幕是一种互动的体验。对于互动来说,考虑最多的地方就是:高稳定性、高可用性以及低延迟这三个方面。 高稳定性,为了保证互动的实时性,所以要求连接状态稳定; 高可用性,相当于提供一种备用...
  • 叉叉助手能实现从它的app打开另外的app,并实现弹幕,请问下这种弹幕效果是用的什么技术呢?
  • 感谢原理概念cid: 爬取弹幕需要的id号,可以由BV号通过API接口获得步骤BV转cid浏览器输入:https://api.bilibili.com/x/player/pagelist?bvid=BV1x54y1e7zf&jsonp=jsonpcid=226204073由cid得到当日条数小于1000的...
  • css3实现蒙版弹幕功能

    2020-12-13 22:54:12
    最近在b站上看到一种弹幕效果叫做智能防挡弹幕,也就是传说中的蒙版弹幕, 打开之后效果大概是这样的 再也不用担心男神女神的绝世容颜被花里胡哨的弹幕挡住啦,是不是感觉很神奇。 实现原理其实就是类似于ps的蒙版...
  • 看直播的童鞋们应该会经常看到满屏幕的滚动弹幕,看到密密麻麻的弹幕第一印象就是怎么样高效加载来避免卡顿,弹幕组成部分包含用户头像、用户昵称、弹幕的内容、表情等,本文介绍的实现原理就是把这几部分绘制成一张...
  • 安卓弹幕Demo

    2018-05-30 14:33:19
    安卓弹幕Demo,实现在Android手机上观看视频并查看弹幕,并支持发送弹幕
  • css3实现乞丐版的弹幕css3弹幕性能优化canvas实现弹幕canva弹幕的扩展功能1. css3实现乞丐版的弹幕(1)如何通过css3实现弹幕首先来看如何通过css的方法实现一个最简单的弹幕:首先在html中定义一条弹幕的dom结构:我...
  • 本篇文章通过原理分析和代码实例讲述了实现iOS视频直播弹幕功能的方法,需要的朋友参考学习下吧。
  • //将弹幕移动到屏幕外面,正好超出的位置 //拼写动画帧函数,记得每个ani要进行区分,宽度从自己的负宽度移动一整个屏幕的距离 var keyframes = `\ @keyframes ani${index}{ form{ right:${-width}px; } to{ right:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,507
精华内容 2,202
关键字:

弹幕原理