Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何抗rotate剧齿 #53

Open
leeenx opened this issue Jul 12, 2017 · 2 comments
Open

如何抗rotate剧齿 #53

leeenx opened this issue Jul 12, 2017 · 2 comments

Comments

@leeenx
Copy link
Contributor

leeenx commented Jul 12, 2017

有时候,我们需要transform的rotate方法来使元素倾斜,但是角度倾斜会给元素带来意料之外的效果--锯齿。

如何在IOS下对抗rotate造成的锯齿

在IOS中,搞锯齿的方法很简单,只要开启元素的3d属性既开启了GPU加速后,能有效的实现抗锯齿。如果在开启了GPU后,ios页面仍出现锯齿现象,使用安卓的最终解决方案可以起最后的搞锯齿作用

而安卓就比较复杂了,下面就如何在安卓下有效抗做一次梳理。

以下都是针对简单的方块模块(即一块div)进行研究

这里研究的安卓系统都是4.0以上的,2.3不做研究(因为份额几乎快没了)

如何在安卓下对抗rotate造成的锯齿

为了方便描述,把被rotate的元素简单地划分成两类:

  1. 图片(即标签的元素)
  2. 带背景的div

本人已经通过验证了,以纯色作背景的div和以图片作背影的div在锯齿的表现上是一样的,所以把二者统一成一类。

图片rotate后抗锯齿方法

保证img在一个容器(如div)内,并使rotate作用于容器上,此时只要开启容器的GPU,和为容器添加一个透明border即可

