精华内容
下载资源
问答
  • Android弹幕功能实现,模仿斗鱼直播的弹幕效果

    万次阅读 多人点赞 2016-10-18 08:01:32
    记得之前有位朋友在我的公众号里问过我,像直播的那种弹幕功能该如何实现?如今直播行业确实是非常火爆啊,大大小小的公司都要涉足一下直播的领域,用斗鱼的话来讲,现在就是千播之战。而弹幕则无疑是直播功能当中...

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/51933728

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每天都有文章更新。

    大家好,感觉好像已经很久没更新博客了。前段时间主要是忙于新书的事情,时间比较紧张。而现在新书已经完稿,剩下的事情就都是出版社的工作了,那么我又可以抽出时间来写写博客了。

    记得之前有位朋友在我的公众号里问过我,像直播的那种弹幕功能该如何实现?如今直播行业确实是非常火爆啊,大大小小的公司都要涉足一下直播的领域,用斗鱼的话来讲,现在就是千播之战。而弹幕则无疑是直播功能当中最为重要的一个功能之一,那么今天,我就带着大家一起来实现一个简单的Android端弹幕效果。

    分析

    首先我们来看一下斗鱼上的弹幕效果,如下图所示:

    这是一个Dota2游戏直播的界面,我们可以看到,在游戏界面的上方有很多的弹幕,看直播的观众们就是在这里进行讨论的。

    那么这样的一个界面该如何实现呢?其实并不复杂,我们只需要首先在布局中放置一个显示游戏界面的View,然后在游戏界面的上方再覆盖一个显示弹幕的View就可以了。弹幕的View必须要做成完全透明的,这样即使覆盖在游戏界面的上方也不会影响到游戏的正常观看,只有当有人发弹幕消息时,再将消息绘制到弹幕的View上面就可以了。原理示意图如下所示:

    但是我们除了要能看到弹幕之外也要能发弹幕才行,因此还要再在弹幕的View上面再覆盖一个操作界面的View,然后我们就可以在操作界面上发弹幕、送礼物等。原理示意图如下所示:

    这样我们就把基本的实现原理分析完了,下面就让我们开始一步步实现吧。

    实现视频播放

    由于本篇文章的主题是实现弹幕效果,并不涉及直播的任何其他功能,因此这里我们就简单地使用VideoView播放一个本地视频来模拟最底层的游戏界面。

    首先使用Android Studio新建一个DanmuTest项目,然后修改activity_main.xml中的代码,如下所示:

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000">
    
        <VideoView
            android:id="@+id/video_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"/>
    
    </RelativeLayout>

    布局文件的代码非常简单,只有一个VideoView,我们将它设置为居中显示。
    然后修改MainActivity中的代码,如下所示:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            VideoView videoView = (VideoView) findViewById(R.id.video_view);
            videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/Pixels.mp4");
            videoView.start();
        }
    
    
        @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);
            }
        }
    
    }

    上面的代码中使用了VideoView的最基本用法。在onCreate()方法中获取到了VideoView的实例,给它设置了一个视频文件的地址,然后调用start()方法开始播放。当然,我事先已经在SD的根目录中准备了一个叫Pixels.mp4的视频文件。

    这里使用到了SD卡的功能,但是为了代码简单起见,我并没有加入运行时权限的处理,因此一定要记得将你的项目的targetSdkVersion指定成23以下。

    另外,为了让视频播放可以有最好的体验效果,这里使用了沉浸式模式的写法。对沉浸式模式还不理解的朋友可以参考我的上一篇文章 Android状态栏微技巧,带你真正理解沉浸式模式

    最后,我们在AndroidManifest.xml中将Activity设置为横屏显示并加入权限声明,如下所示:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.example.guolin.danmutest">
    
        <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:screenOrientation="landscape"
                      android:configChanges="orientation|keyboardHidden|screenLayout|screenSize">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

    OK,现在可以运行一下项目了,程序启动之后就会自动开始播放视频,效果如下图所示:

    这样我们就把第一步的功能实现了。

    实现弹幕效果

    接下来我们开始实现弹幕效果。弹幕其实也就是一个自定义的View,它的上面可以显示类似于跑马灯的文字效果。观众们发表的评论都会在弹幕上显示出来,但又会很快地移出屏幕,既可以起到互动的作用,同时又不会影响视频的正常观看。

    我们可以自己来编写这样的一个自定义View,当然也可以直接使用网上现成的开源项目。那么为了能够简单快速地实现弹幕效果,这里我就准备直接使用由哔哩哔哩开源的弹幕效果库DanmakuFlameMaster了。

    DanmakuFlameMaster库的项目主页地址是:https://github.com/Bilibili/DanmakuFlameMaster

    话说现在使用Android Studio来引入一些开源库真的非常方便,只需要在build.gradle文件里面添加开源库的依赖就可以了。那么我们修改app/build.gradle文件,并在dependencies闭包中添加如下依赖:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:24.2.1'
        testCompile 'junit:junit:4.12'
        compile 'com.github.ctiao:DanmakuFlameMaster:0.5.3'
    }

    这样我们就将DanmakuFlameMaster库引入到当前项目中了。然后修改activity_main.xml中的代码,如下所示:

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000">
    
        <VideoView
            android:id="@+id/video_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            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" />
    
    </RelativeLayout>

    可以看到,这里在RelativeLayout中加入了一个DanmakuView控件,这个控件就是用于显示弹幕信息的了。注意一定要将DanmakuView写在VideoView的下面,因为RelativeLayout中后添加的控件会被覆盖在上面。

    接下来修改MainActivity中的代码,我们在这里加入弹幕显示的逻辑,如下所示:

    public class MainActivity extends AppCompatActivity {
    
        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);
            videoView.setVideoPath(Environment.getExternalStorageDirectory() + "/Pixels.mp4");
            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);
        }
    
        /**
         * 向弹幕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);
        }
    
        /**
         * 随机生成一些弹幕内容以供测试
         */
        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 (InterruptedException 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;
            }
        }
    
        ......
    
    }

    可以看到,在onCreate()方法中我们先是获取到了DanmakuView控件的实例,然后调用了enableDanmakuDrawingCache()方法来提升绘制效率,又调用了setCallback()方法来设置回调函数。

    接着调用DanmakuContext.create()方法创建了一个DanmakuContext的实例,DanmakuContext可以用于对弹幕的各种全局配置进行设定,如设置字体、设置最大显示行数等。这里我们并没有什么特殊的要求,因此一切都保持默认。

    另外我们还需要创建一个弹幕的解析器才行,这里直接创建了一个全局的BaseDanmakuParser。

    有了DanmakuContext和BaseDanmakuParser,接下来我们就可以调用DanmakuView的prepare()方法来进行准备,准备完成后会自动调用刚才设置的回调函数中的prepared()方法,然后我们在这里再调用DanmakuView的start()方法,这样DanmakuView就可以开始正常工作了。

    虽说DanmakuView已经在正常工作了,但是屏幕上没有任何弹幕信息的话我们也看不出效果,因此我们还要增加一个添加弹幕消息的功能。

    观察addDanmaku()方法,这个方法就是用于向DanmakuView中添加一条弹幕消息的。其中首先调用了createDanmaku()方法来创建一个BaseDanmaku实例,TYPE_SCROLL_RL表示这是一条从右向左滚动的弹幕,然后我们就可以对弹幕的内容、字体大小、颜色、显示时间等各种细节进行配置了。注意addDanmaku()方法中有一个withBorder参数,这个参数用于指定弹幕消息是否带有边框,这样才好将自己发送的弹幕和别人发送的弹幕进行区分。

    这样我们就把最基本的弹幕功能就完成了,现在只需要当在接收到别人发送的弹幕消息时,调用addDanmaku()方法将这条弹幕添加到DanmakuView上就可以了。但接收别人发送来的消息又涉及到了即时通讯技术,显然这一篇文章中不可能将复杂的即时通讯技术也进行讲解,因此这里我专门写了一个generateSomeDanmaku()方法来随机生成一些弹幕消息,这样就可以模拟出和斗鱼类似的弹幕效果了。

    除此之外,我们还需要在onPause()、onResume()、onDestroy()方法中进行一些逻辑处理,以保证DanmakuView的资源可以得到释放。

    现在重新运行一下程序,效果如下图所示:

    这样我们就把第二步的功能也实现了。

    加入操作界面

    那么下面我们开始进行第三步功能实现,加入发送弹幕消息的操作界面。

    首先修改activity_main.xml中的代码,如下所示:

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000">
    
        ......
    
        <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>

    可以看到,这里我们加入了一个LinearLayout来作为操作界面。LinearLayout中并没有什么复杂的控件,只有一个EditText用于输入内容,一个Button用于发送弹幕。注意我们一开始是将LinearLayout隐藏的,因为不能让这个操作界面一直遮挡着VideoView,只有用户想要发弹幕的时候才应该将它显示出来。

    接下来修改MainActivity中的代码,在这里面加入发送弹幕的逻辑,如下所示:

    public class MainActivity extends AppCompatActivity {
    
        ......
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ......
            final  LinearLayout operationLayout = (LinearLayout) findViewById(R.id.operation_layout);
            final 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 view) {
                    if (operationLayout.getVisibility() == View.GONE) {
                        operationLayout.setVisibility(View.VISIBLE);
                    } else {
                        operationLayout.setVisibility(View.GONE);
                    }
                }
            });
            send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    String content = editText.getText().toString();
                    if (!TextUtils.isEmpty(content)) {
                        addDanmaku(content, true);
                        editText.setText("");
                    }
                }
            });
            getWindow().getDecorView().setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() {
                @Override
                public void onSystemUiVisibilityChange(int visibility) {
                    if (visibility == View.SYSTEM_UI_FLAG_VISIBLE) {
                        onWindowFocusChanged(true);
                    }
                }
            });
        }
        ......
    
    }

    这里的逻辑还是比较简单的,我们先是给DanmakuView设置了一个点击事件,当点击屏幕时就会触发这个点击事件。然后进行判断,如果操作界面是隐藏的就将它显示出来,如果操作界面是显示的就将它隐藏掉,这样就可以简单地通过点击屏幕来实现操作界面的隐藏和显示了。

    接下来我们又给发送按钮注册了一个点击事件,当点击发送时,获取EditText中的输入内容,然后调用addDanmaku()方法将这条消息添加到DanmakuView上。另外,这条弹幕是由我们自己发送的,因此addDanmaku()方法的第二个参数要传入true。

    最后,由于系统输入法弹出的时候会导致焦点丢失,从而退出沉浸式模式,因此这里还对系统全局的UI变化进行了监听,保证程序一直可以处于沉浸式模式。

    这样我们就将所有的代码都完成了,现在可以运行一下看看最终效果了。由于电影播放的同时进行GIF截图生成的文件太大了,无法上传,因此这里我是在电影暂停的情况进行操作的。效果如下图所示:

    可以看到,我们自己发送的弹幕是有一个绿色边框包围的,很容易和其他弹幕区分开。

    这样我们就把第三步的功能也实现了。


    虽说现在我们已经成功实现了非常不错的弹幕效果,但其实这只是DanmakuFlameMaster库提供的最基本的功能而已。哔哩哔哩提供的这个弹幕开源库中拥有极其丰富的功能,包含各种不同的弹幕样式、特效等等。不过本篇文章的主要目标是带大家了解弹幕效果实现的思路,并不是要对DanmakuFlameMaster这个库进行全面的解析。如果你对这个库非常感兴趣,可以到它的github主页上面去学习更多的用法。

    那么今天的文章到此结束。

    源码下载,请点击这里

    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • bilibili弹幕库图文弹幕实现
  • android 弹幕实现

    2016-10-22 10:09:54
    android 弹幕实现
  • unity弹幕效果实现

    2019-12-04 15:58:34
    unity弹幕效果实现 *弹幕会在范围内随机位置出现 *出现频率/间隔可控 *可以动态增加弹幕 ---------- *引用了DOTween插件
  • 那就是弹幕,现在也越来越多的人喜欢上了弹幕,甚至有人常说,‘正经人谁看视频啊,我是来看弹幕的????’,下面看下它的效果: 相信小伙伴们都看过了,那么它实现的原理是什么呢,那么我们前端怎么用我们web技术...

    随着b站的越做越强,出现了越来越多的仿照b站的视频站点。然而这些视频站仿照的最多的只有一点!那就是弹幕,现在也越来越多的人喜欢上了弹幕,甚至有人常说,‘正经人谁看视频啊,我是来看弹幕的🦉’,下面看下它的效果 :

    弹幕效果

    相信小伙伴们都看过了,那么它实现的原理是什么呢,那么我们前端怎么用我们web技术去实现呢??

    我们先新建一个html文件:

    哈哈哈,大家别像我一样用中文命名。中文命名是不合规范的,行走江湖,带佬们看见你的中文命名会笑话你的。

    上图中,我们引入了jquery插件,没错我们用jq写,回归原始(找不到cdn链接的小伙伴可以百度bootcdn,在里面搜索jquery)。并且取了个弹幕网的标题。

    建好html文件,我们要搞出初始模版来。在这里不得不提一句,我推荐前端开发的小伙伴们用vscode来开发,很好用的

    一个小技巧:在空白html文件输入!会自动帮你初始化html模板

     模板已经建立完毕,并且引入jquery后,正文开始了:

    HTML添加

    <body>
        <div class="con">
            <div id="screen">
                <div class="dm_show">
                    <!-- <div>text message</div> -->
                </div>
            </div>
            <div class="edit">
                <p class="msg">
                    <input placeholder="说点什么吧?" class="content" type="text" />
                </p>
                <p class="btns">
                    <input type="button" class="send" value="发送" />
                    <input type="button" class="clear" value="清屏" />
                </p>
            </div>
        </div>
    </body>

    一段朴实无华的html。

    再来写好css:

    CSS填充

    <style>
        .con {
            background-color: floralwhite;
            padding: 40px 80px 80px;
        }
    
        #screen {
            background-color: #fff;
            width: 100%;
            height: 380px;
            border: 1px solid rgb(229, 229, 229);
            font-size: 14px;
        }
    
        .content {
            border: 1px solid rgb(204, 204, 204);
            border-radius: 3px;
            width: 320px;
            height: 35px;
            font-size: 14px;
            margin-top: 30px;
        }
    
        .send {
            border: 1px solid rgb(230, 80, 30);
            color: rgb(230, 80, 0);
            border-radius: 3px;
            text-align: center;
            padding: 0;
            height: 35px;
            line-height: 35px;
            font-size: 14px;
            width: 159px;
            background-color: white;
        }
    
        .clear {
            border: 1px solid;
            color: darkgray;
            border-radius: 3px;
            text-align: center;
            padding: 0;
            height: 35px;
            line-height: 35px;
            font-size: 14px;
            width: 159px;
            background-color: white;
        }
    
        .edit {
            margin: 20px;
            text-align: center;
        }
    
        .edit .btns{
            margin-top: 20px;
        }
    
        .edit .msg input{
            padding-left: 5px;
        }
    
        .text {
            position: absolute;
        }
    
        * {
            margin: 0;
            padding: 0;
        }
    
        .dm_show {
            margin: 30px;
        }
    </style>

    看看效果:

     整体结构已经出来了,下面就是真二八经的js:

    js逻辑代码

    <script>
        $(function () {
            //设置“发送”按钮点击事件,将弹幕体显示在弹幕墙上
            $('.send').click(function () {
                //获取文本输入框的内容
                var val = $('.content').val();
                //将文本框的内容赋值给val后,将文本框的内容清除,以便用户下一次输入
                $('.content').val('');
                //将文本框内容用div包裹起来,便于后续处理
                var $content = $('<div class="text">' + val + '</div>');
                //获取弹幕墙对象
                $screen = $(document.getElementById("screen"));
                //设置弹幕体出现时的上边距,为任意值
                var top = Math.random() * $screen.height() + 40;
                //设置弹幕体的上边距和左边距
                $content.css({
                    top: top + "px",
                    left: 80
                });
                //将弹幕体添加到弹幕墙中
                $('.dm_show').append($content);
                //弹幕体从左端移动到最右侧,时间为8秒,然后直接删除该元素
                $content.animate({
                    left: $screen.width() + 80 - $content.width()
                }, 8000, function () {
                    $(this).remove();
                });
            });
            //设置“清屏”点击事件,清除弹幕墙中的所有内容
            $('.clear').click(function () {
                $('.dm_show').empty();
            });
        });
    </script>

    意外吗?就这么几行😓,哈哈哈。注释里写的很详细,小伙伴们可以好好看来看,这里我给大家演示演示:

    动画效果

    请原谅我这糟糕的画质,还好不影响看效果,这里只是简单的实现了一个弹幕的效果,还不能够达到企业级的应用,有需求的也可以自行完善,道理就是这么个道理,嘿嘿。

    如果企业级视频弹幕的话,这里也推荐dplayer播放器,一个非常完美的播放器。

     以上关于前端弹幕的讨论就差不多到此了,如果有要和我吹水的,请看我的主页。

    展开全文
  • 作者panghaijiao,源码HJDanmakuDemo,iOS系统上弹幕源码实现,弹幕,国内...看过DanmakuFlameMaster源码的朋友都知道,弹幕实现主要需要解决以下几个问题 弹幕绘制方式 弹幕时间控制 弹幕碰撞检测原理 弹幕暂停及恢复
  • 简易弹幕效果实现

    2015-08-14 15:12:44
    简易弹幕效果实现,对应博文:http://blog.csdn.net/books1958/article/details/46375591
  • 自定义弹幕实现实现思路实现功能截图:上代码 实现思路 在视频view的上层ViewGroup添加TextView并使其滚动 实现功能 弹幕能够实现在屏幕上滚动,可以指定滚动的方向 弹幕的文字大小颜色样式等可以自定义 可以实现...

    实现思路

    在视频view的上层ViewGroup添加TextView并使其滚动

    实现功能

    1. 弹幕能够实现在屏幕上滚动,可以指定滚动的方向
    2. 弹幕的文字大小颜色样式等可以自定义
    3. 可以实现不同样式的弹幕,如静止的,滚动的,带图片的
    4. 动画效果流畅,资源占用少(弹幕view循环复用)。
    5. 实现弹幕层级控制。

    截图:

    在这里插入图片描述

    上代码

    https://gitee.com/gentlemanyc/DanmuDemo.git

    展开全文
  • 弹幕实现方式

    2018-03-26 20:39:01
    实现最基本的弹幕 脚手架~~使用的是springboot, maven编译
  • 手机弹幕实现

    2015-09-10 22:40:16
    继承NotificationListenerService,利用浮动窗口和自写控件实现手机弹幕功能,特别注意NotificationListenerService是在android 4.3以后添加的服务,4.3以前会崩溃。
  • 弹幕效果实现

    千次阅读 2016-08-04 19:08:45
    呜呼哀哉,临睡前看了一个弹幕的效果实现的技术视频,听着很好的,自己在做电商这块也没写过弹幕。今天来到公司趁着闲暇按照人家说的思路写了一下,有用得上的可以看看。 demo地址正文先来看一下最终实现的效果: ...

    前言

    昨天晚上11点了还没睡着,惆怅能力不足,但不知道怎么能快速的提升自己。呜呼哀哉,临睡前看了一个弹幕的效果实现的技术视频,听着很好的,自己在做电商这块也没写过弹幕。今天来到公司趁着闲暇按照人家说的思路写了一下,有用得上的可以看看。
    demo地址

    正文

    先来看一下最终实现的效果:

    运行结果
    在这里,我只说一下重要的思想和代码块部分,关于其他的不再细说。代码很简单,基本上跑一下在瞅瞅就理解的差不多了。

    思想

    弹幕说白了就是移动的label.创建一个view里装着label用于做移动使用。view,label的宽度根据字体的多少去改变。然后开始移动,改变view的x值的大小。当view完全移除屏幕的时候这条弹幕完成,把view移除,开始下一条。

    核心代码

    装着label的view

    首先构造方法不能少的

    //创建
    - (instancetype)initWithComment:(NSString *)comment{
    
        self = [super init];
        if (self) {
            //创建label
            self.commentLabel.text = comment;
            NSDictionary *attr = @{NSFontAttributeName:[UIFont systemFontOfSize:14]};
            CGFloat width = [comment sizeWithAttributes:attr].width;//求出字体的宽度
            self.bounds = CGRectMake(0, 0, width + 2 * padding, 30);
            self.commentLabel.frame = CGRectMake(padding, 0, width, 30);
        }
        return self;
    }

    构造里面有label的frame确定,那我们内存空间一定要有label才可以,这里才用懒加载的方式创建label

    //创建label
    - (UILabel *)commentLabel{
    
        if (!_commentLabel) {
            _commentLabel = [[UILabel alloc]initWithFrame:self.bounds];
            _commentLabel.backgroundColor = [UIColor redColor];
            _commentLabel.textColor = [UIColor whiteColor];
            _commentLabel.font = [UIFont systemFontOfSize:14];
            _commentLabel.textAlignment = NSTextAlignmentCenter;
            _commentLabel.layer.cornerRadius = 5;
            _commentLabel.layer.masksToBounds = YES;
            [self addSubview:_commentLabel];
        }
        return _commentLabel;
    }

    然后开始弹幕动画的实现

    #pragma mark -------开始结束方法
    //开始方法
    - (void)startAnimation{
    
        //给出uilabel的位置大小
        CGFloat screen = [UIScreen mainScreen].bounds.size.width;
        CGFloat duration = 3.0f;
        CGFloat wholeWidth = screen + CGRectGetWidth(self.bounds);
    
        //开始
        if (self.successShowComment) {
            self.successShowComment(syf_start);
        }
    
        //运行时间
        CGFloat speed = wholeWidth / 3.0f;
        CGFloat enterDurtion = CGRectGetWidth(self.bounds) / speed;
        [self performSelector:@selector(p_enterScreen) withObject:self afterDelay:enterDurtion];
    
        __block CGRect frame = self.frame;
        [UIView animateWithDuration:duration animations:^{
    
            frame.origin.x -= wholeWidth;
            self.frame = frame;
        } completion:^(BOOL finished) {
            [self removeFromSuperview];
    
            if (self.successShowComment) {
                self.successShowComment(syf_end);
            }
        }];
    }
    //结束
    - (void)stopAnimation{
    
        [NSObject cancelPreviousPerformRequestsWithTarget:self];
        [self.layer removeAllAnimations];
        [self removeFromSuperview];
    }
    
    #pragma mark -------私有方法
    //view尾部也进入屏幕后调用
    - (void)p_enterScreen{
    
        if (self.successShowComment) {
            self.successShowComment(syf_enter);
        }
    }

    为了逻辑清晰,以后代码好维护。我们才用了一个manager类区管理弹幕的方法实现,而不直接去vc里面写。
    首先创建三个数组,分别去存弹幕view,原始数据,和数据处理数组

    @property(nonatomic,strong)NSMutableArray *commentArray;//数据数组
    @property(nonatomic,strong)NSMutableArray *barrageViews;//弹幕数组
    @property(nonatomic,strong)NSMutableArray *barrageComment;//处理数组
    @property(nonatomic,assign)BOOL stopAnimation;//判断开始还是停止状态

    然后就是管理弹幕开始移动和停止的方法

    #pragma mark -----开始停止方法
    //开始
    - (void)start{
    
        if (!self.stopAnimation) {
            return;
        }
        self.stopAnimation = NO;
    
        //数组数据全部移除
        [self.barrageComment removeAllObjects];
        [self.barrageComment addObjectsFromArray:self.commentArray];
        [self p_initBarrageComment];
    
    
    }
    //停止
    - (void)stop{
        if (self.stopAnimation) {
            return;
        }
        self.stopAnimation = YES;
        if (self.barrageViews.count > 0) {
    
            [self.barrageViews enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                SYFBarrageView *view = obj;
                [view stopAnimation];
                view = nil;
            }];
    
            [self.barrageViews removeAllObjects];
        }
    
    }

    底下的代码块是核心的操作代码:
    基本思路就是看你需要创建几个同时移动的弹幕,我这里选择了5个,然后随机选择每个弹道去放弹幕,当一个弹幕的尾部也出现到屏幕时,就检查是否还有没有显示的弹幕内容,如果有,在这个弹道显示没有显示的弹幕内容。

    #pragma mark ------私有方法
    - (void)p_initBarrageComment{
    
        NSMutableArray *tempArray = [NSMutableArray arrayWithObjects:@(0),@(1),@(2),@(3),@(4), nil];
        for(int i = 0;i < 5;i ++){
    
            if (self.barrageComment.count > 0 ) {
    
                //随机数
                NSInteger index = arc4random() % tempArray.count;
                int temp = [[tempArray objectAtIndex:index] intValue];
                [tempArray removeObjectAtIndex:index];
    
                //从弹幕数组逐一取出数据
                NSString *comment = [self.barrageComment firstObject];
                [self.barrageComment removeObjectAtIndex:0];
    
                //创建view
                [self p_createViewBarrageView:comment temp:temp];
            }
    
        }
    }
    //创建弹幕
    - (void)p_createViewBarrageView:(NSString *)comment temp:(int)tempIndex{
    
        if (self.stopAnimation) {
            return;
        }
        SYFBarrageView *barrageView = [[SYFBarrageView alloc]initWithComment:comment];
        barrageView.tempIndex = tempIndex;
    
    
        __block typeof(self)weakSelf =self;
        __block typeof(SYFBarrageView *)weakView = barrageView;
        //移除屏幕
        barrageView.successShowComment = ^(NSInteger status){
    
            if (weakSelf.stopAnimation) {//防止在停止时被调用
                return ;
            }
            switch (status) {
                case syf_start:{
                    //弹幕进入
                    [weakSelf.barrageViews addObject:weakView];
                }
                    break;
                case syf_enter:{
                    //是否还有其他内容有,在该轨迹创建一个
                    NSString *nextComment = [weakSelf p_nextComment];
                    if (nextComment) {
                        [weakSelf p_createViewBarrageView:nextComment temp:tempIndex];
                    }
                }
                    break;
                case syf_end:
                {
                    //弹幕飞出从数组删除
                    if ([weakSelf.barrageViews containsObject:weakView]) {
                        [weakView stopAnimation];
                        [weakSelf.barrageViews removeObject:weakView];
                    }
    
                    if (weakSelf.barrageViews.count == 0) {//没有数据源了
                        weakSelf.stopAnimation = YES;
                        [weakSelf start];
    
                    }
                }
    
                    break;
    
                default:
                    break;
            }
    
        };
    
    
        if (self.generateViewBlock) {
            self.generateViewBlock(barrageView);
        }
        [barrageView startAnimation];
    }
    //是否还有弹幕
    - (NSString *)p_nextComment{
    
        if (self.barrageComment.count == 0) {
            return  nil;
        }
        NSString *comment = [self.barrageComment firstObject];
    
        if (comment) {
            [self.barrageComment removeObjectAtIndex:0];
        }
        return comment;
    }

    结语

    好了,感觉还是看源码好理解,有问题的可以联系我。有能力的感觉对你有点用的就给个星星,谢了。

    展开全文
  • 这篇文章主要介绍了关于实现弹幕效果的方法总结(css和canvas) ,有着一定的参考价值,现在分享给大家,有需要的朋友可以参考一下之前在一个移动端的抽奖页面中,在抽奖结果的展示窗口需要弹幕轮播显示,之前踩过一些...
  • unity弹幕功能实现

    千次阅读 2016-11-17 16:03:32
    实际的项目中肯定是用服务器客户端直接的数据来控制的,这里只在客户端进行测试实现弹幕核心功能。 下面说的就是我是如何实现弹幕功能的。 首先是需求:要求全屏有四行弹幕,从下至上排列,每行依次显示,弹幕从右...
  • 游戏弹幕系统实现

    2020-08-19 16:15:34
    现在的公司在做一个VR影院的项目,需要实现弹幕聊天功能。在借鉴其它博主的实现思路上,做了一些改编,希望能为大家提供参考。 using UnityEngine; using System.Collections; using UnityEngine.UI; using DG....
  • 弹幕实现

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

    万次阅读 热门讨论 2016-02-25 15:08:42
    弹幕看起来很有意思,今天我们就来实现一个简单的弹幕效果。  从直观上,弹幕效果就是在一个ViewGroup上增加一些View,然后让这些View移动起来。所以,整体的实现思路大概是这样的: 1、定义一个RelativeLayout...
  • ios-弹幕实现.zip

    2019-07-11 19:56:43
    弹幕实现
  • JS弹幕实现

    2017-10-23 23:36:51
    基于直播平台的流行,利用原生实现一下弹幕效果。 原文链接实现原理1、设置展示弹幕元素位置属性为relative 2、动态创建弹幕元素,位置属性设置absolute,left为展示宽度 3、随机设置弹幕元素top值 4、随机...
  • 弹幕简单实现

    2017-01-14 21:46:40
    发现创建弹幕是出现一条弹幕就创建一条,结果来了一堆,就挂了,后面找了好多都是一条弹幕就创建一条弹幕,这样都会有内存溢出。于是,想起动画不是可以重复的吗,我把重复次数改成Integer.MAX_VALUE,外面用索引拿...
  • 简易弹幕功能实现

    2020-06-05 16:03:42
    实现简单的弹幕功能 move() { //弹幕div所在位置减去屏幕左上角位置,得left参数:弹幕div离最左边的位置 let left = this.Dom.offset().left - $(".screen").offset().left; //随机生成弹幕移动速度 this.speed...
  • JS实现弹幕

    千次阅读 2018-12-29 11:01:18
    JS弹幕实现
  • 主要介绍了Html5移动端弹幕动画实现示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 转载请注明出处 ...原文链接:http://blog.csdn.net/u013200308/article/details/53559707现在各大视屏网站都有了弹幕功能,但显示效果最好的非b站...如果你也想拥有和b站一样炫酷的弹幕效果,那么就跟着我来一步步实现
  • 实现弹幕 js

    2018-03-26 11:08:27
    可以自己修改实现弹幕,前端的一些知识,又兴趣的可以下看
  • 弹幕(barrage),顾名思义是指子弹多而形成的幕布,大量吐槽评论从屏幕飘过时效果看上去像是飞行射击游戏里的弹幕。——来自百度百科 弹幕的特点 1、弹幕可以循环播放 2、弹幕的速度不同 3、弹幕是随机出现的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,418
精华内容 6,967
关键字:

弹幕是怎么实现的