【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

 

相关文章
|
1月前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
142 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
14天前
|
存储 监控 API
【Azure App Service】分享使用Python Code获取App Service的服务器日志记录管理配置信息
本文介绍了如何通过Python代码获取App Service中“Web服务器日志记录”的配置状态。借助`azure-mgmt-web` SDK,可通过初始化`WebSiteManagementClient`对象、调用`get_configuration`方法来查看`http_logging_enabled`的值,从而判断日志记录是否启用及存储方式(关闭、存储或文件系统)。示例代码详细展示了实现步骤,并附有执行结果与官方文档参考链接,帮助开发者快速定位和解决问题。
63 22
|
28天前
|
容器
【Azure Container App】在消耗性的Container App Environmnet中无法查看当时正在使用多少CPU多少实例数的替代方案
在 Azure Container Apps 中使用 Consumption 消耗型环境时,无法通过门户查看当前核心 (CPU) 和实例使用情况。这是因为消耗型工作负载配置文件的设计所致。若需查看使用状态,可使用 az cli 命令 `az containerapp env list-usages` 获取详细信息,包括 Current Cores 数量。文档还提供了参考资料链接以帮助用户深入了解相关命令用法。
61 17
|
25天前
|
网络协议
【Azure App Service】App Service 如何配置私网域名以及证书呢?
本文解答了关于 Azure App Service 如何配置私网域名及证书的问题。App Service 不支持私网域名,自定义域名需配置在公共 DNS 服务器上。文章引用官方文档详细说明了映射自定义 DNS 的步骤,并附带参考资料链接,帮助用户深入了解相关配置方法。
|
1月前
|
存储 安全 开发工具
【Azure Storage Account】利用App Service作为反向代理后续 ---- 隐藏 SAS Token
本文介绍了如何通过App Service作为反向代理,将SAS Token追加到请求URL中,以避免直接在代码或配置文件中存储SAS Token带来的安全性和维护问题。具体步骤包括修改App Service的web.config Rewrite规则,将SAS Token添加到转发的URL中;并在.NET SDK中仅使用不包含SAS Token的Uri进行Blob操作。这样既提高了安全性,也简化了SAS Token的管理。
53 16
|
2月前
|
Web App开发 网络协议 网络安全
【Azure App Service】App Service 是否支持HostName SNI 证书?
App Service 支持 HostName SNI 证书。为自定义域名添加 SSL 证书时,可以选择 SNI SSL 和基于 IP 的 SSL。SNI SSL 允许多个 TLS/SSL 证书保护同一 IP 地址上的多个域,适用于大多数现代浏览器。配置方法是在添加自定义域名后,点击 Add binding 配置 SNI SSL。参考文档:[Azure 官方文档](https://docs.azure.cn/zh-cn/app-service/configure-ssl-bindings#add-the-binding)。
|
1月前
|
存储 XML 开发工具
【Azure Storage Account】利用App Service作为反向代理, 并使用.NET Storage Account SDK实现上传/下载操作
本文介绍了如何在Azure上使用App Service作为反向代理,以自定义域名访问Storage Account。主要内容包括: 1. **设置反向代理**:通过配置`applicationhost.xdt`和`web.config`文件,启用IIS代理功能并设置重写规则。 2. **验证访问**:测试原生URL和自定义域名的访问效果,确保两者均可正常访问Storage Account。 3. **.NET SDK连接**:使用共享访问签名(SAS URL)初始化BlobServiceClient对象,实现通过自定义域名访问存储服务。
|
1月前
|
安全 API Swift
如何在苹果内购开发中获取App Store Connect API密钥-共享密钥理解内购安全-优雅草卓伊凡
如何在苹果内购开发中获取App Store Connect API密钥-共享密钥理解内购安全-优雅草卓伊凡
108 15
如何在苹果内购开发中获取App Store Connect API密钥-共享密钥理解内购安全-优雅草卓伊凡
|
25天前
|
Web App开发 编解码 算法
布谷一对一直播源码开发:阿里云视频语音通话社交交友App的必备功能
在当今移动社交领域,一对一视频和语音通话功能已成为用户期待的基础配置。从熟人社交到陌生人交友,从专业咨询到情感陪伴,实时音视频互动能力直接决定了社交App的用户留存和市场竞争力。山东布谷科技将深入探讨一对一直播源码开发高质量一对一视频和语音通话功能的关键要素和技术实现方案。
布谷一对一直播源码开发:阿里云视频语音通话社交交友App的必备功能
|
18天前
|
人工智能 小程序 API
【一步步开发AI运动APP】四、使用相机组件抽帧
本文介绍了如何使用`ai-camera`组件开发AI运动APP,助力开发者深耕AI运动领域。`ai-camera`是专为AI运动场景设计的相机组件,支持多平台,提供更强的抽帧处理能力和API。文章详细讲解了获取相机上下文、执行抽帧操作以及将帧保存到相册的功能实现,并附有代码示例。无论是AI运动APP还是其他场景,该组件都能满足预览、拍照、抽帧等需求。下篇将聚焦人体识别检测,敬请期待!

热门文章

最新文章