【Azure Developer】App Service + PubSub +JS 實現多人版黑客帝國文字流效果圖

路邊兩盞燈發表於2022-05-28

需要描述

1)實現黑客帝國文字流效果圖,JS功能

2)部署在雲中,讓大家都可以訪問,App Service實現

3)大家都能傳送訊息,並顯示在文字流中,PubSub(websocket)實現

 

終極效果顯示:

 

執行步驟

1)在 Azure 中建立 App Service 服務,參考官方文件:快速入門:部署 ASP.NET Web 應用

Azure門戶中建立App Service的動畫演示:

【Azure Developer】App Service + PubSub +JS 實現多人版黑客帝國文字流效果圖

 

2)在 Azure 中建立 PubSub 服務,參考官方文件:快速入門:從 Azure 門戶建立 Web PubSub 例項

Azure門戶中建立Web PubSub的動畫演示:

 【Azure Developer】App Service + PubSub +JS 實現多人版黑客帝國文字流效果圖

 

3)使用Visual Studio 2022建立一個.NET 6.0 Web專案,參考PubSub的文件來建立一個服務端來託管 /negotiate API和Web 頁面,參考示例:教程:使用子協議在 WebSocket 客戶端之間釋出和訂閱訊息

Program.cs檔案內容為:

  • string ConnectionString = "<Web PubSub Access Key>" 獲取的辦法見第二步建立的PubSub服務的Key頁面 --> Connection String

using Azure.Messaging.WebPubSub;
using Microsoft.Extensions.Azure;

string ConnectionString = "<Web PubSub Access Key>";

// Add WebPubSub Service 
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAzureClients(builder =>
{
    builder.AddWebPubSubServiceClient(ConnectionString, "stream");
});

var app = builder.Build();

app.UseStaticFiles();

app.UseRouting();

//實現生產客戶端WebSocket SAS URL
app.UseEndpoints(endpoints =>
{
    endpoints.MapGet("/negotiate/{userid}", async (string userid, HttpContext context) =>
    {
        var service = context.RequestServices.GetRequiredService<WebPubSubServiceClient>();
        var response = new
        {
            url = service.GetClientAccessUri(userId: userid, roles: new string[] { "webpubsub.sendToGroup.stream", "webpubsub.joinLeaveGroup.stream" }).AbsoluteUri
        };
        await context.Response.WriteAsJsonAsync(response);
    });
});

app.MapGet("/", () => "/index.html");

app.Run();

前端 wwwroot/stream.html 內容為:

<html>
<style>
    html,
    body {
        margin: 0;
        padding: 0;
        background-color: rgb(0, 0, 0);
    }

    #divList {
        width: 98%;
        height: 79%;
        border: solid 1px rgb(0, 15, 0);
        ;
        margin: 0px auto;
        overflow: hidden;
        position: relative;
    }

    .divText {
        position: absolute;
    }

    .divText span {
        display: block;
        font-weight: bold;
        font-family: Courier New;
    }
</style>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>

