CSS转换效果在Chrome中使图像模糊/移动图像1px?

172

我有一些CSS,当鼠标悬停时,CSS过渡效果将移动一个div。

问题在于,正如你在示例中所看到的那样,translate过渡效果会产生可怕的副作用,使得div中的图像向下/向右移动1px(可能还会微微缩小),以至于它看起来不协调、模糊不清...

从试错的过程来看,这个故障似乎在整个悬停效果应用期间都存在,并且只在translate过渡效果移动div时才会出现(当去除box shadow和opacity时,也不会对错误产生影响)。

该问题似乎仅在页面具有滚动条时发生。因此,只有一个div实例的示例是正常的,但一旦添加了更多相同的div并且页面因此需要滚动条,问题就再次出现...


1
我在OSX上使用Chrome 27,一切正常。我相信当内容被放入图层时,在动画期间它会被转换为位图,在旧版本/旧显卡上这看起来并不好。尝试使用更新的版本,看看是否已经解决了这个问题。 - Rich Bradshaw
Chrome 25 OS X上一切正常。顺便说一下:我建议使用不同的方法来实现背景渐变纹理,而不是一个300KB的图像! - Paolo
2
当动画由GPU处理时,问题就出现了,似乎位图的圆角有点偏差。在Canary中重现,但如果关闭GPU加速,则正常工作。 - vals
你可以在每一帧尝试这个解决方案... https://dev59.com/X14c5IYBdhLWcg3w7t7E#42256897 - Miguel
我发现这个问题只会在浏览器缩放时出现。你可以使用CTR+0 / CMD+0重置。 - Nathan Redblur
显示剩余2条评论
14个回答

276

2020更新

  • 如果你遇到模糊的图片问题,请确保从下面的答案中检查,特别是image-rendering CSS属性。
  • 为了最佳的可访问性和SEO优化,你可以使用object-fit CSS属性,用<img>标签替换背景图片。

原始回答

在你的CSS中尝试这个:

.your-class-name {
    /* ... */
    -webkit-backface-visibility: hidden;
    -webkit-transform: translateZ(0) scale(1, 1);
}

这样做的效果是使分割线呈现出“更2D”的行为。

  • 默认情况下,背面会被绘制,以便使用旋转等方式翻转物体。但是,如果您只是左右移动、上下移动、缩放或顺时针/逆时针旋转,则没有必要进行此操作。
  • 将Z轴平移至始终具有零值。
  • Chrome现在可以处理backface-visibilitytransform,不需要使用-webkit-前缀。目前我不知道这会对其他浏览器的渲染产生何种影响(如FF,IE),因此请谨慎使用非带前缀版本。

29
可能没有解释什么,但已经足够为我解决这个问题了。 - McNab
对我而言非常好用,甚至在Firefox上也没问题 :O - Ms01
1
我建议使用这个解决方案https://dev59.com/X14c5IYBdhLWcg3w7t7E#42256897,我发布链接以避免重复。 - Miguel
1
有人能否确认这个还有效吗?因为每当我添加 -webkit-backface-visibility-webkit-transform 时,我看不到任何变化,而且当我打开 Chrome 的开发者控制台时,我会看到这两个 CSS 属性被划掉了,就好像它们被覆盖了一样,但它们并没有(空的 CSS 和 HTML)。就好像 Chrome 不再接受它们一样。 - Kevin M
1
@KevinM,请尝试不使用-webkit-前缀,这些已经成为标准CSS了。 - sampoh
显示剩余18条评论

99

你需要对该元素应用3D变换,这样它将获得自己的合成层。例如:

.element{
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
}
或者
.element{
    -webkit-transform: translate3d(0,0,0);
    transform: translate3d(0,0,0);
}

关于创建图层的更多细节,请阅读此处: 在Chrome中进行加速渲染


一个解释:

示例(悬停绿色框):

当您在元素上使用任何过渡时,它会导致浏览器重新计算样式,然后重新布局内容,即使过渡属性是视觉的(在我的示例中,它是不透明度),最后绘制元素:

截图

