【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
【Azure Developer】App Service + PubSub +JS 实现多人版黑客帝国文字流效果图的更多相关文章
- 【Azure DevOps系列】使ASP.NET Core应用程序托管到Azure Web App Service
使用Azure DevOps Project设置ASP.NET项目 我们需要先在Azure面板中创建一个Azure WebApp服务,此处步骤我将省略,然后点击部署中心如下图所示: 此处我选择的是Az ...
- 【Azure 应用服务】App Service中,为Java应用配置自定义错误页面,禁用DELETE, PUT方法
问题定义 使用Azure应用服务(App Service),部署Java应用,使用Tomcat容器,如何自定义错误页面呢?同时禁用DELETE, PUT方法 解决办法 如何自定义错误页面呢?需要在 J ...
- 【应用服务 App Service】快速获取DUMP文件(App Service for Windows(.NET/.NET Core))
问题情形 当应用在Azure 应用服务App Service中运行时,有时候出现CPU,Memory很高,但是没有明显的5XX错误和异常日志,有时就是有异常但是也不能明确的指出具体的代码错误.当面临这 ...
- 【Azure 应用服务】App Service 通过配置web.config来添加请求返回的响应头(Response Header)
问题描述 在Azure App Service上部署了站点,想要在网站的响应头中加一个字段(Cache-Control),并设置为固定值(Cache-Control:no-store) 效果类似于本地 ...
- 【Azure 应用服务】NodeJS Express + MSAL 应用实现AAD集成登录并部署在App Service Linux环境中的实现步骤
问题描述 实现部署NodeJS Express应用在App Service Linux环境中,并且使用Microsoft Authentication Library(MSAL)来实现登录Azure ...
- 改善Azure App Service托管应用程序性能的几个技巧
本文介绍了几个技巧,这些技巧可以改善Azure App Service托管应用程序的性能.其中一些技巧是你现在就可以进行的配置变更, 而其他技巧则可能需要对应用程序进行一些重新设计和重构. 开发者都希 ...
- 【应用服务 App Service】解决无法从Azure门户SSH登录问题
问题描述 中国区的Azure App Service(应用服务)已经支持创建Docker并选择Linux环境.在使用中,我们可以继续通过kudu站点的方式登录查看站点的一些日志及部署文件.它的登录方式 ...
- 【Azure 应用服务】App Service与APIM同时集成到同一个虚拟网络后,如何通过内网访问内部VNET的APIM呢?
问题描述 App Service访问的APIM已配置内部虚拟网络(Internal VNet)并拥有内网IP地址.App Service与APIM都在相同的虚拟网络(VNET)中.App Servic ...
- 【Azure 应用服务】App Service 在使用GIt本地部署,上传代码的路径为/home/site/repository,而不是站点的根目录/home/site/wwwroot。 这个是因为什么?
问题描述 App Service 在使用GIt本地部署,上传代码的路径为/home/site/repository,而不是站点的根目录/home/site/wwwroot. 这个是因为什么? 并且通过 ...
随机推荐
- 《深入理解ES6》笔记—— Promise与异步编程(11)
为什么要异步编程 我们在写前端代码时,经常会对dom做事件处理操作,比如点击.激活焦点.失去焦点等:再比如我们用ajax请求数据,使用回调函数获取返回值.这些都属于异步编程. 也许你已经大概知道Jav ...
- jdbc连接数据库以及简单实现(普通JDBC方法实现和连接池方式实现)
@ 目录 总结内容 1. 基本概念 jdbc的概念 2. 数据库连接 数据库的连接 DAO层思想 重构设计 3. 事务 概念 事务的ACID属性 事务的操作 4. 连接池 为什么要使用连接池 连接池分 ...
- Java中 i++和++i 的区别
学习目标: 理解i++和++i的区别 学习内容: 1.i++ / i- - i++/i- -:遇到 i++或 i- -,i先参与运算,然后 i 再自加或自减1 代码如下: int a = 1; int ...
- redis笔记补充
redis补充 这篇文章是redis入门笔记的补充. 1.info命令 用来显示服务的信息. info命令可以跟下面的选项: server: 关于 Redis 服务器的一些信息 clients: 客户 ...
- style设置 三个 竖线隔得太近了,这个属性和值 设置让他们分开一点
||| // html letter-spacing: 0.2em; // css
- ArcGIS使用技巧(三)——关于投影
新手,若有错误还请指正! 简单记录一下自己所理解的ArcGIS中的有关投影的知识点. 在数据处理过程中,基本都是需要将相关数据放在同一投影坐标系下,需要用到投影转换工具,但若有的数据没有坐标信息,则首 ...
- pgpool-II 4.3 中文手册 - 入门教程
本章解释了如何开始使用 Pgpool-II. 安装 在本节中,我们假设您已经安装了 Pgpool-II 与 PostgreSQL 集群. 你的第一个复制(Replication) 在本节中,我们将解释 ...
- 程序员延寿指南「GitHub 热点速览 v.22.17」
很多人对程序员的固有印象之一便是常加班.易"猝死"!近几年的许多报道似乎也进一步加深了这种印象.应该如何更好地健康地活着.敲喜欢的代码呢?HowToLiveLonger 教你如何从 ...
- 从0开始用webpack开发antd,react组件库npm包并发布
一.初始化一个npm包 1.新建一个文件夹(名称随意,建议和报名一致),输入命令 :npm init -y 会自动生成一个包的说明文件 package.json如下(本文以scroll-antd-ta ...
- lab_1 清华大学ucore bootload启动ucore os(预备基础知识+实验过程)
实验1 :bootload启动ucore os 1.0实验内容: lab1中包含一个bootloader和一个OS.这个bootloader可以切换到X86保护模式,能够读磁盘并加载ELF执行文件格式 ...
