From 811a5821ba2d4a1188da735dd4ce231603bbedf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=C4=83ng=20Nguy=E1=BB=85n?= Date: Thu, 2 Oct 2025 14:16:20 +0700 Subject: [PATCH] update mouse indicator --- .../Pages/Components/Mapping/MapView.razor | 143 ++++++++++------ .../Components/Mapping/MapView.razor.css | 155 +++--------------- RobotApp/Controllers/ImagesController.cs | 15 +- RobotApp/robot.db | Bin 167936 -> 167936 bytes 4 files changed, 126 insertions(+), 187 deletions(-) diff --git a/RobotApp.Client/Pages/Components/Mapping/MapView.razor b/RobotApp.Client/Pages/Components/Mapping/MapView.razor index 5900caf..df2d737 100644 --- a/RobotApp.Client/Pages/Components/Mapping/MapView.razor +++ b/RobotApp.Client/Pages/Components/Mapping/MapView.razor @@ -18,39 +18,13 @@ -
-
- - - - Zoom: @($"{ZoomScale:F2}x") - - | - - - Mouse: (@($"{MouseX:F0}"), @($"{MouseY:F0}")) - - | - - - World: (@($"{WorldMouseX:F2}m"), @($"{WorldMouseY:F2}m")) - - | - - - Translate: (@($"{CanvasTranslateX:F2}"), @($"{CanvasTranslateY:F2}")) - - -
-
-
+
+ @onmouseleave="HandleMouseLeave">
@@ -106,18 +80,96 @@ await ctx.ClearRectAsync(0, 0, CanvasWidth, CanvasHeight); - // Draw rulers first (outside transform) await DrawRulers(ctx); await ctx.SaveAsync(); await ctx.TranslateAsync(CanvasTranslateX, CanvasTranslateY); await ctx.ScaleAsync(ZoomScale, ZoomScale); - await DrawGrid(ctx); await DrawAxes(ctx); await ctx.RestoreAsync(); + + // Draw mouse indicator (outside transform) + if (IsMouseInCanvas) + { + await DrawMouseIndicator(ctx); + } + } + + private async Task DrawMouseIndicator(Context2D ctx) + { + await ctx.SaveAsync(); + + await ctx.StrokeStyleAsync("rgba(255, 50, 50, 0.8)"); + await ctx.LineWidthAsync(1); + await ctx.SetLineDashAsync(new double[] { 3, 3 }); + + await ctx.BeginPathAsync(); + await ctx.MoveToAsync(MouseX, RulerHeight); + await ctx.LineToAsync(MouseX, CanvasHeight); + await ctx.StrokeAsync(); + + await ctx.BeginPathAsync(); + await ctx.MoveToAsync(RulerHeight, MouseY); + await ctx.LineToAsync(CanvasWidth, MouseY); + await ctx.StrokeAsync(); + + await ctx.SetLineDashAsync(new double[] { }); + + const double labelPadding = 7; + const double labelMargin = 8; + + string coordinateText = $"({WorldMouseX:F2}m, {WorldMouseY:F2}m)"; + + await ctx.FontAsync("bold 12px Arial"); + var textMetrics = await ctx.MeasureTextAsync(coordinateText); + double textWidth = textMetrics.Width; + double textHeight = 16; + + double labelX = MouseX + labelMargin; + double labelY = MouseY - textHeight - labelPadding * 2 - labelMargin; + + if (labelX + textWidth + labelPadding * 2 > CanvasWidth) + { + labelX = MouseX - textWidth - labelPadding * 2 - labelMargin; + } + if (labelY - textHeight - labelPadding * 2 < RulerHeight) + { + labelY = MouseY + labelMargin; + } + + await ctx.FillStyleAsync("rgba(0, 0, 0, 0.8)"); + await ctx.FillRectAsync(labelX, labelY, textWidth + labelPadding * 2, textHeight + labelPadding * 2); + + await ctx.StrokeStyleAsync("rgba(255, 255, 255, 0.6)"); + await ctx.LineWidthAsync(1); + await ctx.StrokeRectAsync(labelX, labelY, textWidth + labelPadding * 2, textHeight + labelPadding * 2); + + await ctx.FillStyleAsync("rgba(255, 50, 50, 0.9)"); + await ctx.BeginPathAsync(); + await ctx.ArcAsync(MouseX, MouseY, 3, 0, Math.PI * 2); + await ctx.FillAsync(FillRule.NonZero); + + await ctx.StrokeStyleAsync("rgba(255, 255, 255, 0.8)"); + await ctx.LineWidthAsync(2); + await ctx.BeginPathAsync(); + await ctx.ArcAsync(MouseX, MouseY, 6, 0, Math.PI * 2); + await ctx.StrokeAsync(); + + await ctx.SaveAsync(); + + await ctx.TranslateAsync(labelX + labelPadding + textWidth / 2, labelY + textHeight / 2); + await ctx.ScaleAsync(1, -1); + + await ctx.FillStyleAsync("white"); + await ctx.FontAsync("bold 12px Arial"); + await ctx.TextAlignAsync(TextAlign.Center); + await ctx.TextBaseLineAsync(TextBaseLine.Bottom); + await ctx.FillTextAsync(coordinateText, 0, 0); + + await ctx.RestoreAsync(); } private async Task DrawRulers(Context2D ctx) @@ -137,14 +189,14 @@ { double pixelsPerMeter = BASE_PIXELS_PER_METER * ZoomScale; - if (pixelsPerMeter >= 400) return 0.1; + if (pixelsPerMeter >= 400) return 0.1; else if (pixelsPerMeter >= 200) return 0.2; else if (pixelsPerMeter >= 100) return 0.5; else if (pixelsPerMeter >= 50) return 1.0; else if (pixelsPerMeter >= 25) return 2.0; else if (pixelsPerMeter >= 12) return 5.0; else if (pixelsPerMeter >= 6) return 10.0; - else return 20.0; + else return 20.0; } private async Task DrawXRuler(Context2D ctx, double rulerHeight, double visibleWorldLeft, double visibleWorldRight, double scaleInterval) @@ -184,17 +236,17 @@ if (isMajorTick && canvasX >= -20 && canvasX <= CanvasWidth + 20) { await ctx.SaveAsync(); - + await ctx.TranslateAsync(canvasX, rulerHeight - tickHeight - 8); - await ctx.ScaleAsync(1, -1); - + await ctx.ScaleAsync(1, -1); + await ctx.FillStyleAsync("blue"); await ctx.FontAsync("bold 10px Arial"); await ctx.TextAlignAsync(TextAlign.Center); string labelText = FormatRulerLabel(worldX, scaleInterval); await ctx.FillTextAsync(labelText, 0, 0); - + await ctx.RestoreAsync(); } } @@ -240,9 +292,9 @@ await ctx.TranslateAsync(rulerWidth - tickWidth - 2, canvasY); await ctx.ScaleAsync(1, -1); - await ctx.RotateAsync(-Math.PI / 2); + await ctx.RotateAsync(-Math.PI / 2); - await ctx.FillStyleAsync("blue"); + await ctx.FillStyleAsync("blue"); await ctx.FontAsync("bold 10px Arial"); await ctx.TextAlignAsync(TextAlign.Center); @@ -264,18 +316,7 @@ private string FormatRulerLabel(double worldValue, double scaleInterval) { - if (scaleInterval < 1.0) - { - return $"{worldValue:F1}m"; - } - else if (scaleInterval < 10.0) - { - return $"{worldValue:F0}m"; - } - else - { - return $"{worldValue:F0}m"; - } + return scaleInterval < 1.0 ? $"{worldValue:F1}m" : $"{worldValue:F0}m"; } private async Task DrawAxes(Context2D ctx) @@ -422,8 +463,8 @@ { CanvasTranslateX += e.MovementX; CanvasTranslateY -= e.MovementY; - await DrawCanvas(); } + await DrawCanvas(); } private async Task HandleMouseLeave(MouseEventArgs e) diff --git a/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css b/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css index 2d9e089..deafb45 100644 --- a/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css +++ b/RobotApp.Client/Pages/Components/Mapping/MapView.razor.css @@ -30,144 +30,41 @@ background-color: var(--bs-gray-200); } - .view .toolbar .action-button:hover { - background-color: var(--mud-palette-action-hover); - transform: translateY(-1px); - } + .view .toolbar .action-button:hover { + background-color: var(--mud-palette-action-hover); + transform: translateY(-1px); + } - .view .toolbar .action-button:disabled { - opacity: 0.6; - cursor: not-allowed; - } + .view .toolbar .action-button:disabled { + opacity: 0.6; + cursor: not-allowed; + } - .view .toolbar .action-button:disabled:hover { - transform: none; - background-color: transparent; - } + .view .toolbar .action-button:disabled:hover { + transform: none; + background-color: transparent; + } .view .toolbar .icon-button { font-size: 1.2rem; color: var(--mud-palette-primary); } - .view canvas { - transition: cursor 0.2s ease; + + .view > div { + display: flex; + position: relative; + width: 100%; + flex-grow: 1; + overflow: hidden; } - .view canvas:hover { - cursor: crosshair; + .view > div > canvas { + transition: cursor 0.2s ease; + display: block; + transform: scale(1, -1); } -/* Enhanced zoom and coordinate info styling */ -.zoom-info-container { - display: flex; - align-items: center; - background-color: var(--mud-palette-background); - border: 1px solid var(--mud-palette-divider); - border-radius: 6px; - padding: 0.5rem 0.75rem; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); -} - -.zoom-info { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - font-size: 0.8rem; - color: var(--mud-palette-text-primary); - display: flex; - align-items: center; - gap: 0.5rem; - white-space: nowrap; -} - -.info-item { - display: flex; - align-items: center; - gap: 0.25rem; - font-weight: 500; -} - - .info-item i { - font-size: 0.9rem; - color: var(--mud-palette-primary); - } - -.info-separator { - color: var(--mud-palette-divider); - font-weight: 300; - margin: 0 0.25rem; -} - -/* Responsive design for info container */ -@media (max-width: 768px) { - .zoom-info-container { - padding: 0.25rem 0.5rem; - } - - .zoom-info { - font-size: 0.7rem; - gap: 0.25rem; - } - - .info-item { - gap: 0.15rem; - } - - .info-item i { - font-size: 0.8rem; - } -} - -/* Very small screens - stack vertically */ -@media (max-width: 480px) { - .zoom-info { - flex-direction: column; - gap: 0.15rem; - text-align: center; - } - - .info-separator { - display: none; - } - - .zoom-info-container { - padding: 0.35rem; - } -} - -/* Hover effects for info container */ -.zoom-info-container:hover { - background-color: var(--mud-palette-action-hover); - box-shadow: 0 4px 8px rgba(0,0,0,0.15); -} - -/* Animation for coordinate updates */ -.info-item { - transition: color 0.2s ease; -} - -.info-item:hover { - color: var(--mud-palette-primary); -} - -/* High contrast mode support */ -@media (prefers-contrast: high) { - .zoom-info-container { - border-width: 2px; - background-color: var(--mud-palette-surface); - } - - .info-item { - font-weight: 600; - } -} - -/* Dark mode adjustments */ -@media (prefers-color-scheme: dark) { - .zoom-info-container { - box-shadow: 0 2px 4px rgba(0,0,0,0.3); - } - - .zoom-info-container:hover { - box-shadow: 0 4px 8px rgba(0,0,0,0.4); - } -} + .view > div > canvas:hover { + cursor: crosshair; + } diff --git a/RobotApp/Controllers/ImagesController.cs b/RobotApp/Controllers/ImagesController.cs index 875c9ca..05c2a21 100644 --- a/RobotApp/Controllers/ImagesController.cs +++ b/RobotApp/Controllers/ImagesController.cs @@ -1,10 +1,11 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; -namespace RobotApp.Controllers +namespace RobotApp.Controllers; + +[Route("api/[controller]")] +[ApiController] +[AllowAnonymous] +public class ImagesController : ControllerBase { - [Route("api/[controller]")] - [ApiController] - public class ImagesController : ControllerBase - { - } } diff --git a/RobotApp/robot.db b/RobotApp/robot.db index d2792ed259f13d40d7066815b3677c054b0f50db..6249b2f3137fa2719f33f62889ab5f61546f69ea 100644 GIT binary patch delta 50 zcmZozz}2vTYl0LL3-d%7Cm^{opv6ZQ%m5HIAp^>SfiLrU}FZuRg@{HSm$uqGP F004s#4}1Us