首页 > 解决方案 > 有没有其他人遇到过 CSS Grid 和奇数像素变暗单元格的故障?试图修复

问题描述

我需要创建一个动态大小的网格,可以缩放并保持其比例。这适用于游戏和拼图,因此请考虑 20 x 25 单元格并填充其容器的最短相对尺寸。在以非常直接的方式在 Firefox 中完美运行之后,我想在 Chrome 和 Edge 中对其进行测试(甚至不会尝试 IE)。就在那时,我在混合了背景和单元格内容的单元格之间发现了这些神秘的渲染线。那些线必须走,这是我的问题。

我将所有填充、网格间隙、边框等设置为 0。

以下是我所知道的:我预计会出现渲染故障,因此我的调整大小处理程序已经调整了 CSS 网格的大小,以四舍五入到最接近的整数大小(以像素为单位)单元格的整数。这让一切看起来都很干净。但是,当列数为奇数(如 21)并且单元格的宽度变为奇数(如 13px,因为总宽度为 273px)时,就会出现伪影。同样的事情在垂直方向上独立发生。这似乎是某种亚像素伪影,但我不知道为什么当所有尺寸都是整数像素数时会发生这种情况。如果我将单元格数设为 20 或 22,或调整网格大小使单元格尺寸为 12 或 14 像素,则没有伪影。似乎发生在所有奇数/奇数情况下。使用控制台检查器和屏幕截图来确认网格和单元格等的整数 px 大小。

请注意,无论是由主体还是另一个 DIV 元素包含,都会发生这种情况,并且不受任何父容器是否具有整个像素或 % 大小的影响。

标签: cssgridrenderpixelartifact

解决方案


在我把代码贴在这里之前......我没有意识到这是一个期望的“哎呀”,让我说我解决了一半的问题。要查看它的实际效果:

<!DOCTYPE html>
<html>
<head>
    <meta charset=utf-8>
    <!--meta name="viewport" content="width=device-width, user-scalable=no, 
minimum-scale=1.0, maximum-scale=1.0"-->
    <title>Blocks</title>
    <style>
        body
        {
            margin: 0px;
            position: relative; /*activate child positioning*/
            height: 100vh;      /*fill screen vertically*/
            background-color: rgb(64, 64, 64);
        }
        #consoleDiv
        {
            position: absolute;
            top: 50%;        /*center*/
            left: 50%;
            transform: translate(-50%,-50%);
        }
    </style>
</head>
<body>
    <div id="consoleDiv">
    </div>      
    <script>
        function blocks(tElement, bClr, fClr)
        {
            const GRID_ACROSS = 21;
            const GRID_DOWN = 24;
            const aspect = GRID_ACROSS/GRID_DOWN;
            const consoleDiv = document.querySelector("#" + tElement);
            consoleDiv.style.position = "absolute";
            consoleDiv.style.display = "grid";
            consoleDiv.style.gridTemplateColumns = "repeat(" + GRID_ACROSS + 
",1fr)";
            consoleDiv.style.gridTemplateRows = "repeat(" + GRID_DOWN + ",1fr)";
            
            for (let n = 1; n < GRID_DOWN + 1; n += 1)
            {
                for (let m = 1; m < GRID_ACROSS + 1; m += 1)
                {
                    newItem = document.createElement("DIV");
                    newItem.className = "gridCell";
                    newItem.style.backgroundColor = bClr;
                    newItem.style.backgroundColor = fClr;
                    newItem.style.gridArea = n + " / " + m;
                    consoleDiv.appendChild(newItem);
                };
            };
            
            let lastWidth = -1;
            let lastHeight = -1;
            return (
            function()
            {
                const body = document.getElementsByTagName("body")[0];
                const wNow = body.clientWidth;
                const hNow = body.clientHeight;
                if ((wNow != lastWidth) || (hNow != lastHeight))
                {
                    const dispAspect = wNow / hNow;
                    consoleDiv.style.height = "100%";
                    consoleDiv.style.width = "100%";
                    if (dispAspect > aspect)
                    {
                        consoleDiv.style.width = 100 * aspect / dispAspect + 
 "%";
                    }
                    else if (dispAspect < aspect)
                    {//space is too tall
                        consoleDiv.style.height = 100 * dispAspect / aspect + 
"%";
                    };
                    const cellWidthPx = Math.floor(consoleDiv.clientWidth / 
GRID_ACROSS);
                    const cellHeightPx = Math.floor(consoleDiv.clientHeight / 
GRID_DOWN);
                    const hTweak = 0;
                    //const hTweak = (GRID_ACROSS % 2) * (cellWidthPx % 2);
                    consoleDiv.style.width = GRID_ACROSS * cellWidthPx + hTweak 
+ "px";
                    consoleDiv.style.height = GRID_DOWN * cellHeightPx + "px";
                    lastWidth = wNow;
                    lastHeight = hNow;
                };
            });
        };
        
        animateBlocks = blocks("consoleDiv", "green", "pink");
        animate = function(timeStamp)
        {
            animateBlocks(timeStamp);
            requestAnimationFrame(animate);
        };
        requestAnimationFrame(animate);
    </script>
</body>
</html>

尝试调整大小(Chrome),然后在每个其他奇怪的大小“块”上,您应该会看到出现垂直线伪影。您可以使用控制台检查器检查网格是整个单元格的倍数,并且每个单元格都是整个像素。如果将 GRID_DOWN 更改为奇数,您也会看到水平伪影。边缘行为更糟糕——在奇数像素单元的偶数倍上也很差。在 Firefox 中,我看不到任何问题。

罪魁祸首似乎是 transform: translate centering 方法。它有点窒息,以使奇数个像素居中。我想这不是不合理的,但我永远不会选择他们的“解决方案”。

至少对我来说,解决方法是检查奇数单元格数和奇数单元格像素宽度。如果两者都是奇数,则添加到包含网格的 DIV 的 1px 侧边框可以解决此问题。要查看“修复”REM/unREM 定义 hTweak 的行。我还没有在 Edge 中测试过这个,但不久前我放弃了微软的产品。


推荐阅读