холст, установленный на clientWidth, не сжимается при изменении размера соседнего элемента - PullRequest
0 голосов
/ 17 июня 2020

У меня есть веб-приложение с пользовательским интерфейсом на основе сетки CSS. В одном из разделов сетки есть складная боковая панель и элемент холста, размер которого я хочу всегда изменять, чтобы заполнить оставшееся пространство. Холст обновляет каждый кадр, поэтому я устанавливаю canvas.width на clientWidth div-оболочки в каждом кадре.

Холст правильно расширяется, чтобы заполнить пространство, когда панель свернута, но когда панель снова открывается, холст не сжимается, а вместо этого выходит за пределы страницы. У меня есть несколько теорий о том, почему это может происходить, например, об отказе div обертки сжиматься после расширения холста, но я не могу понять, как избавиться от этой проблемы.

Вот простой макет проблемы:

<meta charset="utf-8" />

<script>
    let open = true;

    window.onload = () => {
        let canvas = document.getElementById("canvas");

        canvasUpdate(canvas);
    }

    function canvasUpdate(canvas){
        let ctx = canvas.getContext("2d");

        canvas.width = document.getElementById("canvasWrapper").clientWidth;
        canvas.height = document.getElementById("canvasWrapper").clientHeight;

        ctx.fillStyle = "gray";
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, 50, 50);

        ctx.fillStyle = "black";
        ctx.fillRect(canvas.width - 50, canvas.height - 50, 50, 50);

        window.requestAnimationFrame(()=>{canvasUpdate(canvas)});
    }

    function togglePanel(){
        if (open){
            document.getElementById("sidePanel").style.display = "none";
            document.getElementById("expandBtn").style.display = "block";
            open = false;
        }
        else{
            document.getElementById("sidePanel").style.display = "flex";
            document.getElementById("expandBtn").style.display = "none";
            open = true;
        }
    }
</script>

<style>
    html, body{
        margin: 0;
        padding: 0;
    }

    #gridWrapper{
        display: grid;
        grid-template-areas: 
            "header header"
            "col1 col2";
        grid-template-columns: 100pt 1fr;
        grid-template-rows: 50pt minmax(0, 1fr);
        width: 100%;
        height: 100%;
    }

    #header{
        grid-area: header;
        background: #AAFFAA;
    }

    #col1{
        grid-area: col1;
        background: #FFAAAA;
    }

    #col2{
        grid-area: col2;
        display: flex;
        flex-direction: row;
    }

    #sidePanel{
        display: flex;
        flex-direction: row;
        height: 100%;
    }

    #expandBtn{
        display: none;
        height: 100%;
    }

    #canvasWrapper{
        width: 100%;
        height: 100%;
    }
</style>

<body>
    <div id="gridWrapper">
        <div id="header">
            Header contents
        </div>
        <div id="col1">
            Col1 contents
        </div>
        <div id="col2">
            <div id="sidePanel">
                <div id="panelContents">
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                </div>
                <button id="collapseBtn" onclick="togglePanel()">
                    &lt;
                </button>
            </div>
            <button id="expandBtn" onclick="togglePanel()">
                &gt;
            </button>
            <div id="canvasWrapper">
                <canvas id="canvas">
                    //Error loading canvas
                </canvas>
            </div>
        </div>
    </div>
</body>

, если я установил сетку overflow = hidden, он решит проблему запуска страницы, но холст по-прежнему остается расширенной шириной.

1 Ответ

0 голосов
/ 17 июня 2020

Я разобрался. похоже, что элемент уровня блока не сжимается до размера меньше, чем элемент холста, независимо от того, какие CSS настройки вы установили, но, к счастью, я нашел обходной путь, временно установив холст w / h на 1, затем , получив и сохраняя размеры оболочки, прежде чем окончательно установить холст в сохраненные размеры.

    let wrapperBounds = {width:0,height:0};

    canvas.width = 1;
    canvas.height = 1;

    wrapperBounds.width = document.getElementById("canvasWrapper").clientWidth;
    wrapperBounds.height = document.getElementById("canvasWrapper").clientHeight;

    canvas.width = wrapperBounds.width;
    canvas.height = wrapperBounds.height;

Я изначально не думал, что это сработает, так как я решил, что автоматически c ширина была рассчитана в другой точке в жизненном цикле, чем requestAnimationFrame(), но очевидно, что CSS рассчитывается сразу после изменения.

Полная реализация кода:

<meta charset="utf-8" />

<script>
    let open = true;

    window.onload = () => {
        let canvas = document.getElementById("canvas");

        canvasUpdate(canvas);
    }

    function canvasUpdate(canvas){
        let ctx = canvas.getContext("2d");
        let wrapperBounds = {width:0,height:0};

        canvas.width = 1;
        canvas.height = 1;

        wrapperBounds.width = document.getElementById("canvasWrapper").clientWidth;
        wrapperBounds.height = document.getElementById("canvasWrapper").clientHeight;

        canvas.width = wrapperBounds.width;
        canvas.height = wrapperBounds.height;

        ctx.fillStyle = "gray";
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, 50, 50);

        ctx.fillStyle = "black";
        ctx.fillRect(canvas.width - 50, canvas.height - 50, 50, 50);

        window.requestAnimationFrame(()=>{canvasUpdate(canvas)});
    }

    function togglePanel(){
        if (open){
            document.getElementById("sidePanel").style.display = "none";
            document.getElementById("expandBtn").style.display = "block";
            open = false;
        }
        else{
            document.getElementById("sidePanel").style.display = "flex";
            document.getElementById("expandBtn").style.display = "none";
            open = true;
        }
    }
</script>

<style>
    html, body{
        margin: 0;
        padding: 0;
    }

    #gridWrapper{
        display: grid;
        grid-template-areas: 
            "header header"
            "col1 col2";
        grid-template-columns: 100pt 1fr;
        grid-template-rows: 50pt minmax(0, 1fr);
        width: 100%;
        height: 100%;
    }

    #header{
        grid-area: header;
        background: #AAFFAA;
    }

    #col1{
        grid-area: col1;
        background: #FFAAAA;
    }

    #col2{
        grid-area: col2;
        display: flex;
        flex-direction: row;
    }

    #sidePanel{
        display: flex;
        flex-direction: row;
        height: 100%;
    }

    #expandBtn{
        display: none;
        height: 100%;
    }

    #canvasWrapper{
        width: 100%;
        height: 100%;
    }
</style>

<body>
    <div id="gridWrapper">
        <div id="header">
            Header contents
        </div>
        <div id="col1">
            Col1 contents
        </div>
        <div id="col2">
            <div id="sidePanel">
                <div id="panelContents">
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                    <div class="contents">Contents</div>
                </div>
                <button id="collapseBtn" onclick="togglePanel()">
                    &lt;
                </button>
            </div>
            <button id="expandBtn" onclick="togglePanel()">
                &gt;
            </button>
            <div id="canvasWrapper">
                <canvas id="canvas">
                    //Error loading canvas
                </canvas>
            </div>
        </div>
    </div>
</body>
...