<body> <br>
    <h2 style="text-align:center; color:white;">STREAM BOARDCAST (<span id="spanCount">0</span>)</h2>
    <div id="divList">
    </div>
    <h3 style="text-align:left; color:grey;">Send</h3>
    <div style=" margin: 5px 15px;">
        <input id="inputmessage" style="text-align:left; color:green; width: 86%; height: 55px; font-size: 40px;" onsubmit="sendmessage()"><button
            style="text-align:center; color:green; width: 12%; height: 55px;font-size: 40px;" onclick="sendmessage()">Send</button>
    </div>
    <div style="display:none;">
        <h1>Message</h1>
        <div id="output"></div>
        <br>
        <h1>Message</h1>
        <div id="outputmsg"></div>
    </div>
    <script>
        var textarray = ["Hello Mooncake,Welcome to use PubSub", "Who are you? Put your PIN ... "];
        var wsurl = "";
        var wsclient;
        let ackId = 0;
        (async function () {
            let res = await fetch('/negotiate/client_2')
            let data = await res.json();
            wsurl = data.url;
            let ws = new WebSocket(data.url, 'json.webpubsub.azure.v1');
            ws.onopen = () => {
                console.log('connected');
            };

            let output = document.querySelector('#output');
            let outputmsg = document.querySelector('#outputmsg');

            ws.onmessage = event => {
                let d = document.createElement('p');
                d.innerText = event.data;
                output.appendChild(d);

                let message = JSON.parse(event.data);
                if (message.type === 'message' && message.group === 'stream') {
                    let d = document.createElement('span');
                    d.innerText = message.data;
                    textarray.push(message.data);
                    outputmsg.appendChild(d);
                    window.scrollTo(0, document.body.scrollHeight);
                }
            };

            ws.onopen = () => {
                console.log('connected');
                ws.send(JSON.stringify({
                    type: 'joinGroup',
                    group: 'stream',
                    ackId: ++ackId
                }));

                wsclient = ws;
            };
        })();


        function sendmessage() {

            if (wsclient.readyStatue == WebSocket.OPEN) {
                wsclient.send(JSON.stringify(
                    {
                        type: "sendToGroup",
                        group: "stream",
                        data: $('#inputmessage').val(),
                        ackId: ++ackId // ackId is optional, use ackId to make sure this action is executed
                    }
                ));
            }
            else { 
                wsclient = new WebSocket(wsurl, 'json.webpubsub.azure.v1');

                wsclient.onopen = () => {
                    console.log('connected again');
                    wsclient.send(JSON.stringify({
                        type: "sendToGroup",
                        group: "stream",
                        data: $('#inputmessage').val(),
                        ackId: ++ackId
                    }));
 
                };
            }
            $('#inputmessage').focus();
        }



        function rand(min, max) {
            return min + Math.round(Math.random() * (max - min));
        }

        function add(message) {
            var maxwdth = $('#divList').width();
            var x = rand(0, maxwdth);
            var html = '<div class="divText" style="left:' + x + 'px; bottom:500px;">';

            var color = [];
            for (var i = 1; i < message.length; i++) {
                var f = i.toString(16);
                color.push('0' + f + '0');
            }

            var fontSize = rand(20, 36);
            for (var i = 1; i <= message.length; i++) {
                var c = message[i - 1];
                html += '<span class="s' + i + '" style="color:#' + color[i - 1] + '; font-size:' + fontSize + 'px; text-shadow:0px 0px 10px #' + color[i - 1] + ';">' + c + '</span>';
            }
            html += '</div>';
            $('#divList').append(html);
        }

        function run() {
            var x = rand(0, 100);
            if (x < 100) {
                var lgh = textarray.length;
                if (textarray.length > x) add(textarray[x]);
                else
                    add(textarray[x % lgh]);
            }
            $('#spanCount').html($('.divText').size());

            $('.divText').each(function () {
                var y = $(this).css('bottom');
                y = parseInt(y);
                y -= $(this).find('span').eq(0).height();
                $(this).css('bottom', '' + y + 'px');
                if (y + $(this).height() <= 0) {
                    $(this).remove();
                    return;
                }
            });

            window.setTimeout(run, 200);
        }
        run();

    </script>
</body>

</html>

專案結構示意圖:

【Azure Developer】App Service + PubSub +JS 實現多人版黑客帝國文字流效果圖

 

專案建立完成後,可以在本機進行除錯。正常執行後即可釋出到Azure App Service上。因程式碼簡單並且可讀性強,可自行理解。

 

4)通過VS 2022釋出到App Service中

VS 2022 通過Publish Profile 釋出站點演示動畫:

【Azure Developer】App Service + PubSub +JS 實現多人版黑客帝國文字流效果圖

釋出版本操作完成。

 

