首页 > 解决方案 > 如何在 Vaadin 画布上的框下方绘制图像?

问题描述

我目前有一个画布,在所有其他画布鼠标绘制功能进入(绘制框)之前,我将首先在其中绘制图像。我还有一个撤消功能,它将删除最后一个绘制的框,清除整个画布并将所有剩余的框重新绘制到画布上。但是,撤消后,图像会以某种方式出现在框的顶部并覆盖它们,而不是应该在下方。我以前可以在 HTML5 中使用 z-index 解决此问题,但不知道如何使用 Java 方式来解决此问题。

这是我的 Canvas.java(撤消方法接近尾声):

package com.vaadin.starter.beveragebuddy.ui.components;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasSize;
import com.vaadin.flow.component.HasStyle;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.dom.ElementFactory;
import com.vaadin.flow.shared.Registration;
import com.vaadin.starter.beveragebuddy.backend.MainLayout;
import elemental.json.JsonObject;
import java.util.ArrayList;
import java.util.List;

/**
 * Canvas component that you can draw shapes and images on. It's a Java wrapper
 * for the
 * <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API">HTML5
 * canvas</a>.
 * <p>
 * Use {@link #getContext()} to get API for rendering shapes and images on the
 * canvas.
 * <p>
 */
@Tag("canvas")
@SuppressWarnings("serial")
public class Canvas extends Component implements HasStyle, HasSize {

    private static CanvasRenderingContext2D context;
    private Element element;
    private boolean mouseSelect = false;
    private boolean mouseIsDown = false;
    private double endX;
    private double endY;
    public static int boxCount = 0;
    public static boolean undoCalled = false;

    public static ArrayList <BoundingBox> arrayBoxes = new ArrayList<BoundingBox>();
    public static ArrayList <MousePosition> mousePosArray = new ArrayList<MousePosition>();
    public static ArrayList <SelectBox> selectBoxes = new ArrayList<SelectBox>();
    private List<Runnable> mouseMoveListeners = new ArrayList<>(0);

    public static ArrayList<BoundingBox> getArrayBoxes() {
        return arrayBoxes;
    }

    public static ArrayList<MousePosition> getMousePosArray() {
        return mousePosArray;
    }

    public static void setMousePosArray(ArrayList<MousePosition> mousePosArray) {
        Canvas.mousePosArray = mousePosArray;
    }

    /**
     * Creates a new canvas component with the given size.
     * <p>
     * Use the API provided by {@link #getContext()} to render graphics on the
     * canvas.
     * <p>
     * The width and height parameters will be used for the canvas' coordinate
     * system. They will determine the size of the component in pixels, unless
     * you explicitly set the component's size with {@link #setWidth(String)} or
     * {@link #setHeight(String)}.
     *
//     * @param width
//     *            the width of the canvas
//     * @param height
//     *            the height of the canvas
//     */

    public Registration addMouseMoveListener(Runnable listener) {
        mouseMoveListeners.add(listener);
        return () -> mouseMoveListeners.remove(listener);
    }

    public Canvas(int width, int height) {

        context = new CanvasRenderingContext2D(this);

        context.drawImage("https://freedesignfile.com/upload/2016/10/Red-Clouds-and-Prairie-Background.jpg", 0, 0);

        element = getElement();
        element.getStyle().set("border", "1px solid");

        getElement().setAttribute("width", String.valueOf(width));
        getElement().setAttribute("height", String.valueOf(height));

        element.addEventListener("mousedown", event -> {  // Retrieve Starting Position on MouseDown

            Element boundingBoxResult = ElementFactory.createDiv();
            element.appendChild(boundingBoxResult);

            JsonObject evtData = event.getEventData();

            double xBox = evtData.getNumber("event.x");
            double yBox = evtData.getNumber("event.y");
            boundingBoxResult.setAttribute("data-x", String.format("%f", xBox));
            boundingBoxResult.setAttribute("data-y", String.format("%f", yBox));

            BoundingBox newBox = new BoundingBox("","", xBox, yBox, 0.0, 0.0);
            arrayBoxes.add(newBox);

            SelectBox select = new SelectBox(xBox, 0.0, yBox, 0.0);
            selectBoxes.add(0, select);

            mouseIsDown=true;

            mouseMoveListeners.forEach(Runnable::run);

        }).addEventData("event.x").addEventData("event.y");


        element.addEventListener("mouseup", event -> {  // Draw Box on MouseUp

            Element boundingBoxResult2 = ElementFactory.createDiv();
            element.appendChild(boundingBoxResult2);

            JsonObject evtData2 = event.getEventData();

            endX = evtData2.getNumber("event.x");
            endY = evtData2.getNumber("event.y");
            boundingBoxResult2.setAttribute("end-x", String.format("%f", endX));
            boundingBoxResult2.setAttribute("end-y", String.format("%f", endY));

//            System.out.println(endX);
//            System.out.println(endY);

            double xcoordi = 0;
            double ycoordi = 0;
            double boxWidth = 0;
            double boxHeight = 0;

//            for (int i = 0; i < arrayBoxes.size(); i++) {
                System.out.println(endX);
                System.out.println(endY);
                    arrayBoxes.get(boxCount).setWidth(endX, arrayBoxes.get(boxCount).xcoordi);
                    arrayBoxes.get(boxCount).setHeight(endY, arrayBoxes.get(boxCount).ycoordi);
                    xcoordi = arrayBoxes.get(boxCount).getXcoordi();
                    ycoordi = arrayBoxes.get(boxCount).getYcoordi();
                    boxWidth = arrayBoxes.get(boxCount).getWidth();
                    boxHeight = arrayBoxes.get(boxCount).getHeight();
                    boxCount++;


            mouseIsDown=false;

            context.beginPath();
            context.setStrokeStyle("green");
            context.setLineWidth(2);
            context.strokeRect(xcoordi, ycoordi, boxWidth, boxHeight);
            context.stroke();
            context.fill();



            SelectBox select = new SelectBox(endX, 0.0, endY, 0.0);
            selectBoxes.add(1, select);

//            if (selectBoxes.get(1).getSelectEndX() == selectBoxes.get(0).getSelectStartX()){
//                mouseSelect = true;
//                context.beginPath();
//                context.setStrokeStyle("yellow");
//                context.setLineWidth(2);
//                context.strokeRect(arrayBoxes.get(i).xcoordi, arrayBoxes.get(i).ycoordi, arrayBoxes.get(i).boxWidth, arrayBoxes.get(i).boxHeight);
//                context.fill();
//            }

            System.out.println(arrayBoxes.toString());
//
//            for (int i = 0; i < arrayBoxes.size(); i++){
//                if(arrayBoxes.get(i).xcoordi)
//                if (endX > arrayBoxes.get(i).xcoordi){
//                    if (endX < arrayBoxes.get(i).endY)
//                }
//            }

            mouseMoveListeners.forEach(Runnable::run);

        }).addEventData("event.x").addEventData("event.y");

        element.addEventListener("mousemove", event -> {  // Retrieve Mouse Position when Moving

                JsonObject mousePos = event.getEventData();

                double mouseX = mousePos.getNumber("event.x");
                double mouseY = mousePos.getNumber("event.y");

                MousePosition currentPos = new MousePosition(mouseX, mouseY);
                mousePosArray.add(0, currentPos);
                setMousePosArray(mousePosArray);
            mouseMoveListeners.forEach(Runnable::run);

     }).addEventData("event.x").addEventData("event.y");

    }