这里的问题是重新布局内容可能会在过渡发生时造成页面上的元素“跳舞”或“闪烁”的效果。 如果您转到设置,选中“显示合成层”复选框,然后对元素应用3d变换,则会看到它拥有自己的带有橙色边框的图层。

截图

元素获得自己的图层后,浏览器只需在过渡期间合成图层,而无需重新布局或甚至绘制操作,因此问题必须得到解决:

截图


很棒的东西!因为你的回答非常详细,所以得到了一分!你用什么软件进行屏幕截图/箭头标注? - kroe
非常准确,伙计!!那救了我不少麻烦。 - user1017882
这对我很有帮助。起初,我在动画的父级上使用了translateZ,但其中的背景图像精灵仍然模糊。我正在使用Velocity.js来缩放另一个容器,并应用类似于translateZ:0.000001(一些无限小的数字),然后就大功告成了!背景图像再次清晰锐利! - notacouch
谢谢,伙计。这解决了我的问题。 顺便说一下,我的问题是我有一个元素被旋转了90度,并使用不透明度进行淡入过渡。当触发过渡时,元素的内容会从左边移动1像素。 - Lloyd aaron

48

在使用嵌入YouTube iframe(用于居中iframe元素)时遇到了相同的问题。直到尝试重置CSS过滤器,才找到了解决方案。

结构:


<div class="translate">
     <iframe/>
</div>

样式[之前]

.translate {
  transform: translateX(-50%);
  -webkit-transform: translateX(-50%);
}

样式 [后]

.translate {
  transform: translateX(-50%);
  -webkit-transform: translateX(-50%);
  filter: blur(0);
  -webkit-filter: blur(0);
}

11
对我来说,filter: blur(0)就足够了! - Nick
3
难以置信 O_o 这个模糊是怎么回事?为什么默认开启了呢? - Yuriy Polezhayev
这对我也是解决方案。 接受的答案可能适用于没有在其转换属性中使用其他“翻译”功能的人,但对我来说不起作用。 - Edward Coyle Jr.
-webkit- 前缀不应该放在前面吗?更多信息 - Trevor Nestman

36

我推荐一个我在最新的浏览器上测试过的实验性新属性CSS,效果很好:

image-rendering: optimizeSpeed;             /*                     */
image-rendering: -moz-crisp-edges;          /* Firefox             */
image-rendering: -o-crisp-edges;            /* Opera               */
image-rendering: -webkit-optimize-contrast; /* Chrome (and Safari) */
image-rendering: optimize-contrast;         /* CSS3 Proposed       */
-ms-interpolation-mode: nearest-neighbor;   /* IE8+                */

通过这个,浏览器将知道渲染的算法


这解决了我的模糊旋转图像问题,而 backface-visibility、blur(0)、translateZ 对我无效。谢谢。 - Louis Ameline
在某些情况下,固定图像会使情况变得更糟糕 :-) 不管怎样,这很有趣! - Simon Steinberger
深入挖掘后发现:在Chrome浏览器中,image-rendering: -webkit-optimize-contrast; 可以解决问题。然而,在其他浏览器(如Firefox)中,启用该渲染选项会使图像质量大幅下降。因此,我只使用WebKit指令,这也适用于Blink引擎。谢谢! - Simon Steinberger
在某些情况下,它会导致图像明显变得锯齿状。似乎找不到模糊结果和这个之间的最佳平衡点叹气 - chamberlainpi
不再使用 optimizeSpeed,而是使用 pixelated -> 在 mozilla 网站 上查看。 - Raphaël Balet

6
刚发现一个元素在进行变换时为什么会变得模糊。我使用了transform: translate3d(-5.5px, -18px, 0);来重新定位已经加载好的元素,但这个元素变得模糊了。
我尝试了以上所有建议,但事实证明是我在一个translate值上使用了小数点。整数不会导致模糊,而且距离整数越远,模糊越严重。
5.5px会使元素模糊最多,5.1px则最少。
如果有帮助的话,我想把这个信息分享在这里。

谢谢,这正是我的问题所在 - 我使用了translateY(-50%),这可能会评估为十进制像素值。 - b4tch