附錄一:本地黑客帝國文字流效果,可自行輸入即修改內容版

複製內容,直接儲存在本地檔案中,檔案命名為 localstream.html後使用瀏覽器開啟即可

<html>
<style>
    html,
    body {
        margin: 0;
        padding: 0;
        background-color: rgb(0, 0, 0);
    }

    #divList {
        width: 98%;
        height: 79%;
        border: solid 1px rgb(0, 15, 0);
        ;
        margin: 0px auto;
        overflow: hidden;
        position: relative;
    }

    .divText {
        position: absolute;
    }

    .divText span {
        display: block;
        font-weight: bold;
        font-family: Courier New;
    }
</style>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>

<body> <br>
    <h2 style="text-align:center; color:white;">STREAM BOARDCAST (<span id="spanCount">0</span>)</h2>
    <div id="divList">
    </div>
    <h3 style="text-align:left; color:grey;">Send</h3>
    <div style=" margin: 5px 15px;">
        <input id="inputmessage" style="text-align:left; color:green; width: 86%; height: 55px; font-size: 40px;" onsubmit="sendmessage()"><button
            style="text-align:center; color:green; width: 12%; height: 55px;font-size: 40px;" onclick="sendmessage()">Send</button>
    </div>
    <div style="display:none;">
        <h1>Message</h1>
        <div id="output"></div>
        <br>
        <h1>Message</h1>
        <div id="outputmsg"></div>
    </div>
    <script>
        var textarray = ["Hello Mooncake,Welcome to use PubSub", "Who are you? Put your PIN ... "];

        function sendmessage() {
            textarray.push($('#inputmessage').val());
            $('#inputmessage').focus();
        }



        function rand(min, max) {
            return min + Math.round(Math.random() * (max - min));
        }

        function add(message) {
            var maxwdth = $('#divList').width();
            var x = rand(0, maxwdth);
            var html = '<div class="divText" style="left:' + x + 'px; bottom:500px;">';

            var color = [];
            for (var i = 1; i < message.length; i++) {
                var f = i.toString(16);
                color.push('0' + f + '0');
            }

            var fontSize = rand(20, 36);
            for (var i = 1; i <= message.length; i++) {
                var c = message[i - 1];
                html += '<span class="s' + i + '" style="color:#' + color[i - 1] + '; font-size:' + fontSize + 'px; text-shadow:0px 0px 10px #' + color[i - 1] + ';">' + c + '</span>';
            }
            html += '</div>';
            $('#divList').append(html);
        }

        function run() {
            var x = rand(0, 100);
            if (x < 100) {
                var lgh = textarray.length;
                if (textarray.length > x) add(textarray[x]);
                else
                    add(textarray[x % lgh]);
            }
            $('#spanCount').html($('.divText').size());

            $('.divText').each(function () {
                var y = $(this).css('bottom');
                y = parseInt(y);
                y -= $(this).find('span').eq(0).height();
                $(this).css('bottom', '' + y + 'px');
                if (y + $(this).height() <= 0) {
                    $(this).remove();
                    return;
                }
            });

            window.setTimeout(run, 200);
        }
        run();

    </script>
</body>

</html>

PS: 與PubSub版本相比,只是移除了WebSocket的相關程式碼

執行效果

【Azure Developer】App Service + PubSub +JS 實現多人版黑客帝國文字流效果圖

 

 

參考資料

JS 黑客帝國文字下落效果: https://www.cnblogs.com/zjfree/p/3833592.html 

教程:使用子協議在 WebSocket 客戶端之間釋出和訂閱訊息:https://docs.microsoft.com/zh-cn/azure/azure-web-pubsub/tutorial-subprotocol?tabs=csharp#using-a-subprotocol

快速入門:部署 ASP.NET Web 應用:https://docs.azure.cn/zh-cn/app-service/quickstart-dotnetcore?tabs=net60&pivots=development-environment-vs

 

相關文章