    public static void undoLast() {

        undoCalled = true;

        if (arrayBoxes.size() > 0) {
            arrayBoxes.remove(arrayBoxes.size() - 1);
        }
        System.out.println(arrayBoxes.toString());
        System.out.println(arrayBoxes.size());

        context.clearRect(0, 0, 1580, 700);
        context.drawImage("https://freedesignfile.com/upload/2016/10/Red-Clouds-and-Prairie-Background.jpg", 0, 0);

        for (int i = 0; i < arrayBoxes.size(); i++){
            context.beginPath();
            context.setStrokeStyle("green");
            context.setLineWidth(2);
            context.strokeRect(arrayBoxes.get(i).xcoordi, arrayBoxes.get(i).ycoordi, arrayBoxes.get(i).boxWidth, arrayBoxes.get(i).boxHeight);
            context.fill();
        }

        boxCount--;
        System.out.println("Box Count: " + boxCount);
    }

    /**
     * Gets the context for rendering shapes and images in the canvas.
     * <p>
     * It is a Java wrapper for the <a href=
     * "https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D">same
     * client-side API</a>.
     *
     * @return the 2D rendering context of this canvas
     */
    public CanvasRenderingContext2D getContext() {
        return context;
    }

    /**
     * {@inheritDoc}
     * <p>
     * <b>NOTE:</b> Canvas has an internal coordinate system that it uses for
     * drawing, and it uses the width and height provided in the constructor.
     * This coordinate system is independent of the component's size. Changing
     * the component's size with this method may scale/stretch the rendered
     * graphics.
     */
    @Override
    public void setWidth(String width) {
        HasSize.super.setWidth(width);
    }

    /**
     * {@inheritDoc}
     * <p>
     * <b>NOTE:</b> Canvas has an internal coordinate system that it uses for
     * drawing, and it uses the width and height provided in the constructor.
     * This coordinate system is independent of the component's size. Changing
     * the component's size with this method may scale/stretch the rendered
     * graphics.
     */
    @Override
    public void setHeight(String height) {
        HasSize.super.setHeight(height);
    }

    /**
     * {@inheritDoc}
     * <p>
     * <b>NOTE:</b> Canvas has an internal coordinate system that it uses for
     * drawing, and it uses the width and height provided in the constructor.
     * This coordinate system is independent of the component's size. Changing
     * the component's size with this method may scale/stretch the rendered
     * graphics.
     */
    @Override
    public void setSizeFull() {
        HasSize.super.setSizeFull();
    }

    public void addComponent(Label label) {
    }
}

非常感谢任何帮助,谢谢!

标签: javacanvasvaadindrawimage

解决方案


我相信您的问题是由这些问题之一或两者引起的

  1. 绘制图像时,您正确地等待它加载,然后再将其实际绘制到画布 ( image.onload = ...)。这意味着您的代码可能会开始加载图像,然后绘制所有框,然后加载图像以便将其绘制在顶部。

  2. 您在 上运行图像绘制脚本beforeClientResponse,这意味着它可能会在调用所有用于绘制框的代码之后被调用。

最简单的解决方案是,如果您总是希望将图像作为背景,则使用两个相互重叠的画布(例如,使用绝对定位)。这样,您始终可以将图像绘制到背景画布,并将所有框绘制到前景画布。这样做的好处是,即使您清除了前景画布,也不必重新绘制背景。


推荐阅读