update mouse indicator
This commit is contained in:
parent
3b44ea6d8d
commit
811a5821ba
|
|
@ -18,39 +18,13 @@
|
||||||
<i class="mdi mdi-restore icon-button"></i>
|
<i class="mdi mdi-restore icon-button"></i>
|
||||||
</button>
|
</button>
|
||||||
</MudTooltip>
|
</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>
|
||||||
<div @ref="ViewContainerRef" class="d-flex position-relative w-100 flex-grow-1 overflow-hidden">
|
<div @ref="ViewContainerRef">
|
||||||
<canvas @ref="CanvasRef"
|
<canvas @ref="CanvasRef"
|
||||||
@onwheel="HandleWheel"
|
@onwheel="HandleWheel"
|
||||||
@onwheel:preventDefault="true"
|
@onwheel:preventDefault="true"
|
||||||
@onmousemove="HandleMouseMove"
|
@onmousemove="HandleMouseMove"
|
||||||
@onmouseleave="HandleMouseLeave"
|
@onmouseleave="HandleMouseLeave"></canvas>
|
||||||
style="display: block; cursor: crosshair; transform: scale(1, -1)"></canvas>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -106,18 +80,96 @@
|
||||||
|
|
||||||
await ctx.ClearRectAsync(0, 0, CanvasWidth, CanvasHeight);
|
await ctx.ClearRectAsync(0, 0, CanvasWidth, CanvasHeight);
|
||||||
|
|
||||||
// Draw rulers first (outside transform)
|
|
||||||
await DrawRulers(ctx);
|
await DrawRulers(ctx);
|
||||||
|
|
||||||
await ctx.SaveAsync();
|
await ctx.SaveAsync();
|
||||||
|
|
||||||
await ctx.TranslateAsync(CanvasTranslateX, CanvasTranslateY);
|
await ctx.TranslateAsync(CanvasTranslateX, CanvasTranslateY);
|
||||||
await ctx.ScaleAsync(ZoomScale, ZoomScale);
|
await ctx.ScaleAsync(ZoomScale, ZoomScale);
|
||||||
|
|
||||||
await DrawGrid(ctx);
|
await DrawGrid(ctx);
|
||||||
await DrawAxes(ctx);
|
await DrawAxes(ctx);
|
||||||
|
|
||||||
await ctx.RestoreAsync();
|
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)
|
private async Task DrawRulers(Context2D ctx)
|
||||||
|
|
@ -264,18 +316,7 @@
|
||||||
|
|
||||||
private string FormatRulerLabel(double worldValue, double scaleInterval)
|
private string FormatRulerLabel(double worldValue, double scaleInterval)
|
||||||
{
|
{
|
||||||
if (scaleInterval < 1.0)
|
return scaleInterval < 1.0 ? $"{worldValue:F1}m" : $"{worldValue:F0}m";
|
||||||
{
|
|
||||||
return $"{worldValue:F1}m";
|
|
||||||
}
|
|
||||||
else if (scaleInterval < 10.0)
|
|
||||||
{
|
|
||||||
return $"{worldValue:F0}m";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $"{worldValue:F0}m";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DrawAxes(Context2D ctx)
|
private async Task DrawAxes(Context2D ctx)
|
||||||
|
|
@ -422,8 +463,8 @@
|
||||||
{
|
{
|
||||||
CanvasTranslateX += e.MovementX;
|
CanvasTranslateX += e.MovementX;
|
||||||
CanvasTranslateY -= e.MovementY;
|
CanvasTranslateY -= e.MovementY;
|
||||||
await DrawCanvas();
|
|
||||||
}
|
}
|
||||||
|
await DrawCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleMouseLeave(MouseEventArgs e)
|
private async Task HandleMouseLeave(MouseEventArgs e)
|
||||||
|
|
|
||||||
|
|
@ -30,144 +30,41 @@
|
||||||
background-color: var(--bs-gray-200);
|
background-color: var(--bs-gray-200);
|
||||||
}
|
}
|
||||||
|
|
||||||
.view .toolbar .action-button:hover {
|
.view .toolbar .action-button:hover {
|
||||||
background-color: var(--mud-palette-action-hover);
|
background-color: var(--mud-palette-action-hover);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.view .toolbar .action-button:disabled {
|
.view .toolbar .action-button:disabled {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view .toolbar .action-button:disabled:hover {
|
.view .toolbar .action-button:disabled:hover {
|
||||||
transform: none;
|
transform: none;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.view .toolbar .icon-button {
|
.view .toolbar .icon-button {
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
color: var(--mud-palette-primary);
|
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 {
|
.view > div > canvas {
|
||||||
cursor: crosshair;
|
transition: cursor 0.2s ease;
|
||||||
|
display: block;
|
||||||
|
transform: scale(1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enhanced zoom and coordinate info styling */
|
.view > div > canvas:hover {
|
||||||
.zoom-info-container {
|
cursor: crosshair;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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