EDG牛逼!我搞了一很骚的弹幕页面

·  阅读 2977
EDG牛逼!我搞了一很骚的弹幕页面

我本身是一个十年的英雄联盟玩家,EDG夺冠当天兴奋不已,好像国足进世界杯那般。同时陈奕迅的《孤勇者》配合上我想吹爆的《双城之战》最近也上映了,燃起了我这网瘾青年久违的激情。
我考虑决定做一个主题弹幕网页,纪念这一次来之不易的冠军,业余时间熬夜做完并把代码开源。

image.png


网页入口:www.lhqcloud.top/EDG_NB/page…

Github: github.com/doterlin/ED…

兼容性:现代浏览器,暂未适配移动设备,请在PC浏览。

技术选型:前端基本都是原生CSS3 + ES5编写,后端是node + socket.io + mysql

有同学恶意刷弹幕...一天多了十几万条数据,我在11月15日会清空数据。

运行与部署

安装依赖:

npm install
复制代码

建数据库edg_nb, 导入service/bullet.sql建好表。

pages/index.html部署到静态目录或者本地直接打开到浏览器运行, 然后运行node服务:

node ./service/app.js
复制代码

websocket进程比较脆弱,你可以使用pm2守护:

pm2 start ./service/app.js
复制代码

前端

前端的动画效果自己设计水平有限,氛围感不是很强,但全是原生写,不依赖第三方库。为了增加更多趣味,结合EDG的BO5英雄技能设计了弹幕的动画效果,在构想效果上就绞尽脑汁,代码编写反倒觉得舒服点。

背景动画

背景动画类的实现如下,需要配合css,样式部分可在static/style.css找到对应的类查看。

 // Bg类,画背景
    // @param parentElement 画背景的节点
    function Bg(parentElement, randomTextArray) {
        this.parentElement = parentElement;
        this.randomTextArray = randomTextArray;
        this.parentW = parentElement.clientWidth,
            this.parentH = parentElement.clientHeight;

        this.rowWidth = 40;
        this.rowMargin = 50;

        this.rowClassName = 'bg-row';

    }
    
    // 开始画
    Bg.prototype.draw = function () {
        this.drawRow();
    }


    Bg.prototype.drawRow = function () {
        // parent节点能画多少列
        var rowNumber = Math.ceil(this.parentW / (this.rowWidth + this.rowMargin));

        for (let i = 0; i < rowNumber; i++) {
            var el = document.createElement('div');
            el.setAttribute('class', 'bg-row');
            this.parentElement.append(el);

            new ScrollText(el, this.randomTextArray);
        }

    }

    Bg.prototype.destroy = function () {
        this.parentElement.innerHTML = '';
    }
复制代码

背景文字类:

// 滚动文字类
    function ScrollText(rowElement, textArray) {
        this.rowElement = rowElement;
        this.textArray = textArray;
        this.aniTime = 10000; //文字动画滚动时长

        this.createRomdomTextEl();
    }

    ScrollText.prototype.createRomdomTextEl = function () {
        var text = this.textArray[Math.floor((Math.random() * this.textArray.length))].toUpperCase(); //随机取出一个
        var p = document.createElement('p');
        p.setAttribute('class', 'bg-text');
        p.innerHTML = text;
        var ts = this;
        p.className = 'bg-text slideDown';
        this.rowElement.append(p);

        setTimeout(function () {
            ts.rowElement.removeChild(p)
        }, this.aniTime);

        setTimeout(() => {
            ts.createRomdomTextEl();
        }, (Math.random() * 1 + 2.2) * 1000);

    }
复制代码

弹幕

弹幕功能由动画效果和websocket连接交互构成。动画核心代码:

 // 弹幕类
    // @param text 弹幕文字
    // @param aniType 弹幕动画类型
    function Bullet({ text, aniType, isCustomer }, parentElement) {
        this.text = text;
        this.aniType = aniType;
        this.isCustomer = isCustomer;
        this.parentElement = parentElement;

        this.el = null;
        this.aniTime = aniType === '1' ? 4000 : 10000; //弹幕留存时间

        this.create()
    }

    Bullet.prototype.create = function () {
        this.el = document.createElement('div');
        var innerHTML = this.text;

        if (this.isCustomer) {
            innerHTML += '<span class="vip-tag"> 来自尊贵的程序员</span>';
        }
        if (this.aniType === '6') {
            innerHTML = '<img class="img-0-21-0" src="../static/imgs/0-21-0.webp"/><div>' + innerHTML + '</div>'
        }

        this.el.innerHTML = innerHTML;
        this.el.setAttribute('class', 'bullet-text bullet-ani' + this.aniType);

        //初始高度随机
        this.el.setAttribute('style', 'top: ' + Math.random() * 90 + '%;');
    }

    Bullet.prototype.go = function () {
        var ts = this;
        ts.parentElement.append(ts.el);
        setTimeout(function () {
            ts.parentElement.removeChild(ts.el)
        }, this.aniTime)
    }

复制代码

websocker连接与处理:

 // websocket
    function createSocketIo() {

        var socket = io('localhost:3300', { transports: ['websocket'] });
        // 监听连接
        socket.on('connect', function () {
            console.log('%c connect success.', 'color: #690');
        });

        //监听计数 
        socket.on('count', function (data) {
            countText1.innerHTML = data.C1;
            countText2.innerHTML = data.C2;
            
        });

        // 监听数据
        socket.on('add', function (data) {
            new Bullet(data, parentEl).go(true)
        });

        return socket;
    }

复制代码

后端

后端要实现的功能很简单,一是建立websocket连接和推送消息,二是在数据库记录和查询用户的弹幕数。 主要代码:

const db = require("./db")
let http = require("http");
let fs = require("fs");

let server = http.createServer((req, res) => {
    res.end('Nothing!')
})

server.listen(3300, () => {
    console.log("http服务已启动");
})

var io = require('socket.io')(server);
// 连接
io.on('connection', function (socket) {
    getCount(res => {
        socket.emit('count', res)
    })
    // 监听
    socket.on('add', function (data, a2, a3) {
        const _data = {
            ...data,
            isCustomer: isCustomer(data.text)
        }
        socket.emit('add', _data);
        addRecord(_data, socket);

        getCount(res => {
            socket.emit('count', res)
        })
    });
});
复制代码

其中db.js是封装的mysql查询模块。

一个彩蛋

项目中“藏”了一个小彩蛋给身为召唤师的程序员朋友们,我相信你们很快就能找到这个彩蛋。 如果真需要触发彩蛋的提示,可以在console控制台查看(但我相信大部分看到这篇文章的朋友都不需要提示)。

欢迎大家贡献弹幕,如果你喜欢这个网页欢迎你分享给其他召唤师。后面到一定时间我会出一个统计结果,有问题随时交流。

断剑重铸之日,其势归来之时! 加油,召唤师。

分类:
前端
标签:
分类:
前端
标签: