本文转载自 张鑫旭博客 原文链接:Chrome opacity非1时border-radius圆角边框剪裁问题

大家都知道把元素设置 border-radius:50% 可以让正方形元素变为原型,如果元素设置了 border 属性,则会变现为一个圆,类似这样:

但有时候,border边框的这个圈圈会在边缘处发生剪裁,不是一个完美的圈圈了,类似下面这样:

圈圈的两侧不是圆的了,而是像被什么东西裁剪了一下,变成了直的。

出现这种渲染问题,需要满足下面两个条件:

  1. 元素的透明度opacity不是1;
  2. 元素的位置并不是完美起止于屏幕的像素点上;

第一个条件很好理解,第二个条件是什么意思呢?

大家都知道普通显示器的最小显示单位是像素,如果某一个元素的起点是从0.5像素开始的,那这个元素的开始位置就不是正好在屏幕的像素点上,而是中间。

但是有人会问既然最小显示单位是像素,又何来从0.5像素开始的定位呢?其实在原来所有的设置为小数的CSS属性最终表现的时候都会是以整数表现出来(例: margin:.5px=margin:1px );

但是进入CSS3的时代,随着各种效果兴起,尤其是 transform 变换。由于 transform 变换基于矩阵计算,无论是旋转还是拉伸,其点坐标十有八九一定是N位数的小数。如果此时我们的浏览器按照最小的1像素开始渲染,那图形的边缘那就是满满的锯齿,渲染效果会非常糟糕(一开始的 transform 变换效果真就这么糟糕)。浏览器厂商一看,我去,这效果挫到我自己都看不下去,于是下功夫开始提升渲染体验。弄了一套算法,对非整数像素点边缘进行柔化,也就是虽然还是占据的1像素的格子,但是边缘像素点通过半透明等特殊处理,我们视觉上看就好像元素开始于0.5像素的位置。

如下代码重现上述圆圈被裁剪现象:

<span class="example"></span>
.example {
    display: inline-block;
    width: 40px; height: 40px;
    border: 1px solid #fff;
    border-radius: 50%;
    transform: translate(.5px, 0);
    opacity: .5;
}

当然你也可以利用 margin或者其他任何可以实现的方式使元素不是完美起止于屏幕的像素点上。

了解了出现问题的原因,接下来就是解决问题,针对出现原因列出两个解决方向:

  1.元素尺寸和位置都设置整数像素

  但是这方案显然是不合理的,因为实际开发中想要强制元素尺寸和位置都是整数并不太容易。

  2.不使用opacity实现透明度效果

   如果我们想使一个元素的背景或者边框变成半透明,除了 opacity外,我们还可以使用RGBA或者HSLA颜色。

所以CSS代码我们可以这样写:

.example {
    display: inline-block;
    width: 40px; height: 40px;
    border: 1px solid rgba(0,0,0,.3);
    border-radius: 50%;
    transform: translate(.5px, 0);
    background: rgba(255,255,255,.3);
}