5

我使用分步过渡的方式来解决问题,而不是平滑过渡。

transition-timing-function: steps(10, end);

这不是解决方法,而是一种欺骗,不能在所有情况下使用。

我无法解释为什么,但它对我有用。其他答案都没有帮助我(OSX, Chrome 63, 非Retina显示器)。

https://jsfiddle.net/tuzae6a9/6/


1
在你的fiddle中,它像帕金森症一样抖动,但在我的情况下却有效。 - Tárcio Zemel

2
在2018年,对于我来说,唯一解决了我的问题(鼠标悬停时图像上出现的白色闪烁线)的方法是将此代码应用于包含具有“transform: scale(1.05)”属性的图像元素的链接元素。请注意保留HTML标签。
a {
   -webkit-backface-visibility: hidden;
   backface-visibility: hidden;
   -webkit-transform: translateZ(0) scale(1.0, 1.0);
   transform: translateZ(0) scale(1.0, 1.0);
   -webkit-filter: blur(0);
   filter: blur(0);
}
a > .imageElement {
   transition: transform 3s ease-in-out;
}

是的!在Chrome中,'blur(0)'对我有用。但是在调整大小时,会使图像略微模糊,但比跳动/调整大小不那么明显。 - 00-BBB

2

使用缩放将其扩大一倍,然后再减半是行得通的。

transform: scale(2);
zoom: 0.5;

这似乎在Chrome中对图像有效。不幸的是,它也会修改您放置在其中的任何HTML。 - Jack Davidson

2

我已经尝试了大约10种可能的解决方案。将它们混合在一起,但它们仍然不能正确地工作。最后总是有1像素的晃动。

我通过缩短过滤器的转换时间来找到了解决方案。

这个方法不起作用:

.elem {
  filter: blur(0);
  transition: filter 1.2s ease;
}
.elem:hover {
  filter: blur(7px);
}

解决方案:

.elem {
  filter: blur(0);
  transition: filter .7s ease;
}
.elem:hover {
  filter: blur(7px);
}

在fiddle中尝试这个:

.blur {
  border: none;
  outline: none;
  width: 100px; height: 100px;
  background: #f0f;
  margin: 30px;
  -webkit-filter: blur(10px);
  transition: all .7s ease-out;
  /* transition: all .2s ease-out; */
}
.blur:hover {
  -webkit-filter: blur(0);
}

.blur2 {
  border: none;
  outline: none;
  width: 100px; height: 100px;
  background: tomato;
  margin: 30px;
  -webkit-filter: blur(10px);
  transition: all .2s ease-out;
}
.blur2:hover {
  -webkit-filter: blur(0);
}
<div class="blur"></div>

<div class="blur2"></div>

我希望这能帮助到某些人。

2

所有这些方法都不起作用,对我有用的是将图片缩小。

因此,根据您想要的图像大小或图像的分辨率,您可以执行以下操作:

.ok {
      transform: perspective(100px) rotateY(0deg) scale(0.5);
      transition: transform 1s;
      object-fit:contain;
}
.ok:hover{
      transform: perspective(100px) rotateY(-10deg) scale(0.5);
}

/* Demo Preview Stuff */
.bad {
   max-width: 320px;
   object-fit:contain;
   transform: perspective(100px) rotateY(0deg);
   transition: transform 1s;
}
.bad:hover{
      transform: perspective(100px) rotateY(-10deg);
}

div {
     text-align: center;
     position: relative;
     display: flex;
}
h3{
    position: absolute;
    bottom: 30px;
    left: 0;
    right: 0;
}
     
.b {
    display: flex;
}
<center>
<h2>Hover on images</h2>
<div class="b">
<div>
  <img class="ok" src='https://www.howtogeek.com/wp-content/uploads/2018/10/preview-11.png'>
  <h3>Sharp</h3>
</div>

<div>
  <img class="bad" src='https://www.howtogeek.com/wp-content/uploads/2018/10/preview-11.png'>
  <h3>Blurry</h3>
</div>

</div>

</center>

图片应该缩小,确保您有一个大的图像分辨率。

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