需要描述
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