update mouse indicator
This commit is contained in:
parent
3b44ea6d8d
commit
811a5821ba
|
|
@ -18,39 +18,13 @@
|
|||
<i class="mdi mdi-restore icon-button"></i>
|
||||
</button>
|
||||
</MudTooltip>
|
||||
<div class="ms-auto d-flex align-items-center">
|
||||
<div class="zoom-info-container">
|
||||
<small class="zoom-info">
|
||||
<span class="info-item">
|
||||
<i class="mdi mdi-magnify"></i>
|
||||
Zoom: @($"{ZoomScale:F2}x")
|
||||
</span>
|
||||
<span class="info-separator">|</span>
|
||||
<span class="info-item">
|
||||
<i class="mdi mdi-crosshairs-gps"></i>
|
||||
Mouse: (@($"{MouseX:F0}"), @($"{MouseY:F0}"))
|
||||
</span>
|
||||
<span class="info-separator">|</span>
|
||||
<span class="info-item">
|
||||
<i class="mdi mdi-map-marker"></i>
|
||||
World: (@($"{WorldMouseX:F2}m"), @($"{WorldMouseY:F2}m"))
|
||||
</span>
|
||||
<span class="info-separator">|</span>
|
||||
<span class="info-item">
|
||||
<i class="mdi mdi-map-marker"></i>
|
||||
Translate: (@($"{CanvasTranslateX:F2}"), @($"{CanvasTranslateY:F2}"))
|
||||
</span>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div @ref="ViewContainerRef" class="d-flex position-relative w-100 flex-grow-1 overflow-hidden">
|
||||
<div @ref="ViewContainerRef">
|
||||
<canvas @ref="CanvasRef"
|
||||
@onwheel="HandleWheel"
|
||||
@onwheel:preventDefault="true"
|
||||
@onmousemove="HandleMouseMove"
|
||||
@onmouseleave="HandleMouseLeave"
|
||||
style="display: block; cursor: crosshair; transform: scale(1, -1)"></canvas>
|
||||
@onmouseleave="HandleMouseLeave"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue
Block a user