soket实现实时弹幕

2022/10/19

# 理论

  • 主屏幕WS长连接
  • 多个弹幕发射器触发ws发射请求,server利用ws长连接向主屏幕推送消息

# 服务器

const express = require('express');
const app = new express();
require("express-ws")(app);
app.listen(8888, () => {
    console.log('listen 8888');
});

let ws;
app.ws("/", (wsInstance, req) => {
    ws = wsInstance;
    ws.on('message', function (msg) {
        console.log(msg);
        ws.send(msg);
    });
})
app.get("/msg", (req, res) => {
    const { m } = req.query;
    if (!ws || !m) return res.send({ "done": false })
    ws.send(m);
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 屏幕

<body>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            width: 100vw;
            height: 100vh;
            overflow: hidden;
            background-color: #000000;
            color: #fff;
        }

        .dynamic {
            position: fixed;
            left: 100vw;
            white-space: nowrap;
            animation: moveToLeft 5s linear forwards;
        }

        @keyframes moveToLeft {
            to {
                left: -10vw;
            }
        }
    </style>
    <script>
        const body = document.body;

        const goToFullScreen = (element = document.body) => {
            console.log(element);
            if (element.requestFullscreen) {
                element.requestFullscreen();
            } else if (element.mozRequestFullScreen) {
                element.mozRequestFullScreen();
            } else if (element.msRequestFullscreen) {
                element.msRequestFullscreen();
            } else if (element.webkitRequestFullscreen) {
                element.webkitRequestFullScreen();
            }
        };

        body.addEventListener("click", () => goToFullScreen());


        let socket = new WebSocket("ws://localhost:8888/");
        socket.onopen = function () {
            console.log('用websocket与服务器建立连接...');
        };

        socket.onmessage = function (e) {
            const msg = JSON.parse(e.data);
            if (msg?.text) {
                dynamicText(msg.text);
            }
        };

        setTimeout(() => {
            const msg = JSON.stringify({ text: 'ws连接成功' });
            socket.send(msg);
        }, 2000);

        const dynamicText = (text) => {
            const span = document.createElement("span");
            span.classList.add("dynamic");
            span.style.top = `${Math.random() * 80 + 10}vh`;
            span.style.animationDuration = `${Math.random() * 5 + 5}s`;
            span.innerText = text;
            document.body.appendChild(span);
            setTimeout(() => {
                document.body.removeChild(span);
            }, 5000);
        };
    </script>
</body>
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
71
72
73
74
75
76
77

# 弹幕发射器

<body>
    <input type="text" class="ipt">
    <button class="submit">发送</button>
    <script>
        const ipt = document.querySelector(".ipt");
        const submit = document.querySelector(".submit");
        const wsUrl = "http://localhost:8888/msg?m={msg}";

        submit.addEventListener("click", () => {
            const val = ipt.value;
            const msg = JSON.stringify({ text: val });
            fetch(wsUrl.replace("{msg}", msg)).catch(e => { });
        })
    </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 效果预览

上次更新: 6/13/2025