Quantcast
Channel: Active questions tagged blazor - Stack Overflow
Viewing all articles
Browse latest Browse all 4839

Issues with Positioning Objects on an Image in a Blazor Component

$
0
0

I'm working on a Blazor project where I need to place objects (like bread, watermelon) on a background image. The problem is that when I move the objects, they appear underneath the image rather than on top of it where I want them. Here's an overview of my implementation and the problem:

Problem

I have a Blazor component where users can drag and drop objects onto a background image. However, the objects are not placed directly on the image but seem to appear underneath it when moved. I want the objects to be positioned exactly on the image, not beneath it.

Code

Here is the relevant part of my code:

@page "/planDisplay"@using siteplan_Projekt.Data@inject FileService fileService@inject IJSRuntime JsRuntime@rendermode InteractiveServer<PageTitle>Plan-Display</PageTitle>@if (errors.Count > 0){<ul class="text-danger">        @foreach (var error in errors)        {<li>@error</li>        }</ul>}<!-- siteplan-Projekt/Components/Pages/SitePlan/PlanDisplay.razor --><MudDropContainer T="DropItem" Items="_items" ApplyDropClassesOnDragStarted="_applyDropClassesOnDragStarted" ItemsSelector="@((item, dropzone) => item.Place == dropzone)" ItemDropped="ItemUpdated" Class="d-flex flex-column flex-grow-1"><ChildContent><div class="d-flex flex-wrap justify-space-between" @onmousemove="OnMouseMove"><MudDropZone T="DropItem" Identifier="ObjBox" CanDrop="@(item => false)" Class="rounded-lg border-2 border-solid mud-border-lines-default pa-6 ma-8"><MudText Typo="Typo.h6" Class="mb-4">Objekt</MudText></MudDropZone><div class="image-wrapper" style="position: relative; width: 800px; height: 600px;"><MudDropZone T="DropItem" Identifier="Image" CanDrop="@(item => item.IsPicked == false && item.IsRotten == false)" Class="site-plan-dropzone"><img src="@imageUpload[0].Url" alt="Site plan" class="site-plan" @onclick="onImageClick" style="width: 100%; height: 100%;"/></MudDropZone><svg class="grid-overlay" xmlns="http://www.w3.org/2000/svg" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">                    @if (selectedPoints.Count == 1 && tempPoint != null)                    {<line x1="@selectedPoints[0].X" y1="@selectedPoints[0].Y" x2="@tempPoint.Value.X" y2="@tempPoint.Value.Y" stroke="red" stroke-width="3"/>                    }                    @if (selectedPoints.Count == 2)                    {<line x1="@selectedPoints[0].X" y1="@selectedPoints[0].Y" x2="@selectedPoints[1].X" y2="@selectedPoints[1].Y" stroke="red" stroke-width="3"/>                    }                    @if (selectedPoints.Count <= 2)                    {                    @foreach (var point in selectedPoints)                    {<circle cx="@point.X" cy="@point.Y" r="5" fill="blue"></circle>                    }                    }</svg></div></div></ChildContent><ItemRenderer><MudPaper Height="54px" Width="54px" Class="pa-2 icon-over-image" Elevation="0"><MudIcon Icon="@context.Icon"/></MudPaper></ItemRenderer></MudDropContainer>@if (selectedPoints.Count == 2){<MudItem xs="12"><MudTextField @bind-Value="realDistance" Label="Real Distance (meters)"/><MudButton OnClick="calculateScale">Calculate Scale</MudButton></MudItem>}<MudItem xs="12"><MudText>Scale: @scaleManager.getScale() pixels per meter</MudText></MudItem><MudToolBar><MudSpacer/><MudButton OnClick="Reset">Reset</MudButton></MudToolBar>
using System.Drawing;using Microsoft.AspNetCore.Components;using Microsoft.AspNetCore.Components.Forms;using Microsoft.AspNetCore.Components.Web;using Microsoft.JSInterop;using MudBlazor;using siteplan_Projekt.Data;using siteplan_Projekt.Models;namespace siteplan_Projekt.Components.Pages.SitePlan;/// <summary>///     This class is responsible for displaying the plan.///     It initializes and manages the display of uploaded images,///     and handles rendering behavior to ensure proper initialization./// </summary>public partial class PlanDisplay{    #region Protected    /// <summary>    ///     Called when the component is initialized.    ///     Populates the `imageUpload` list with files retrieved from the `fileService`.    /// </summary>    protected override void OnInitialized()    {        try        {            imageUpload = fileService.GetUploadedFiles();            var dimensions = fileService.GetImageDimensions(imageUpload[0].Name);            imageWidth = dimensions.width;            imageHeight = dimensions.height;        }        catch (Exception e)        {            Console.WriteLine($"Error: {e.Message}");        }    }    /// <summary>    ///     Asynchronously called after the component has rendered.    ///     Executes actions that should only occur during the first render.    ///     If `firstRender` is true and `isFirstRender` is true,    ///     sets `isFirstRender` to `false` to prevent the first render    ///     logic from running again.    /// </summary>    /// ///    /// <param name="CamBox">    ///     True if this is the first time the component is rendered;    ///     false if it has been rendered before.    /// </param>    protected override async Task OnAfterRenderAsync(bool firstRender)    {        if (firstRender && isFirstRender) isFirstRender = false;    }    #endregion    #region Private    private readonly List<PointF> selectedPoints = new();    private readonly Dictionary<string, object> svgAttributes = new();    private readonly List<PointF> objectPoints = new();    /// <summary>    ///     A list to store any errors encountered during processing.    /// </summary>    private readonly List<string> errors = new();    private readonly bool _applyDropClassesOnDragStarted = false;    private readonly List<DropItem> _items = new()    {        new DropItem { Icon = Icons.Custom.Uncategorized.ChessKing, IsRotten = false, Place = "ObjBox" },        new DropItem { Icon = Icons.Custom.Uncategorized.Baguette, IsRotten = false, Place = "ObjBox" },        new DropItem { Icon = Icons.Custom.Uncategorized.Sausage, IsRotten = true, Place = "ObjBox" },        new DropItem { Icon = Icons.Custom.Uncategorized.WaterMelon, IsRotten = false, Place = "ObjBox" },        new DropItem { Icon = Icons.Custom.Uncategorized.Fish, IsRotten = true, Place = "ObjBox" }    };    private PointF? tempPoint { get; set; }    private string? imageUrl { get; set; }    private double realDistance { get; set; }    [Inject] private ScaleManager scaleManager { get; set; }    private IBrowserFile? iBrowserFile;    private int imageWidth;    private int imageHeight;    /// <summary>    ///     Indicates whether this is the first time the component is rendering.    ///     Initially set to true, it will be set to false after the first render.    /// </summary>    private bool isFirstRender = true;    /// <summary>    ///     A list of image upload services used to manage uploaded images.    /// </summary>    private List<UploadedImageService> imageUpload = new();    private async Task onImageClick(MouseEventArgs e)    {        try        {            if (selectedPoints.Count < 2)            {                selectedPoints.Add(new PointF((float)e.OffsetX, (float)e.OffsetY));            }            else            {                selectedPoints.RemoveAt(0);                selectedPoints.Add(new PointF((float)e.OffsetX, (float)e.OffsetY));            }            if (selectedPoints.Count == 2)            {                // Calculate the pixel distance between the two points                var pixelDistance = calculatePixelDistance(selectedPoints[0], selectedPoints[1]);                // Output the points for debugging purposes                await JsRuntime.InvokeVoidAsync("console.log",                    $"Point 1: {selectedPoints[0]}, Point 2: {selectedPoints[1]}, Distance: {pixelDistance}px");            }            objectPoints.Add(new PointF((float)e.OffsetX, (float)e.OffsetY));            StateHasChanged();        }        catch (Exception ex)        {            await JsRuntime.InvokeVoidAsync("console.error", $"Error: {ex.Message}");        }    }    private double calculatePixelDistance(PointF point1, PointF point2)    {        return Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));    }    private void calculateScale()    {        if (selectedPoints.Count == 2 && realDistance > 0)        {            var pixelDistance = calculatePixelDistance(selectedPoints[0], selectedPoints[1]);            scaleManager.scale = pixelDistance / realDistance;        }    }    private async Task onFileSelected(IBrowserFile file)    {        iBrowserFile = file;        var buffer = new byte[iBrowserFile.Size];        await iBrowserFile.OpenReadStream().ReadAsync(buffer);        // Convert the uploaded image to a base64 string        imageUrl = $"data:image/png;base64,{Convert.ToBase64String(buffer)}";    }    private void OnMouseMove(MouseEventArgs e)    {        if (selectedPoints.Count == 1)        {            tempPoint = new PointF((float)e.OffsetX, (float)e.OffsetY);            StateHasChanged();        }    }    private void Reset()    {        foreach (var item in _items)        {            item.Place = "ObjBox";            item.IsPicked = false;        }    }    private void ItemUpdated(MudItemDropInfo<DropItem> dropItem)    {        dropItem.Item.IsPicked = true;        dropItem.Item.Place = dropItem.DropzoneIdentifier;    }    #endregion}public class DropItem{    #region Public    public string Icon { get; init; }    public bool IsRotten { get; set; }    public bool IsPicked { get; set; }    public string Place { get; set; }    #endregion}

Questions

  1. Why are the objects appearing underneath the image rather than directly on it?

  2. How can I ensure that the objects are correctly positioned on the image?

I would greatly appreciate any help or guidance on how to resolve this issue. Thanks in advance!


Viewing all articles
Browse latest Browse all 4839

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>