将两个 Vaadin 画布与 CSS 分层在一起



我有两个画布 - 用于加载图像的底部画布,以及用于绘制边界框等的顶部画布(意味着顶部画布具有所有鼠标侦听器等(。在我的主布局中,我创建了这两个画布(首先是底部画布(。这有效,我可以看到正在加载两个画布 - 但是,我需要将它们堆叠在一起而不是分开。当我在CSS文件中编辑位置时,我实际上可以看到两个画布被正确分层,但是下面留下了一个巨大的空白区域(画布的大小(,而我应该位于画布下方的按钮现在都位于页面的左上角。

这是我的主布局.java:

/*
 * Copyright 2000-2017 Vaadin Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.vaadin.starter.beveragebuddy.backend;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.dependency.HtmlImport;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MultiFileMemoryBuffer;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.Theme;
import com.vaadin.flow.theme.lumo.Lumo;
import com.vaadin.starter.beveragebuddy.service.UserService;
import com.vaadin.starter.beveragebuddy.ui.components.*;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import java.util.ArrayList;
/**
 * The main layout contains the header with the navigation buttons, and the
 * child views below that.
 */
@HtmlImport("frontend://styles/shared-styles.html")
@Route("")
@Theme(Lumo.class)
public class MainLayout extends Div {
    private CanvasRenderingContext2D ctx;
    private Canvas imgCanvas;
    private Canvas canvas;
    ArrayList<MousePosition> mousePosArray = Canvas.getMousePosArray();
    ArrayList<BoundingBox> bb = Canvas.getArrayBoxes();
    public static int count = 0;
    public MainLayout() {
        Div divlayout = new Div();
        H2 title = new H2("Annotation UI");
        title.addClassName("main-layout__title");
        imgCanvas = new Canvas(Canvas.imgOne, 0, 0, 1580, 700);
        canvas = new Canvas(1580, 700);
        imgCanvas.addClassName("imgCanvas");
        canvas.addClassName("canvas");
        ctx = canvas.getContext();
        add(imgCanvas);
        add(canvas);
        Div buttons = new Div();
//        MultiFileMemoryBuffer buffer = new MultiFileMemoryBuffer();
//        Upload upload = new Upload(buffer);
//        upload.setAcceptedFileTypes("image/jpeg", "image/png", "image/gif");
//
//        upload.addSucceededListener(event -> {
//            Component component = createComponent(event.getMIMEType(),
//                    event.getFileName(),
//                    buffer.getInputStream(event.getFileName()));
//            showOutput(event.getFileName(), component, output);
//        });
        Button saveButton = new Button("Save Annotations");
        saveButton.addClickListener( e-> {
            UserService service = new UserService();
            try {
                service.run();
            } catch (Exception e1) {
                e1.printStackTrace();
            }

        });
        buttons.add(saveButton);
        saveButton.addClassName("saveButton");
        Button previousButton = new Button("Previous Picture");
        previousButton.addClickListener(event -> { canvas.previousPicture();});
        buttons.add(previousButton);
        previousButton.addClassName("previousButton");
        Button nextButton = new Button("Next Picture");
        nextButton.addClickListener(event -> { canvas.nextPicture();});
        buttons.add(nextButton);
        nextButton.addClassName("nextButton");
        Button deleteButton = new Button("Delete Selected");
        deleteButton.addClickListener(event -> { canvas.deleteSelected();});
        buttons.add(deleteButton);
        deleteButton.addClassName("deleteButton");
        Button undoButton = new Button("Undo Canvas");
        undoButton.addClickListener(event -> { canvas.undoLast();});
        buttons.add(undoButton);
        undoButton.addClassName("undoButton");
        Button clearButton = new Button("Clear Canvas");
        clearButton.addClickListener(event -> { ctx.clearCanvas(Canvas.imgOne, 0,0,1580,700);});
        buttons.add(clearButton);
        clearButton.addClassName("clearButton");
        add(buttons);
        Label label = new Label();
        canvas.addComponent(label);
        add(label);
        TextField boxname = new TextField();
        boxname.setPlaceholder("Box Name");
        boxname.addClassName("boxname");
        TextField boxcategory = new TextField();
        boxcategory.setPlaceholder("Box Category");
        boxcategory.addClassName("boxcategory");
        add(boxname, boxcategory);
        ComboBox<String> coloursMenu = new ComboBox<>();
        coloursMenu.setPlaceholder("Box Colour");
        coloursMenu.setItems("Aqua", "Blue", "Black",
                "Green", "Magenta", "Orange", "Pink", "Red", "Turquoise", "Yellow");
        coloursMenu.addClassName("boxcolour");
        add(coloursMenu);
        Button submitButton = new Button("Submit");
        submitButton.addClickListener(event -> {
            bb.get(Canvas.boxCount - 1).setPicID(ImageCanvas.getImgOne());
            bb.get(Canvas.boxCount - 1).setName(boxname.getValue());
            bb.get(Canvas.boxCount - 1).setBoxcategory(boxcategory.getValue());
            bb.get(Canvas.boxCount - 1).setBoxcolour(coloursMenu.getValue());
            BoundingBox box = bb.get(Canvas.boxCount - 1);
            Canvas.boxStyle(box);
            boxname.clear();
            boxcategory.clear();
            coloursMenu.clear();
            System.out.println(bb.toString());
            System.out.println("Box Count: " + Canvas.boxCount);
        });
        add(submitButton);
        submitButton.addClassName("submitButton");
        canvas.addMouseMoveListener(() -> label.setText("Coordinates: " + mousePosArray.get(0)));
        label.addClassName("coordinates");
        }
    }

帆布.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.Image;
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.html.ImageElement;
import elemental.json.JsonObject;
import io.vertx.core.impl.Action;
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 Image image;
    public static int boxCount = 0;
    public static boolean undoCalled = false;
    public static int selectedBox = 0;
    public static boolean boxSelected = false;
    public static int imageArrayCount = 0;
    public static boolean imageLoaded = false;
    public static String imgOne = "https://wanderersandwarriors.com/wp-content/uploads/2018/03/Chinatown-Singapore-%e2%80%93-A-Tourist-Guide-3-1440x960.jpg";
    public static String imgTwo = "https://www.weekendnotes.com/im/006/02/orchard-road1.jpg";
    public static String imgThree = "http://static.asiawebdirect.com/m/phuket/portals/www-singapore-com/homepage/attractions/sentosa-island/pagePropertiesImage/sentosa-attractions.jpg.jpg";
    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<String> imageArray = new ArrayList<>();
    public static String getImgOne() {
        return imgOne;
    }
    public void setImgOne(String imgOne) {
        this.imgOne = imgOne;
    }
    public static String getImgTwo() {
        return imgTwo;
    }
    public void setImgTwo(String imgTwo) {
        this.imgTwo = imgTwo;
    }
    public static String getImgThree() {
        return imgThree;
    }
    public void setImgThree(String imgThree) {
        this.imgThree = imgThree;
    }
    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 #setBoxwidth(String)} or
     * {@link #setBoxheight(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(String src, double x, double y, int width, int height){
        context = new CanvasRenderingContext2D(this);
        element = getElement();
        element.getStyle().set("border", "1px solid");
        getElement().setAttribute("width", String.valueOf(width));
        getElement().setAttribute("height", String.valueOf(height));
        context.drawImage(imgOne, 0, 0);
    }
    public Canvas(int width, int height) {
        imageArray.add(imgOne);
        imageArray.add(imgTwo);
        imageArray.add(imgThree);
        context = new CanvasRenderingContext2D(this);
//        context.drawImage(imageArray.get(0), 0, 0);
        imageArrayCount = 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 x and y position
            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, 0.0, 0.0);
            arrayBoxes.add(newBox);
            mouseIsDown=true;
            mouseMoveListeners.forEach(Runnable::run);
        }).addEventData("event.x").addEventData("event.y");
        element.addEventListener("mouseup", event -> {  // Draws box + selection of boxes
            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));
            double xcoordi = 0;
            double ycoordi = 0;
            double boxWidth = 0;
            double boxHeight = 0;
            for (int i = boxCount; i < arrayBoxes.size(); i++) {
                arrayBoxes.get(boxCount).setEndX(endX);
                arrayBoxes.get(boxCount).setEndY(endY);
                if (arrayBoxes.get(i).getYcoordi() != arrayBoxes.get(i).getEndY()) {  // If startY and endY is the same, means user selected a box and not drew a box
                    arrayBoxes.get(boxCount).setBoxwidth(endX, arrayBoxes.get(boxCount).xcoordi);
                    arrayBoxes.get(boxCount).setBoxheight(endY, arrayBoxes.get(boxCount).ycoordi);
                    xcoordi = arrayBoxes.get(boxCount).getXcoordi();
                    ycoordi = arrayBoxes.get(boxCount).getYcoordi();
                    boxWidth = arrayBoxes.get(boxCount).getBoxwidth();
                    boxHeight = arrayBoxes.get(boxCount).getBoxheight();
                    boxCount++;
                    mouseIsDown = false;
                    context.beginPath();
                    context.setStrokeStyle("green");
                    context.setLineWidth(2);
                    context.strokeRect(xcoordi, ycoordi, boxWidth, boxHeight);
                    context.stroke();
//                    context.fill();
                } else {
                    if (arrayBoxes.size() > 0) {
                        arrayBoxes.remove(arrayBoxes.size() - 1);
                        mouseSelect = true;
                        SelectBox select = new SelectBox(endX, endY);
                        selectBoxes.add(0, select);
                    }
                }
            }
            if (mouseSelect == true) {
                for (int i = 0; i < arrayBoxes.size(); i++) {
                    if (arrayBoxes.get(i).getXcoordi() < selectBoxes.get(0).getSelectX() &&  selectBoxes.get(0).getSelectX() < arrayBoxes.get(i).getEndX())
                        if (arrayBoxes.get(i).getEndY() > selectBoxes.get(0).getSelectY() && selectBoxes.get(0).getSelectY() > arrayBoxes.get(i).getYcoordi()) {
                            System.out.println("Selected Box Name: " + arrayBoxes.get(i).boxname);
                            selectedBox = i;
                            boxSelected = true;
                            mouseSelect = false;
                     }
                     else {
                     mouseSelect = false;
                     }
                }
                context.beginPath();
                context.setStrokeStyle("yellow");
                context.setLineWidth(3);
                context.strokeRect(arrayBoxes.get(selectedBox).getXcoordi() - 2, arrayBoxes.get(selectedBox).getYcoordi() - 2, arrayBoxes.get(selectedBox).boxWidth + 4, arrayBoxes.get(selectedBox).boxHeight + 4);
                context.stroke();
//                context.fill();
            }

            System.out.println(arrayBoxes.toString());
            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 boxStyle(BoundingBox box) {  // Change box colour and add label (name, category) to top left corner
        context.setFillStyle(box.getBoxcolour());
        context.setFont("bold 10pt Arial");
        context.fillText(box.getBoxName(), box.getXcoordi() + 5, box.getYcoordi() - 5);
        context.setFillStyle(box.getBoxcolour());
        context.fillText(box.getBoxcategory(), box.getXcoordi() + 5, box.getYcoordi() + 15);
        context.beginPath();
        context.setStrokeStyle(box.getBoxcolour());
        context.setLineWidth(2);
        context.strokeRect(box.getXcoordi(), box.getYcoordi(), box.getBoxwidth(), box.getBoxheight());
        context.stroke();
        context.fill();
    }
    public static void deleteSelected() {  // Deletes the current box that has been selected
        arrayBoxes.remove(selectedBox);
        boxCount--;
        context.clearRect(0, 0, 1600, 800);
        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();
            BoundingBox box = arrayBoxes.get(i);
            Canvas.boxStyle(box);
        }
        System.out.println(arrayBoxes);

    }
    public void undoLast() {  // Removes the last drawn box from the canvas and the arrayBoxes array
        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(imageArray.get(0), 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();
//            BoundingBox box = getArrayBoxes().get(boxCount);
            BoundingBox box = arrayBoxes.get(i);
            Canvas.boxStyle(box);
        }
        boxCount--;
        System.out.println("Box Count: " + boxCount);
    }
    public void previousPicture(){
        context.clearCanvas(imageArray.get(0),0,0,1600,800);
    }
    public void nextPicture(){
        context.clearCanvas(imageArray.get(0), 0,0,1580,700);
    }
    public void setImage(String src){
        image = new Image();
        image.setSrc(src);
    }
    public void paintImage(){
        context.drawImage(imageArray.get(0), 0, 0);
    }
    /**
     * 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 setBoxwidth(String width) {
//        HasSize.super.setBoxwidth(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 setBoxheight(String height) {
//        HasSize.super.setBoxheight(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) {
    }
}

.CSS:

<!--
  ~ Copyright 2000-2017 Vaadin Ltd.
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License"); you may not
  ~ use this file except in compliance with the License. You may obtain a copy of
  ~ the License at
  ~
  ~ http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  ~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  ~ License for the specific language governing permissions and limitations under
  ~ the License.
  -->
<link rel="import" href="../bower_components/vaadin-lumo-styles/color.html">
<link rel="import" href="../bower_components/vaadin-lumo-styles/typography.html">
<dom-module id="view-styles">
    <template>
        <style>
            /* Stretch to fill the entire browser viewport while keeping the content constrained to
            parent element max-width */
            .view-toolbar {
                display: flex;
                background-color: var(--lumo-base-color);
                box-shadow: 0 1px 0 0 var(--lumo-contrast-10pct);
                margin: 0 calc(-50vw + 50%);
                padding: 8px calc(50vw - 50% + 16px);
                position: relative;
                z-index: 1;
                flex: none;
            }
            .view-toolbar__search-field {
                flex: auto;
                min-width: 0;
                margin-right: 16px;
            }
            .view-container {
                flex: auto;
            }
        </style>
    </template>
</dom-module>
<custom-style>
    <style include="view-styles">
        html {
            height: auto;
            --main-layout-header-height: 64px;
            background-color: transparent !important;
        }
        body {
            /* Avoid horizontal scrollbars, mainly on IE11 */
            overflow-x: hidden;
            background-color: var(--lumo-contrast-5pct);
        }
        .main-layout {
            display: flex;
            flex-direction: column;
            width: 100%;
            height: 100%;
            min-height: 100vh;
            max-width: 960px;
            margin: 0 auto;
        }
        .main-layout__title {
            font-size: 1em;
            margin: 0;
            /* Allow the nav-items to take all the space so they are centered */
            width: 0;
            line-height: 1;
            letter-spacing: -0.02em;
            font-weight: 500;
        }
        .main-layout > * {
            flex: auto;
        }
        .main-layout__header {
            display: flex;
            flex: none;
            align-items: center;
            height: var(--main-layout-header-height);
            /* Stretch to fill the entire browser viewport, while keeping the content constrained to
               parent element max-width */
            margin: 0 calc(-50vw + 50%);
            padding: 0 calc(50vw - 50% + 16px);
            background-color: var(--lumo-base-color);
            box-shadow: 0 1px 0 0 var(--lumo-contrast-5pct);
        }
        .main-layout__nav {
            display: flex;
            flex: 1;
            justify-content: center;
        }
        .main-layout__nav-item {
            display: inline-flex;
            flex-direction: column;
            align-items: center;
            padding: 4px 8px;
            cursor: pointer;
            transition: 0.3s color, 0.3s transform;
            will-change: transform;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            font-size: var(--lumo-font-size-s);
            color: var(--lumo-secondary-text-color);
            font-weight: 500;
            line-height: 1.3;
        }
        .main-layout__nav-item:hover {
            text-decoration: none;
        }
        .main-layout__nav-item:not([highlight]):hover {
            color: inherit;
        }
        .main-layout__nav-item[highlight] {
            color: var(--lumo-primary-text-color);
            cursor: default;
        }
        .main-layout__nav-item iron-icon {
            /* Vaadin icons are using a 16x16 grid */
            padding: 4px;
            box-sizing: border-box;
            pointer-events: none;
        }
        .imgCanvas {
            z-index: 0;
            position: absolute;
            top: 0px;
            left: 0px;
         }
        .canvas {
            cursor: crosshair;
            z-index: 10000;
            position: absolute;
            top: 0px;
            left: 0px;
        }
        .buttons {
            display: block;
            padding: 10px;
            position: absolute;
            top: 1600px;
        }
        .saveButton {
            position: relative;
            top: 1600px;
            z-index: 20000;
        }
        .boxname {
            padding: 6px;
            position: absolute;
            top: 1600px;
        }
        .boxcolour {
            padding: 6px;
            position: absolute;
            top: 1600px;
        }
        .submitButton {
            padding: 6px;
            padding-left: 6px;
            position: absolute;
            top: 1600px;
        }
        .coordinates {
            display: block;
            padding:8px;
        }
    </style>
    <dom-module id="my-dialog-styles" theme-for="vaadin-dialog-overlay">
        <template>
            <style include="lumo-color lumo-typography">
                h3 {
                    margin-top: 0;
                }
                vaadin-form-layout {
                    max-width: 30em;
                }
                .buttons {
                    padding: var(--lumo-space-s) var(--lumo-space-l);
                    margin: calc(var(--lumo-space-l) * -1);
                    margin-top: var(--lumo-space-l);
                    border-top: 1px solid var(--lumo-contrast-10pct);
                }
                .buttons > :last-child {
                    margin-left: auto;
                }
                .buttons > :nth-last-child(2) {
                    margin-right: var(--lumo-space-m);
                }
                .confirm-buttons {
                    justify-content: space-between;
                    padding: var(--lumo-space-xs) var(--lumo-space-m);
                    margin-top: var(--lumo-space-m);
                }
                .has-padding {
                    padding: 0 var(--lumo-space-l);
                    margin: 0 calc(var(--lumo-space-l) * -1);
                }
                .confirm-text {
                    max-width: 25em;
                    line-height: var(--lumo-line-height-s);
                }
                .confirm-text > * {
                    margin-bottom: 0.6em;
                }
                .confirm-text div:not(:first-child) {
                    color: var(--lumo-secondary-text-color);
                    font-size: var(--lumo-font-size-s);
                }
            </style>
        </template>
    </dom-module>
</custom-style>

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

我建议将两个画布都包装在div或其他元素中。

然后给那个包装元素position: relative,这意味着其中绝对定位的元素是相对于它的,而不是相对于页面或其他一些父元素。

绝对

定位的元素不占用空间,因此仅使第二个画布绝对,这样第一个画布仍将占用空间。

结构应该是这样的


Wrapper - position relative    
Canvas 1 - normal positioning    
Canvas 2 - absolute position, left 0 top 0 Rest of elements outside the wrapper

最新更新