汉堡图标:汉堡和面包大小一样,尽管像素对齐?

3

enter image description here

问题:中间的条比其他条高一个像素。这不是因为CSS出了问题,而是因为浏览器将矩形的边缘对齐到像素网格。
我尝试了几种不同的方法来制作汉堡图标:
1. 绝对定位几个带有背景颜色的div。 2. 内联SVG标签 3. SVG作为背景图像 4. 单个div,上下边框和背景颜色用于条,填充和background-clip: content-box; 用于空格。
所有这些方法都有同样的问题:
在某些缩放级别/大小下,图标看起来很糟糕,因为三个矩形大小不一。它们有时会因为浏览器喜欢将矩形的边缘与像素网格对齐而变得不同大小。
当然,我也希望两个矩形之间的空格厚度相同。
我知道这需要妥协,即图标必须是5px的倍数高。或者,如果您可以在交替断点处扩展空间一个像素,那么当您扩展条时,它可以是2X或3X不同的大小。

那么诀窍是什么?

一个Web字体是否可以使用hinting,使得无论大小/分辨率/缩放,所有三个矩形都获得相同数量的像素厚度?

我可以做某种CSS数学吗?

我真的希望不必使用JavaScript来尝试检测缩放级别的变化。

编辑:现场演示

var hamburgerIconEl = document.getElementById('hamburgerIcon');
var fontSizeEl = document.getElementById('fontSize');
fontSizeEl.addEventListener('input', function (e) {
    hamburgerIconEl.style.fontSize = '' + (8 + parseFloat(fontSizeEl.value) / 100) + 'px';
});
    .body {
        background: white;
    }
    #hamburgerIcon {
        box-sizing: border-box;
        display: inline-block;
        height: 1.7ex;
        width: 2.04ex;
        border-width: .34ex 0;
        border-style: solid;
        border-color: black;
        background-clip: content-box;
        padding: .34ex 0;
        background-color: black;
    }
<p>Change the font size, watch the hamburger icon, see how there are a lot of sizes in which the bars don't all have the same thickness and/or spacing.</p>
<div>
  <input type="range" id="fontSize" min="0" max="1000" value="0">
  <label for="volume">Font Size</label>
</div>
<div>
    <div id="hamburgerIcon" style="font-size: 8px"></div> Menu
</div>


1
你能贴出你的代码吗?这样我们才能帮到你。因为我们不知道你在使用什么工具。 - disinfor
我没有任何能够产生所需结果的代码。如果您知道一种方法,请发布它。 - JasonWoof
我已经添加了一个带有滑块的实时代码片段,可以缩放图标,这样您就可以看到它在某些大小上看起来很好,而在其他大小上看起来很糟糕。请注意,这取决于屏幕分辨率/缩放级别,因此我不能只选择在我的屏幕上好的尺寸并期望它在任何地方都看起来好。 - JasonWoof
显然,如果你已经有能够产生你想要结果的代码,你就不会寻求帮助 :) 我之前所要求的片段现在可以用来测试。 - disinfor
4个回答

3

现在,我还没有看到你的内联SVG,但问题可能是你将线条放得太靠近SVG边缘了吗?如果是这样,顶部和底部的一部分线条会因为被裁掉而显得更细。

var hamburgerIconEl = document.getElementById('hamburgerIcon');
var fontSizeEl = document.getElementById('fontSize');
fontSizeEl.addEventListener('input', function (e) {
  hamburgerIconEl.style.fontSize = '' + (8 + parseFloat(fontSizeEl.value) / 100) + 'px';
});
svg {
  height: 2em;
  width: 2em;
  font-size: 8px;
}
<div>
  <input type="range" id="fontSize" min="0" max="1000" value="0">
  <label for="volume">Font Size</label>
</div>
<div>
  <svg viewBox="0 0 10 10" id="hamburgerIcon">
    <path d="M 1 3 H 8 M 1 5 H 8 M 1 7 H 8" fill="none" stroke="black" stroke-width="1" />
  </svg>
</div>


哦,这是一个改进 :) 到目前为止,在 Chrome 中看起来最好。但我仍然希望找到一种自动使高度恰到好处的方法,以便不需要抗锯齿。 - JasonWoof

1

系统在处理部分像素时会遇到困难 - 在现代设备上,一个CSS像素可能对应多个屏幕像素。

当尝试显示部分像素时,必须决定是否将屏幕像素“留下”。

使用背景图像线性渐变的略微简化的绘制汉堡包的方法消除了中间条有时未出现在中心的问题。然而,偶尔会出现中间条与其他两条不完全相同高度的情况。

