tune design pattern of IProjectService

This commit is contained in:
unknown 2025-08-25 14:00:52 +08:00
parent feb25e5871
commit b87779ade7

View File

@ -6,453 +6,443 @@ using Hi.Webapi.Services;
namespace Hi.Webapi.Hubs namespace Hi.Webapi.Hubs
{ {
/// <summary> /// <summary>
/// SignalR Hub 用於處理渲染畫布的實時通信 /// SignalR Hub 用於處理渲染畫布的實時通信
/// </summary> /// </summary>
public class RenderingHub : Hub public class RenderingHub : Hub
{ {
private readonly RenderingService _renderingService; private readonly RenderingService _renderingService;
private readonly ILogger<RenderingHub> _logger; private readonly ILogger<RenderingHub> _logger;
private readonly Dictionary<string, byte[]> _lastFrameCache = []; private readonly Dictionary<string, byte[]> _lastFrameCache = [];
private readonly Dictionary<string, DateTime> _lastFrameTime = []; private readonly Dictionary<string, Mat4d> _sketchViewCache = [];
private readonly Dictionary<string, Mat4d> _sketchViewCache = [];
public RenderingHub(RenderingService renderingService, ILogger<RenderingHub> logger)
public RenderingHub(RenderingService renderingService, ILogger<RenderingHub> logger) {
{ _renderingService = renderingService;
_renderingService = renderingService; _logger = logger;
_logger = logger; }
}
/// <summary>
/// <summary> /// 客戶端連接時初始化渲染引擎
/// 客戶端連接時初始化渲染引擎 /// </summary>
/// </summary> public async Task InitializeCanvas(int width, int height)
public async Task InitializeCanvas(int width, int height) {
{ var sessionId = Context.ConnectionId;
var sessionId = Context.ConnectionId; _logger.LogInformation($"InitializeCanvas called - SessionId: {sessionId}, Width: {width}, Height: {height}");
_logger.LogInformation($"InitializeCanvas called - SessionId: {sessionId}, Width: {width}, Height: {height}");
var engine = _renderingService.GetOrCreateEngine(sessionId);
var engine = _renderingService.GetOrCreateEngine(sessionId); _logger.LogInformation($"Engine created/retrieved - SessionId: {sessionId}");
_logger.LogInformation($"Engine created/retrieved - SessionId: {sessionId}");
// 啟動引擎(必須在設置回調之前)
// 啟動引擎(必須在設置回調之前) engine.Start(width, height);
engine.Start(width, height); _logger.LogInformation($"Engine started with size {width}x{height} - SessionId: {sessionId}");
_logger.LogInformation($"Engine started with size {width}x{height} - SessionId: {sessionId}");
// 在設置回調之前,先捕獲客戶端代理
// 在設置回調之前,先捕獲客戶端代理 var clientProxy = Clients.Caller;
var clientProxy = Clients.Caller; var logger = _logger;
var logger = _logger; var frameCount = 0;
var frameCount = 0;
byte[] preImageRgba = null; // 設置渲染回調
unsafe
// 設置渲染回調 {
unsafe engine.ImageRequestAfterBufferSwapped += (byte* bgra, int w, int h) =>
{ {
engine.ImageRequestAfterBufferSwapped += (byte* bgra, int w, int h) => if (bgra == null)
{ return;
if (bgra == null)
return; frameCount++;
if (frameCount <= 5) // 只記錄前5幀
// 幀率限制 - 最多每秒30幀 {
if (_lastFrameTime.TryGetValue(sessionId, out var lastTime)) logger.LogInformation($"Frame {frameCount} rendered - Size: {w}x{h}, SessionId: {sessionId}");
{ }
var elapsed = DateTime.Now - lastTime;
if (elapsed.TotalMilliseconds < 33) // 30 FPS // 檢查寬度和高度是否有效
{ if (w <= 0 || h <= 0)
return; {
} logger.LogWarning($"Invalid image size: {w}x{h} - SessionId: {sessionId}");
} return;
_lastFrameTime[sessionId] = DateTime.Now; }
frameCount++; try
if (frameCount <= 5) // 只記錄前5幀 {
{ // 在回調中同步處理圖像數據,轉換為 byte[]
logger.LogInformation($"Frame {frameCount} rendered - Size: {w}x{h}, SessionId: {sessionId}"); int wh = w * h;
} byte[] rgba = new byte[wh * 4];
for (int i = 0; i < wh; i++)
// 檢查寬度和高度是否有效 {
if (w <= 0 || h <= 0) rgba[i * 4] = bgra[i * 4 + 2];
{ rgba[i * 4 + 1] = bgra[i * 4 + 1];
logger.LogWarning($"Invalid image size: {w}x{h} - SessionId: {sessionId}"); rgba[i * 4 + 2] = bgra[i * 4];
return; rgba[i * 4 + 3] = bgra[i * 4 + 3];
} }
try if (_lastFrameCache.TryGetValue(
{ sessionId, out byte[] preImageRgba))
// 在回調中同步處理圖像數據,轉換為 byte[] {
int wh = w * h; if (preImageRgba != null &&
byte[] rgba = new byte[wh * 4]; preImageRgba.SequenceEqual(rgba))
for (int i = 0; i < wh; i++) return;
{ }
rgba[i * 4] = bgra[i * 4 + 2]; _lastFrameCache[sessionId] = rgba;
rgba[i * 4 + 1] = bgra[i * 4 + 1];
rgba[i * 4 + 2] = bgra[i * 4];
rgba[i * 4 + 3] = bgra[i * 4 + 3]; if (frameCount <= 5)
} {
logger.LogInformation($"Frame {frameCount} sending - Size: {w}x{h}, Data: {rgba.Length} bytes");
// 檢查圖像是否有變化 }
if (preImageRgba != null && preImageRgba.SequenceEqual(rgba))
return; using MemoryStream dstMemoryStream = new MemoryStream();
preImageRgba = rgba; using GZipStream gZipStream = new GZipStream(dstMemoryStream, CompressionMode.Compress);
using MemoryStream srcMemoryStream = new MemoryStream(rgba);
if (frameCount <= 5) srcMemoryStream.CopyTo(gZipStream);
{ gZipStream.Close();
logger.LogInformation($"Frame {frameCount} sending - Size: {w}x{h}, Data: {rgba.Length} bytes"); byte[] compressedRgbaArray = dstMemoryStream.ToArray();
}
if (compressedRgbaArray == null)
// 壓縮數據 return;
using MemoryStream dstMemoryStream = new MemoryStream();
using GZipStream gZipStream = new GZipStream(dstMemoryStream, CompressionMode.Compress); var compressionRatio = (float)compressedRgbaArray.Length / rgba.Length * 100;
using MemoryStream srcMemoryStream = new MemoryStream(rgba);
srcMemoryStream.CopyTo(gZipStream); if (frameCount <= 5)
gZipStream.Close(); {
byte[] compressedRgbaArray = dstMemoryStream.ToArray(); logger.LogInformation($"Compressed: {compressedRgbaArray.Length} bytes ({compressionRatio:F1}% of original)");
}
if (compressedRgbaArray == null)
return; // 發送壓縮數據到客戶端(注意:第二個參數應該是原始長度,不是壓縮後的長度)
_ = clientProxy.SendAsync("ImageUpdate",
var compressionRatio = (float)compressedRgbaArray.Length / rgba.Length * 100; compressedRgbaArray,
rgba.Length,
if (frameCount <= 5) w,
{ h)
logger.LogInformation($"Compressed: {compressedRgbaArray.Length} bytes ({compressionRatio:F1}% of original)"); .ContinueWith(t =>
} {
if (t.IsFaulted)
// 發送壓縮數據到客戶端(注意:第二個參數應該是原始長度,不是壓縮後的長度) {
_ = clientProxy.SendAsync("ImageUpdate", logger.LogError($"Failed to send frame: {t.Exception?.GetBaseException().Message} - SessionId: {sessionId}");
compressedRgbaArray, }
rgba.Length, });
w, }
h) catch (ObjectDisposedException)
.ContinueWith(t => {
{ // Hub 已經被釋放,忽略這個錯誤
if (t.IsFaulted) logger.LogDebug($"Hub已釋放忽略渲染回調 - SessionId: {sessionId}");
{ }
logger.LogError($"Failed to send frame: {t.Exception?.GetBaseException().Message} - SessionId: {sessionId}"); catch (Exception ex)
} {
}); logger.LogError(ex, $"渲染回調錯誤 - SessionId: {sessionId}");
} }
catch (ObjectDisposedException) };
{ }
// Hub 已經被釋放,忽略這個錯誤
logger.LogDebug($"Hub已釋放忽略渲染回調 - SessionId: {sessionId}"); // 設置初始視圖
} if (_sketchViewCache.TryGetValue(sessionId, out var cachedView))
catch (Exception ex) {
{ engine.SketchView = cachedView;
logger.LogError(ex, $"渲染回調錯誤 - SessionId: {sessionId}"); }
} else
}; {
} engine.SetViewToHomeView();
_sketchViewCache[sessionId] = engine.SketchView;
// 設置初始視圖 }
if (_sketchViewCache.TryGetValue(sessionId, out var cachedView)) _logger.LogInformation($"View initialized - SessionId: {sessionId}");
{
engine.SketchView = cachedView; // 確保引擎可見並開始渲染
} engine.IsVisible = true;
else _logger.LogInformation($"Engine visibility set to true - SessionId: {sessionId}");
{
engine.SetViewToHomeView(); await Clients.Caller.SendAsync("CanvasInitialized", sessionId);
_sketchViewCache[sessionId] = engine.SketchView; _logger.LogInformation($"Canvas initialized for session: {sessionId}");
} }
_logger.LogInformation($"View initialized - SessionId: {sessionId}");
/// <summary>
// 確保引擎可見並開始渲染 /// 處理鼠標移動事件
engine.IsVisible = true; /// </summary>
_logger.LogInformation($"Engine visibility set to true - SessionId: {sessionId}"); public Task HandleMouseMove(double x, double y, int buttonMask)
{
await Clients.Caller.SendAsync("CanvasInitialized", sessionId); var sessionId = Context.ConnectionId;
_logger.LogInformation($"Canvas initialized for session: {sessionId}"); _logger.LogDebug($"HandleMouseMove - SessionId: {sessionId}, X: {x}, Y: {y}, ButtonMask: {buttonMask}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> var p = new Vec2i((int)x, (int)y);
/// 處理鼠標移動事件
/// </summary> // 移動鼠標
public Task HandleMouseMove(double x, double y, int buttonMask) engine.MouseMove(p.x, p.y);
{
var sessionId = Context.ConnectionId; // 如果有按鈕按下,處理拖曳
_logger.LogDebug($"HandleMouseMove - SessionId: {sessionId}, X: {x}, Y: {y}, ButtonMask: {buttonMask}"); if (buttonMask > 0)
{
var engine = _renderingService.GetOrCreateEngine(sessionId); engine.MouseDragTransform(p.x, p.y,
var p = new Vec2i((int)x, (int)y); new mouse_button_table__transform_view_by_mouse_drag_t()
{
// 移動鼠標 LEFT_BUTTON = 0,
engine.MouseMove(p.x, p.y); RIGHT_BUTTON = 2
});
// 如果有按鈕按下,處理拖曳 _sketchViewCache[sessionId] = engine.SketchView;
if (buttonMask > 0) }
{
engine.MouseDragTransform(p.x, p.y, return Task.CompletedTask;
new mouse_button_table__transform_view_by_mouse_drag_t() }
{
LEFT_BUTTON = 0, /// <summary>
RIGHT_BUTTON = 2 /// 處理鼠標按下事件
}); /// </summary>
_sketchViewCache[sessionId] = engine.SketchView; public Task HandleMouseDown(double x, double y, int button)
} {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleMouseDown - SessionId: {sessionId}, X: {x}, Y: {y}, Button: {button}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> engine.MouseButtonDown(button);
/// 處理鼠標按下事件 return Task.CompletedTask;
/// </summary> }
public Task HandleMouseDown(double x, double y, int button)
{ /// <summary>
var sessionId = Context.ConnectionId; /// 處理鼠標釋放事件
_logger.LogInformation($"HandleMouseDown - SessionId: {sessionId}, X: {x}, Y: {y}, Button: {button}"); /// </summary>
public Task HandleMouseUp(double x, double y, int button)
var engine = _renderingService.GetOrCreateEngine(sessionId); {
engine.MouseButtonDown(button); var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleMouseUp - SessionId: {sessionId}, X: {x}, Y: {y}, Button: {button}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> engine.MouseButtonUp(button);
/// 處理鼠標釋放事件 return Task.CompletedTask;
/// </summary> }
public Task HandleMouseUp(double x, double y, int button)
{ /// <summary>
var sessionId = Context.ConnectionId; /// 處理鼠標滾輪事件
_logger.LogInformation($"HandleMouseUp - SessionId: {sessionId}, X: {x}, Y: {y}, Button: {button}"); /// </summary>
public Task HandleMouseWheel(double x, double y, double deltaX, double deltaY, string browserBrand = "chrome")
var engine = _renderingService.GetOrCreateEngine(sessionId); {
engine.MouseButtonUp(button); var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleMouseWheel - SessionId: {sessionId}, X: {x}, Y: {y}, DeltaX: {deltaX}, DeltaY: {deltaY}, Browser: {browserBrand}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary>
/// 處理鼠標滾輪事件 // 根據瀏覽器類型獲取縮放比例
/// </summary> double scale = GetWheelScaling(browserBrand);
public Task HandleMouseWheel(double x, double y, double deltaX, double deltaY, string browserBrand = "chrome")
{ engine.MouseWheel((int)(deltaX * scale), -(int)(deltaY * scale));
var sessionId = Context.ConnectionId;
_logger.LogInformation($"HandleMouseWheel - SessionId: {sessionId}, X: {x}, Y: {y}, DeltaX: {deltaX}, DeltaY: {deltaY}, Browser: {browserBrand}"); // 使用 MouseWheelTransform 進行視圖變換
engine.MouseWheelTransform(
var engine = _renderingService.GetOrCreateEngine(sessionId); (int)(deltaX * scale * 1000), -(int)(deltaY * scale * 1000), 0.1 / 1000);
// 根據瀏覽器類型獲取縮放比例 _sketchViewCache[sessionId] = engine.SketchView;
double scale = GetWheelScaling(browserBrand);
return Task.CompletedTask;
engine.MouseWheel((int)(deltaX * scale), -(int)(deltaY * scale)); }
// 使用 MouseWheelTransform 進行視圖變換 /// <summary>
engine.MouseWheelTransform( /// 處理窗口大小變化
(int)(deltaX * scale * 1000), -(int)(deltaY * scale * 1000), 0.1 / 1000); /// </summary>
public Task HandleResize(int width, int height)
_sketchViewCache[sessionId] = engine.SketchView; {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleResize - SessionId: {sessionId}, Width: {width}, Height: {height}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary>
/// 處理窗口大小變化 // 重置圖像緩存以強制重新渲染
/// </summary> if (_lastFrameCache.ContainsKey(sessionId))
public Task HandleResize(int width, int height) {
{ _lastFrameCache.Remove(sessionId);
var sessionId = Context.ConnectionId; }
_logger.LogInformation($"HandleResize - SessionId: {sessionId}, Width: {width}, Height: {height}");
engine.Resize(width, height);
var engine = _renderingService.GetOrCreateEngine(sessionId);
_logger.LogInformation($"Resize completed - SessionId: {sessionId}");
// 重置圖像緩存以強制重新渲染 return Task.CompletedTask;
if (_lastFrameCache.ContainsKey(sessionId)) }
{
_lastFrameCache.Remove(sessionId); /// <summary>
} /// 處理可見性變化
/// </summary>
engine.Resize(width, height); public Task HandleVisibilityChange(string visibilityState)
{
_logger.LogInformation($"Resize completed - SessionId: {sessionId}"); var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleVisibilityChange - SessionId: {sessionId}, State: {visibilityState}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> engine.IsVisible = visibilityState == "visible";
/// 處理可見性變化
/// </summary> return Task.CompletedTask;
public Task HandleVisibilityChange(string visibilityState) }
{
var sessionId = Context.ConnectionId; /// <summary>
_logger.LogInformation($"HandleVisibilityChange - SessionId: {sessionId}, State: {visibilityState}"); /// 設置視圖
/// </summary>
var engine = _renderingService.GetOrCreateEngine(sessionId); public Task SetView(string viewType)
engine.IsVisible = visibilityState == "visible"; {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"SetView called - SessionId: {sessionId}, ViewType: {viewType}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary>
/// 設置視圖 switch (viewType.ToLower())
/// </summary> {
public Task SetView(string viewType) case "front":
{ engine.SetViewToFrontView();
var sessionId = Context.ConnectionId; break;
_logger.LogInformation($"SetView called - SessionId: {sessionId}, ViewType: {viewType}"); case "back":
engine.SetViewToFrontView();
var engine = _renderingService.GetOrCreateEngine(sessionId); engine.TurnBackView();
break;
switch (viewType.ToLower()) case "left":
{ engine.SetViewToRightView();
case "front": engine.TurnBackView();
engine.SetViewToFrontView(); break;
break; case "right":
case "back": engine.SetViewToRightView();
engine.SetViewToFrontView(); break;
engine.TurnBackView(); case "top":
break; engine.SetViewToTopView();
case "left": break;
engine.SetViewToRightView(); case "bottom":
engine.TurnBackView(); engine.SetViewToTopView();
break; engine.TurnBackView();
case "right": break;
engine.SetViewToRightView(); case "isometric":
break; engine.SetViewToIsometricView();
case "top": break;
engine.SetViewToTopView(); case "home":
break; engine.SetViewToHomeView();
case "bottom": break;
engine.SetViewToTopView(); default:
engine.TurnBackView(); _logger.LogWarning($"Unknown view type: {viewType} - SessionId: {sessionId}");
break; break;
case "isometric": }
engine.SetViewToIsometricView();
break; _sketchViewCache[sessionId] = engine.SketchView;
case "home":
engine.SetViewToHomeView(); return Task.CompletedTask;
break; }
default:
_logger.LogWarning($"Unknown view type: {viewType} - SessionId: {sessionId}"); /// <summary>
break; /// 處理鍵盤按下事件
} /// </summary>
public Task HandleKeyDown(string key, string code, bool ctrlKey, bool shiftKey, bool altKey)
_sketchViewCache[sessionId] = engine.SketchView; {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleKeyDown - SessionId: {sessionId}, Key: {key}, Code: {code}, Ctrl: {ctrlKey}, Shift: {shiftKey}, Alt: {altKey}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary>
/// 處理鍵盤按下事件 // 使用 key 的 HashCode與 Blazor 版本一致)
/// </summary> long keyCode = key.ToLower().GetHashCode();
public Task HandleKeyDown(string key, string code, bool ctrlKey, bool shiftKey, bool altKey) engine.KeyDown(keyCode);
{
var sessionId = Context.ConnectionId; // 使用 KeyDownTransform 進行視圖變換
_logger.LogInformation($"HandleKeyDown - SessionId: {sessionId}, Key: {key}, Code: {code}, Ctrl: {ctrlKey}, Shift: {shiftKey}, Alt: {altKey}"); engine.KeyDownTransform(keyCode, new key_table__transform_view_by_key_pressing_t()
{
var engine = _renderingService.GetOrCreateEngine(sessionId); HOME = "home".ToLower().GetHashCode(),
PAGE_UP = "pageup".ToLower().GetHashCode(),
// 使用 key 的 HashCode與 Blazor 版本一致) PAGE_DOWN = "pagedown".ToLower().GetHashCode(),
long keyCode = key.ToLower().GetHashCode(); F1 = "f1".ToLower().GetHashCode(),
engine.KeyDown(keyCode); F2 = "f2".ToLower().GetHashCode(),
F3 = "f3".ToLower().GetHashCode(),
// 使用 KeyDownTransform 進行視圖變換 F4 = "f4".ToLower().GetHashCode(),
engine.KeyDownTransform(keyCode, new key_table__transform_view_by_key_pressing_t() SHIFT = "shift".ToLower().GetHashCode(),
{ ARROW_LEFT = "arrowleft".ToLower().GetHashCode(),
HOME = "home".ToLower().GetHashCode(), ARROW_RIGHT = "arrowright".ToLower().GetHashCode(),
PAGE_UP = "pageup".ToLower().GetHashCode(), ARROW_DOWN = "arrowdown".ToLower().GetHashCode(),
PAGE_DOWN = "pagedown".ToLower().GetHashCode(), ARROW_UP = "arrowup".ToLower().GetHashCode()
F1 = "f1".ToLower().GetHashCode(), });
F2 = "f2".ToLower().GetHashCode(),
F3 = "f3".ToLower().GetHashCode(), _sketchViewCache[sessionId] = engine.SketchView;
F4 = "f4".ToLower().GetHashCode(),
SHIFT = "shift".ToLower().GetHashCode(), return Task.CompletedTask;
ARROW_LEFT = "arrowleft".ToLower().GetHashCode(), }
ARROW_RIGHT = "arrowright".ToLower().GetHashCode(),
ARROW_DOWN = "arrowdown".ToLower().GetHashCode(), /// <summary>
ARROW_UP = "arrowup".ToLower().GetHashCode() /// 處理鍵盤釋放事件
}); /// </summary>
public Task HandleKeyUp(string key, string code, bool ctrlKey, bool shiftKey, bool altKey)
_sketchViewCache[sessionId] = engine.SketchView; {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleKeyUp - SessionId: {sessionId}, Key: {key}, Code: {code}, Ctrl: {ctrlKey}, Shift: {shiftKey}, Alt: {altKey}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary>
/// 處理鍵盤釋放事件 long keyCode = key.ToLower().GetHashCode();
/// </summary> engine.KeyUp(keyCode);
public Task HandleKeyUp(string key, string code, bool ctrlKey, bool shiftKey, bool altKey)
{ return Task.CompletedTask;
var sessionId = Context.ConnectionId; }
_logger.LogInformation($"HandleKeyUp - SessionId: {sessionId}, Key: {key}, Code: {code}, Ctrl: {ctrlKey}, Shift: {shiftKey}, Alt: {altKey}");
/// <summary>
var engine = _renderingService.GetOrCreateEngine(sessionId); /// 處理觸摸按下事件
/// </summary>
long keyCode = key.ToLower().GetHashCode(); public Task HandleTouchDown(int pointerId, double x, double y)
engine.KeyUp(keyCode); {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleTouchDown - SessionId: {sessionId}, PointerId: {pointerId}, X: {x}, Y: {y}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> engine.TouchDown(pointerId, (int)x, (int)y);
/// 處理觸摸按下事件
/// </summary> return Task.CompletedTask;
public Task HandleTouchDown(int pointerId, double x, double y) }
{
var sessionId = Context.ConnectionId; /// <summary>
_logger.LogInformation($"HandleTouchDown - SessionId: {sessionId}, PointerId: {pointerId}, X: {x}, Y: {y}"); /// 處理觸摸移動事件
/// </summary>
var engine = _renderingService.GetOrCreateEngine(sessionId); public Task HandleTouchMove(int pointerId, double x, double y)
engine.TouchDown(pointerId, (int)x, (int)y); {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogDebug($"HandleTouchMove - SessionId: {sessionId}, PointerId: {pointerId}, X: {x}, Y: {y}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> engine.TouchMove(pointerId, (int)x, (int)y);
/// 處理觸摸移動事件
/// </summary> return Task.CompletedTask;
public Task HandleTouchMove(int pointerId, double x, double y) }
{
var sessionId = Context.ConnectionId; /// <summary>
_logger.LogDebug($"HandleTouchMove - SessionId: {sessionId}, PointerId: {pointerId}, X: {x}, Y: {y}"); /// 處理觸摸釋放事件
/// </summary>
var engine = _renderingService.GetOrCreateEngine(sessionId); public Task HandleTouchUp(int pointerId)
engine.TouchMove(pointerId, (int)x, (int)y); {
var sessionId = Context.ConnectionId;
return Task.CompletedTask; _logger.LogInformation($"HandleTouchUp - SessionId: {sessionId}, PointerId: {pointerId}");
}
var engine = _renderingService.GetOrCreateEngine(sessionId);
/// <summary> engine.TouchUp(pointerId);
/// 處理觸摸釋放事件
/// </summary> return Task.CompletedTask;
public Task HandleTouchUp(int pointerId) }
{
var sessionId = Context.ConnectionId; /// <summary>
_logger.LogInformation($"HandleTouchUp - SessionId: {sessionId}, PointerId: {pointerId}"); /// 獲取瀏覽器滾輪縮放比例
/// </summary>
var engine = _renderingService.GetOrCreateEngine(sessionId); private double GetWheelScaling(string browserBrand)
engine.TouchUp(pointerId); {
switch (browserBrand.ToLower())
return Task.CompletedTask; {
} case "firefox":
return 1.0;
/// <summary> case "chrome":
/// 獲取瀏覽器滾輪縮放比例 case "edge":
/// </summary> case "safari":
private double GetWheelScaling(string browserBrand) default:
{ return 0.01;
switch (browserBrand.ToLower()) }
{ }
case "firefox":
return 1.0; /// <summary>
case "chrome": /// 客戶端斷開連接時清理資源
case "edge": /// </summary>
case "safari": public override async Task OnDisconnectedAsync(Exception exception)
default: {
return 0.01; var sessionId = Context.ConnectionId;
}
} // 清理緩存
_lastFrameCache.Remove(sessionId);
/// <summary> _sketchViewCache.Remove(sessionId);
/// 客戶端斷開連接時清理資源
/// </summary> _renderingService.RemoveEngine(sessionId);
public override async Task OnDisconnectedAsync(Exception exception) _logger.LogInformation($"Client disconnected: {sessionId}");
{ await base.OnDisconnectedAsync(exception);
var sessionId = Context.ConnectionId; }
}
// 清理緩存
_lastFrameCache.Remove(sessionId);
_lastFrameTime.Remove(sessionId);
_sketchViewCache.Remove(sessionId);
_renderingService.RemoveEngine(sessionId);
_logger.LogInformation($"Client disconnected: {sessionId}");
await base.OnDisconnectedAsync(exception);
}
}
} }