如下:
rotate-blur2.html:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>rotate锯齿 - 图片</title>
    <style type="text/css">
        html,body{width: 100%; height: 100%; margin: 0; padding: 30px 0 0 0; background-color: #000;}
        .box{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: -80px 0 0 -120px; color: #666; border:2px solid rgba(255,255,255,0);}
        .box2{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: -80px 0 0 20px; color: #666;}
        .box3{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: 50px 0 0 -120px; color: #666; border:2px solid rgba(255,255,255,0);}
        .box4{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: 50px 0 0 20px; color: #666;}
    </style>
</head>
<body ontouchstart>
    <input id="deg" value="0"/>
    <div class="box" id="box">
        <img src="http://smartpro.sinaapp.com//test/white.jpg">
    </div>
    <div class="box2" id="box2">
        <img src="http://smartpro.sinaapp.com//test/white.jpg">
    </div>
    <div class="box3" id="box3">
        <img src="http://smartpro.sinaapp.com//test/white.jpg">
    </div>
    <div class="box4" id="box4">
        <img src="http://smartpro.sinaapp.com//test/white.jpg">
    </div>
</body>
<script type="text/javascript">
    deg.addEventListener("change",function(){
        box.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg) translateZ(0)";
        box2.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg) translateZ(0)";
        box3.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg)";
        box4.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg)";
    });
    //安卓结论抗锯齿的关键,加上3D属性translateZ(0)开启gpu,但是此时仍然会有锯齿问题,需要添加非0的border且颜色为rgba(255,255,255,0)
</script>
</html>

扫一扫:

图片rotate体验地址

以下是截图:

小米2
小米2

小米3
小米3

红米2
小米2

华为3c
华为3c

魅蓝note
华为3c

通过五部手机可以清楚地知道,华为3c对 rotate 锯齿的处理结果就很理想,加不加GPU和border值都没关系。而对于小米系列手机和魅蓝来说,只有加了border:1px solid rgba(255,255,255,0)和translateZ(0) 搞锯齿才起作用。

小结: 解决安卓下rotate图片锯齿的解决方法是为其容器添加"translateZ(0)"和"border:1px solid rgba(255,255,255,0)"两个属性

非图片元素rotate后搞锯齿的方法

对于纯颜色DIV来说,上述的方法是无法解决锯齿问题的。至于原因,这里涉及到border与容器的background之前的关系。

简略的分析如下:
容器可以细分为:外边距,边框,内边距和内容区。在内外边距都为0边框不为0的情况下,可以认为容器就只有边框和内容区,此时边框和内容区是紧挨在一起的,容器的尺寸等边框尺寸,内容区的尺寸略小于容器的尺寸。如下:
容器的边框与内容区示意图

如果容器有填充(即有背景)的元素,那么它是怎么渲染过程如下:
容器的边框与内容区示意图

也就是说此时的边框和容器的填充是有重叠的。
在图片rotate的例子中,图片和外部容器的border它却是不重叠的。

透明border去锯齿的原理:透明边框会在边缘处产生羽化的效果。

有扣图经验的小伙伴都知道羽化是去锯齿的利器。由于背景填充与边框重叠,羽化的区域被困死在重叠区中,起不了抗锯齿的作用。不过知道,搞锯齿的原理,就有办法解决锯齿问题。只要我不把羽化区困死,锯齿就不会有了。css3有一个叫background-clip的属性,用这个属性可以强制容器的填充是填充整个容器还是只填充内容区。现在我只需要填充内容区,于是用background-clip:content-box;那么容器的渲染就会变成:

容器的边框与内容区示意图

根据上述原理,写了另一个demo

rotate-blur.html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
    <meta content="yes" name="apple-mobile-web-app-capable">
    <meta content="black" name="apple-mobile-web-app-status-bar-style">
    <meta content="telephone=no" name="format-detection">
    <meta content="email=no" name="format-detection">
    <title>rotate锯齿</title>
    <style type="text/css">
        html,body{width: 100%; height: 100%; margin: 0; padding: 30px 0 0 0; background-color: #000;}
        .box{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: -160px 0 0 -120px; background:url(http://smartpro.sinaapp.com//test/white.jpg); color: #666; border:1px solid rgba(255,255,255,0);}
        .box2{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: -160px 0 0 20px; background:url(http://smartpro.sinaapp.com//test/white.jpg); color: #666;}
        .box3{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: -20px 0 0 -120px; background:url(http://smartpro.sinaapp.com//test/white.jpg); color: #666; border:1px solid rgba(255,255,255,0);}
        .box4{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: -20px 0 0 20px; background:url(http://smartpro.sinaapp.com//test/white.jpg); color: #666;}
        .box5{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: 120px 0 0 -120px; background:url(http://smartpro.sinaapp.com//test/white.jpg); color: #666; border:1px solid rgba(255,255,255,0); background-clip: content-box;}
        .box6{position: absolute; left: 50%; top: 50%; width: 100px; height: 100px; margin: 120px 0 0 20px; background:url(http://smartpro.sinaapp.com//test/white.jpg); color: #666; border:1px solid rgba(255,255,255,0); background-clip: content-box;}
    </style>
</head>
<body ontouchstart>
    <input id="deg" value="0"/>
    <div class="box" id="box">
        有border有translateZ
    </div>
    <div class="box2" id="box2">
        无border有translateZ
    </div>
    <div class="box3" id="box3">
        有border无translateZ
    </div>
    <div class="box4" id="box4">
        无border无translateZ
    </div>
    <div class="box5" id="box5">
        有border有translateZ加background-clip:content-box;
    </div>
    <div class="box6" id="box6">
        有border无translateZ加background-clip:content-box;
    </div>
</body>
<script type="text/javascript">
    deg.addEventListener("change",function(){
        box.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg) translateZ(0)";
        box2.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg) translateZ(0)";
        box3.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg)";
        box4.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg)";
        box5.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg) translateZ(0)";
        box6.style["-webkit-transform"]="rotate("+(parseFloat(this.value)||0)+"deg) translateZ(0)";
    });
    //安卓结论抗锯齿的关键,加上3D属性translateZ(0)开启gpu,但是此时仍然会有锯齿问题,需要添加非0的border且颜色为rgba(255,255,255,0)
</script>
</html>

扫一扫体验:

图片rotate体验地址

以下是实测截图:

小米2
小米2

小米3
小米3

红米2
小米2

华为3c
华为3c

魅蓝note
华为3c

通过上面的测试,可以发现原来处理锯齿很友好的华为3c也出现锯齿了,不过,使用了我们的抗锯样式后明显好多了。其它的四部手机也是。不过总的说来,抗锯的效果相对于图片来说差了一点。

安卓抗锯总结

综合上述两个解决方案,可以统一得出一个抗锯方案:

.border-blur{-webkit-transform:translateZ(0); border:1px solid rgba(255,255,255,0); background-clip:content-box;}
@h5m1007
Copy link

h5m1007 commented Oct 15, 2018

锯齿写错了 是锯齿

@leeenx
Copy link
Contributor Author

leeenx commented Oct 17, 2018

锯齿写错了 是锯齿

@h5m1007 谢谢指正

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants