基于Webkit的translate3d动画后文字模糊/扭曲问题

62

这个问题似乎影响所有基于 WebKit 的浏览器,包括 iPhone。

首先解释一下背景。我正在处理一个使用 JavaScript 的“滑块”动画的网站。

我正在使用 -webkit-transform: translate3d 来“驱动”实际的动画。当使用这种方法时,与使用基于 JavaScript 的方法相比,一旦内容被动画化,文本就会变得模糊不清。在 iPhone 上尤为明显。

我看到的一些解决方法是删除相对定位,我已经这样做了,并添加了 -webkit-font-smoothing: antialiased 规则,我也试过了。但两种方法都没有任何改善。

唯一能使文本不模糊的方法是完全使用常规的 JavaScript 进行动画并绕过 translate3d。我更喜欢使用 translate3d,因为它在启用 WebKit 的设备上表现得更快,但就我而言,我无法理解为什么它会以如此糟糕的方式影响文本。

如果有任何建议或解决方案,将不胜感激。


请查看http://dropshado.ws/post/6142339613/resolving-anti-aliasing-on-webkit-hardware-accelerated。 - robertklep
1
这里也有同样的问题,当应用translate3d时,浏览器会使整个页面的文本模糊。有人找到了好的解决方案吗?演示:http://jsfiddle.net/DmitrySemenov/PtDVF/ - Dmitry Semenov
我修改了jsfiddle以复制导致文本模糊的缩放问题。 http://jsfiddle.net/PtDVF/14/ 在PC、MAC和Safari for iPhone上都会出现模糊,我还没有测试过Android。 - Brandon Hutchinson
我在2023年仍然遇到这个问题。theyrule.net使用transform translate3D来缩放文本,在Safari中模糊不清。我不想通过调整比例来保持清晰度的hack,因为缩放是动态的... - Korimako
19个回答

24

这些方法似乎都对我不起作用,但我找到了一个稍微有点不太干净的解决方案,似乎能起作用:

top: 49.9%;
left: 49.9%;
-webkit-transform: translate(-50.1%, -50.1%);
transform: translate(-50.1%, -50.1%);

4
丑陋浏览器的丑陋解决方案,以上方法都不起作用,但这个可以。对我来说,Chrome / Webkit是新的IE,我需要一个又一个愚蠢的解决办法。 - RiZKiT
2
在处理 Y 轴时,使用此技巧对我有效,而其他解决方案都无效:translate(-50%,-50%) -> translate(-50%,-50.1%)。有趣的是,稍微增加或减少 50.1 也不起作用... - JBE
1
这对我起作用了,但每个人都需要自己尝试一下,在我的情况下,我必须使用50.2。 - jigzat
6
请看,这将始终取决于您屏幕的总宽度!没有简单的解决办法。如果您调整屏幕大小,您会发现效果会改变... - Miguel
这不是一种通用解决方案。在50%的情况下起作用,但在某些情况下不好,在50%看起来正常的情况下也会有问题。 - marcias

21

正如 @Robert 上面提到的,有时添加背景可以帮助解决问题,但并非总是如此。

因此,对于 Dmitry 添加的示例,你必须做的不仅仅是添加背景:除了背景之外,你必须告诉浏览器明确地使用适当的抗锯齿技术,所以,这里有一个固定的 Dmitry 的示例:http://jsfiddle.net/PtDVF/1/

你需要在需要修复抗锯齿的区块周围(或者针对这些区块)添加以下样式:

background: #FFF; /* Or the actual color of your background/applied image */
-webkit-font-smoothing: subpixel-antialiased;

这个可以用,但如果我需要一个透明的背景怎么办?background: transparent;会再次导致模糊的排版 :-( - Timo Ernst
1
这刚刚救了我的狗命!background: rgba(0,0,0,0);。解决了iOS上一个非常讨厌的模糊问题。 - ne1410s
Safari 11 / High Sierra 在2017年底仍然受到影响,添加实心的 background-color(而不是 transparent 或 rgba(0, 0, 0, 0))是唯一有用的解决方案。 - Volker E.
当我使用translateZ时,我也遇到了同样的问题。对我来说,当我同时使用translateX和translateY而不是使用translateZ时,它起作用了,就像这样:transform: translateX(-50%) translateY(-50%); 在透明背景下也可以工作。 - Pushpender

21

我遇到了与Ken Avila的帖子中描述的完全相同的问题:CSS:transform:translate(-50%,-50%)使文本模糊

问题当然是我使用了transform:translate(-50%,-50%),这使得我的居中内容变得模糊,但仅在osx上的Safari浏览器中出现。

不仅文本会变模糊,整个内容,包括图像,都会变模糊。我在http://keithclark.co.uk/articles/gpu-text-rendering-in-webkit/上读到了一些关于这种“模糊”的原因,是因为该元素在非整数边界上呈现。

我还发现,可以通过阅读https://coderwall.com/p/quutdq/how-to-really-center-an-html-element-via-css-position-absolute-fixed文章来避免在水平居中方面使用transform translate -唯一的缺点是我必须引入一个包装器。

我发现使用transform:translateY(-50%)不会创建任何“模糊”,可能是因为我的元素具有设置的高度,因此不会呈现在非整数边界上。

因此,我的解决方案最终是这样的:

.wrapper {
  position: fixed;
  left: 50%;
  top: 50%;
}
.centered {
  position: relative;
  left: -50%;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
}
<div class="wrapper">
  <div class="centered">
    Content
  </div>
</div>


2
第一篇文章完美地解释了这个问题。关于仅因设置高度而设置y的问题,我通过以下方式解决了它。function centerTarget(tar){ var y = Math.ceil(tar.height()/2), x = Math.ceil(tar.width()/2);tar.css('transform','translateX(' + x + 'px) translateY(' + y + 'px)');} - Spaceman
2
您是传奇!我刚刚花了几个小时尝试了很多解决方案,但都没有起作用,直到这个!非常感谢您!!! - Brett
赞赏你考虑添加一个包装器。我想不出任何缺点,而且似乎很有效。 - zacharydl

18

翻译的元素必须是偶数,它们的宽度和高度都必须是 2 的倍数。

无论你要移动的元素是哪个方向,移动一半时,它的宽度和高度必须都是偶数。这非常类似于响应式图片,当元素可以移动 50% 而不会分裂像素时。

如果一个 div 的宽度为 503px,高度为 500px,则使用 translateX 或 Y 移动它时,无论如何移动或者移动多少,都会导致模糊。使用 transform,它利用 GPU 图形加速器,应该会产生非常清晰和平滑的边缘。此外,设置 box-sizing: border-box; 可以确保计算出的宽度包括 padding 和 borders,这也是一个不错的主意。

在使用百分比宽度时要小心。如果相对于屏幕大小,每隔其他屏幕像素宽度将会导致模糊。


2
我发现即使3D翻译图像的尺寸可以被2整除,它们仍然很模糊。非常模糊。 - Quentin Engles
@QuentinEngles - 我发现在某些情况下,图像是否被翻译并不重要。有两件事情需要注意:1. 如果您正在缩小图像,请确保宽度和高度都可以等分。例如:100像素乘以112像素的图像如果缩放到宽度为99像素,则会显得模糊,因为高度不是整数。2:使用Windows缩放的高分辨率显示器也需要考虑到这种情况。为了安全起见,请将图像缩放设置为100%或50%。 - factorypolaris
我注意到只要盒子的高度是偶数(例如:height:400px),文本的模糊就会消失。 - pref

16

在任何动画发生之前,我通过向元素添加translate3d样式来解决了这个问题。

-webkit-transform: translate3d(0,0,0); 

1
这确实解决了我遇到的亚像素渲染问题,但也由于translate3D引入了模糊文本。它将其推送到GPU进行亚像素渲染,但会使其变得模糊。最好采用像@neil-taylor发布的解决方案一样彻底解决问题。 - Kars Barendrecht

6

这是最佳解决方案 translateX(calc(-50% + 0.5px))



元素重新定位是作为副作用发生的,或者我错过了什么? - retrovertigo

2

可能是最简单的解决方案:

transform: translate(-50%, -50%) scale(2); 
zoom:.5;

缩放和缩小会处理将像素值四舍五入为整数的问题。

1

这些答案都对我没用,但使用

display: inline-table;

做到了。

1

Neil Taylor的解决方案是最好的,但仍有改进的空间:

transform: translateX(-50%) translateX(.5px)

这样有两个优点:

  • 它可以在像IE11这样的糟糕浏览器中工作(不支持translate内部的calc)
  • 它只在解析时计算,而不是每次浏览器评估时都计算。

你的解决方案是最好的 :D - Quinn Wynn

0
我希望这是关于将对象居中到屏幕的确切中心。 使用 transform: translate(-50%,-50%) 会发生模糊。
因此,不要这样做:
position: absolute;
margin:0;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);

我尝试使用JavaScript(React.js)向元素注入样式。

const node = ReactDOM.findDOMNode(this);
var width = node.offsetWidth;
var height = node.offsetHeight;
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;

style={
    left : (windowWidth/2) - (this.state.width/2) + 'px',
    right:  (windowWidth/2) - (this.state.width/2) + 'px',
    top: (windowHeight/2) - (this.state.height/2) + 'px',
    bottom: (windowHeight/2) - (this.state.height/2) + 'px'
}

使用JavaScript向元素注入样式:

例如:

export default class Dialog extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            width: '0px',
            height: '0px'
        };
    }

    setWidthhandHeight(node){
        if (this.state.width !== node.offsetWidth) {
            this.setState({
                width: node.offsetWidth,
                height: node.offsetHeight
            });
        }
    }

    componentDidMount() {
        this.setWidthhandHeight(node);
    }

    render() {
        const windowWidth = window.innerWidth;
        const windowHeight = window.innerHeight;
        return (
            <dialog className={this.props.className + " dialog"}
                    style={{
                        left : (windowWidth/2) - (this.state.width/2) + 'px',
                        right:  (windowWidth/2) - (this.state.width/2) + 'px',
                        top: (windowHeight/2) - (this.state.height/2) + 'px',
                        bottom: (windowHeight/2) - (this.state.height/2) + 'px'
                    }}>
                {this.props.title ? (
                    <header><span>{this.props.title}</span></header>
                    )
                    : null
                }
                {this.props.children}
            </dialog>
        );
    }
}

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