var hamburgerIconEl = document.getElementById('hamburgerIcon');
var fontSizeEl = document.getElementById('fontSize');
const label = document.querySelector('label');
fontSizeEl.addEventListener('input', function(e) {
  hamburgerIconEl.style.fontSize = '' + (8 + parseFloat(fontSizeEl.value) / 100) + 'px';
  label.innerHTML = 'Font Size = ' + hamburgerIconEl.style.fontSize;
});
#hamburgerIcon {
  display: inline-block;
  padding: 0;
  margin: 0;
  height: 1.7ex;
  width: 2.05ex;
  background-image: linear-gradient(black 0 20%, transparent 20% 40%, black 40% 60%, transparent 60% 80%, black 80% 100%);
}
<div>
  <input type="range" id="fontSize" min="0" max="1000" value="0">
  <label for="volume">Font Size</label>
</div>
<div id="hamburgerIcon"></div>


哦,部分得分是因为它比我的更简单和更对称。 - JasonWoof

0

这是一个非常棘手的问题。不同的浏览器以不同的方式舍入半像素,并且具有不同的缩放级别,因此很难设置一个在所有浏览器和缩放下都能良好缩放的大小。

如果您还没有找到答案,可以查看这个问题

您可以尝试使用<canvas>元素来绘制形状。根据我的经验,这种方法更加均匀地缩放。

例如:

const canvas = document.getElementById('canvas')
if (canvas.getContext) {
    var ctx = canvas.getContext('2d');
    ctx.fillStyle = 'green';
    ctx.fillRect(0, 0, 30, 1);
    ctx.clearRect(0, 1, 30, 2);
    ctx.fillRect(0, 3, 30, 1);    
    ctx.clearRect(0, 4, 30, 2);
    ctx.fillRect(0, 6, 30, 1);
  }
.container {
  padding: 10px 20px;
  display: block;
}

.burger {
  background-color: green;
  background-clip: content-box;
  padding-top: 2px;
  padding-bottom: 2px;
  border-top: 2px solid green;
  border-bottom: 2px solid green;
  height: 2px;
  width: 30px;
}
<div class="container">
  <h5>Div with bordrder</h5>
  <div class="burger one"></div>
</div>


<div class="container">
  <h5>Canvas</h5>
  <canvas id="canvas"></canvas>
</div>


0
这是一个JavaScript解决方案。在Chrome上,它对我来说完美地工作,但在我的iPad上以创造性的方式失败。

var hamburgerIconEl = document.getElementById('hamburgerIcon');
var fontSizeEl = document.getElementById('fontSizeSlider');
var dppxEl = document.getElementById('dppx');
function updateHamburgerIcon () {
    var height = parseFloat(getComputedStyle(hamburgerIconEl).height);
    var physPx = height * window.devicePixelRatio;
    var sparePhysPx = physPx % 5;
    var sparePx = sparePhysPx / window.devicePixelRatio;
    hamburgerIconEl.style.setProperty('--sparePx', '' + sparePx + 'px');
}
fontSizeEl.addEventListener('input', function (e) {
    hamburgerIconEl.parentElement.style.fontSize = '' + (8 + parseFloat(fontSizeEl.value) / 50) + 'px';
    updateHamburgerIcon();
});
updateHamburgerIcon();
function onResolutionChange (cb) {
    dppxEl.innerText = '' + window.devicePixelRatio;
    matchMedia('(resolution: ' + window.devicePixelRatio + 'dppx)')
        .addEventListener("change",
            function (e) {
                onResolutionChange(cb);
                cb(e);
            }, { once: true });
}
onResolutionChange(updateHamburgerIcon);
.body {
    background: white;
    color: black;
}
#fontSizeSlider {
    width: 100%;
}
#hamburgerIcon {
    display: inline-block;
    height: .75em;
    width: .88em;
    background-color: transparent;
    background-position: 50% 100%;
    background-repeat: no-repeat;
    background-image: linear-gradient(black 0 20%, transparent 20% 40%, black 40% 60%, transparent 60% 80%, black 80% 100%);
    --sparePx: 0px;
    background-size: auto calc(.75em - var(--sparePx));
}
<p>Try the size slider below, and also changing your zoom (current dppx: <span id="dppx"></span>)</p>
<div>
    <input type="range" id="fontSizeSlider" min="0" max="1000" value="0">
</div>
<div style="font-size: 8px">
    <div id="hamburgerIcon"></div> Menu
</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接