【Azure Developer】App Service + PubSub +JS 实现多人版黑客帝国文字流效果图

简介: 【Azure Developer】App Service + PubSub +JS 实现多人版黑客帝国文字流效果图

需要描述

1)实现黑客帝国文字流效果图,JS功能

2)部署在云中,让大家都可以访问,App Service实现

3)大家都能发送消息,并显示在文字流中,PubSub(websocket)实现

 

终极效果显示:

 

执行步骤

1)在 Azure 中创建 App Service 服务,参考官方文档:快速入门:部署 ASP.NET Web 应用

Azure门户中创建App Service的动画演示:

 

2)在 Azure 中创建 PubSub 服务,参考官方文档:快速入门:从 Azure 门户创建 Web PubSub 实例

Azure门户中创建Web PubSub的动画演示:

 

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 App Service上。因代码简单并且可读性强,可自行理解。

 

4)通过VS 2022发布到App Service中

VS 2022 通过Publish Profile 发布站点演示动画:

发布版本操作完成。

 

附录一:本地黑客帝国文字流效果,可自行输入即修改内容版

复制内容,直接保存在本地文件中,文件命名为 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的相关代码

运行效果

 

 

参考资料

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

 

相关文章
|
12天前
|
C# Windows
【Azure App Service】在App Service for Windows上验证能占用的内存最大值
根据以上测验,当使用App Service内存没有达到预期的值,且应用异常日志出现OutOfMemory时,就需要检查Platform的设置是否位64bit。
37 11
|
13天前
|
JavaScript C++ 容器
【Azure Bot Service】部署NodeJS ChatBot代码到App Service中无法自动启动
2024-11-12T12:22:40.366223350Z Error: Cannot find module 'dotenv' 2024-11-12T12:40:12.538120729Z Error: Cannot find module 'restify' 2024-11-12T12:48:13.348529900Z Error: Cannot find module 'lodash'
37 11
|
7天前
|
缓存 容器 Perl
【Azure Container App】Container Apps 设置延迟删除 (terminationGracePeriodSeconds) 的解释
terminationGracePeriodSeconds : 这个参数的定义是从pod收到terminated signal到最终shutdown的最大时间,这段时间是给pod中的application 缓冲时间用来处理链接关闭,应用清理缓存的;并不是从idel 到 pod被shutdown之间的时间;且是最大时间,意味着如果application 已经gracefully shutdown,POD可能被提前terminated.
|
11天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
10天前
|
Java 开发工具 Windows
【Azure App Service】在App Service中调用Stroage SDK上传文件时遇见 System.OutOfMemoryException
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
|
10天前
|
安全 Apache 开发工具
【Azure App Service】在App Service上关于OpenSSH的CVE2024-6387漏洞解答
CVE2024-6387 是远程访问漏洞,攻击者通过不安全的OpenSSh版本可以进行远程代码执行。CVE-2024-6387漏洞攻击仅应用于OpenSSH服务器,而App Service Runtime中并未使用OpenSSH,不会被远程方式攻击,所以OpenSSH并不会对应用造成安全风险。同时,如果App Service的系统为Windows,不会受远程漏洞影响!
|
1月前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
505 7
|
1月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
599 1
|
21天前
|
小程序 数据挖掘 UED
开发1个上门家政小程序APP系统,都有哪些功能?
在快节奏的现代生活中,家政服务已成为许多家庭的必需品。针对传统家政服务存在的问题,如服务质量不稳定、价格不透明等,我们历时两年开发了一套全新的上门家政系统。该系统通过完善信用体系、提供奖励机制、优化复购体验、多渠道推广和多样化盈利模式,解决了私单、复购、推广和盈利四大痛点,全面提升了服务质量和用户体验,旨在成为家政行业的领导者。
|
1月前
|
JavaScript 前端开发 小程序
uniapp一个人开发APP关键步骤和考虑因素
uniapp一个人开发APP关键步骤和考虑因素
125 1
uniapp一个人开发APP关键步骤和考虑因素

热门文章

最新文章

下一篇
无影云桌面