CSS硬件加速的宽度是什么?

7

我正在尝试构建一个可以让用户通过移动中间分割线来改变两列布局大小的Phonegap应用程序。

我已经成功实现了这一点,但存在一个严重的用户体验问题:它很卡顿。虽然不是特别严重,但在最新的iPad上甚至也能感觉到,这让我感到担忧。

这是我用来调整大小的JS代码:

$("div").on("touchmove", "#columnResizeIcon", function(e) {
    e.preventDefault();
    var left = e.originalEvent.touches[0].pageX;
    var right = $("#columnContainer").width() - left;

    $("#leftColumn").css({
       "width":left - 1 + "px",
       "right":"auto",
    });
    $("#rightColumn").css({
       "width":right - 1 + "px",
       "left":"auto",
    });
    $("#columnResize").css({
       "-webkit-transform":"translate3d(" + left  + "px" + ", 0, 0)",
       "left":"auto",
    });
    $("#columnResizeIcon").css({
       "-webkit-transform":"translate3d(" + left  + "px" + ", 0, 0)",
       "left":"auto",
    });
}); 

您会注意到我利用translate3d()来改变元素的"left"值,因为它是硬件加速的。我认为延迟是由于更改左右列的宽度所产生的,这正是我需要进行硬件加速的地方。
我想到的一个可能的解决方案是使用-webkit-transform:translate3d(50%, 0, 0)将右列向右推动半页,然后只改变该值,希望它只会拉伸到达父级。然而,它继续前进,跨越了整个页面的50%,而不是父级的50%。
我的HTML标记看起来像这样:
<div id="columnContainer">
    <div id="columnResize"></div>
    <div id="columnResizeIcon"></div>

    <div id="leftColumn">
        <div class="header">Left Header</div>
        <div class="content"></div>

    </div>
    <div id="rightColumn">
        <div class="header">Right Header</div>
        <div class="content"></div>
    </div>
</div>

我的CSS如下:
body{
    background-color:#000;
}

#columnContainer{
    position: absolute;
    bottom:0;
    top:0;
    right:0;
    left:0;
    background-color:#000;
}

#leftColumn{
    position: absolute;
    top:0;
    left:0;
    right:50%;
    bottom:0;
    -webkit-overflow-scrolling: touch;
    z-index: 1;
    margin-right: 1px;
}

#rightColumn{
    position: absolute;
    top:0;
    left:50%;
    right:0;
    bottom:0;
    -webkit-overflow-scrolling: touch;
    z-index: 1;
    margin-left: 1px;
}

.header{
    position: absolute;
    left:0;
    right:0;
    height:33px;
    z-index: 5;
    background: -webkit-linear-gradient(top, #f4f5f7 0%,#a7abb7 100%);
    box-shadow:  inset 0 1px 0 #fff, inset 0 -1px 0 #7A8090, 3px 0 2px rgba(0,0,0,.3);
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
    font-size: 17px;
    font-family: Helvetica;
    font-weight: bold;
    letter-spacing: .2px;
    text-align: center;
    padding-top:9px;
    color:#71787F;
    text-shadow: 0 1px 0 #E3E5E9;
}

.content{
    position: absolute;
    left:0;
    right: 0;
    top:42px;
    bottom: 0;
}

#leftColumn .content{
    background-color:#F5F5F5;
}

#rightColumn .content{
    background-color:#fff;
}

#columnResize{
    position: absolute;
    width:2px;
    top:0;
    bottom: 0;
    left:50%;
    margin-left:-1px;
    background-color:#000;
    z-index: 2;
}

#columnResizeIcon{
    position: absolute;
    z-index: 3;
    width:10px;
    height:30px;
    top:50%;
    bottom:50%;
    margin-top:-15px;
    left:50%;
    margin-left:-7px;
    border-left:2px solid #000;
    border-right:2px solid #000;
}
2个回答

2
我终于想出了一个比之前好得多的解决方案。基本上,我对容器进行动画处理,并在调整大小时隐藏内容。然后,当调整大小完成后,我再次显示内容。我使用动画使其在隐藏/显示时看起来更漂亮。代码会比我更好地解释它:

万能的fiddle

1 http://jsfiddle.net/charlescarver/hnQHH/134/

我的解释

当滑块被点击时,它会使用translate3d()变换将所有文本元素推出页面,然后隐藏div。这是因为如果在显示元素时尝试更新宽度会导致延迟。所以,一旦div被隐藏,我就再次使用translate3d()变换将列向左或向右移动。我可以这样做而不必担心每个元素的宽度停止得太短,因为我将leftright值设置为永远无法达到的值,所以它延伸到了页面之外。这样,我就可以简单地移动它,而不用担心它会过早地截断。
奇怪的事情
其中有些部分可能是多余的,但我很快就会清理掉它们。您还可能会注意到一些奇怪的事情,例如(1)cornerLeft,(2)dummy,(3)shadow,以及在JS中,(4)minimum:
当我调整页面大小时,dummy导航栏会延伸到左右两列的整个宽度,这意味着它占据了1000%的宽度。这意味着我无法在导航栏上设置每列左右侧的border-radius,因为它距离屏幕太远,看不见。因此,我制作了一个简单的角来遮盖窗口的每一侧,使其看起来漂亮。
当我调整大小时,我隐藏.contentLeft.contentRight,因为它们在显示时会导致延迟。但我不想取消导航栏,所以我制作了一个dummy导航栏,它始终存在于页面上,并在调整大小即将发生时显示出来。我认为这减少了延迟,因为我不需要添加元素,因为它始终存在。
然而,其中一个问题是当普通导航栏覆盖虚拟导航栏时,box-shadow会重叠,导致它变暗200ms。我不喜欢这样。因此,我放置了一个始终位于导航栏顶部的阴影,无论哪个导航栏正在显示。
现在,我可以轻松地设置可拖动列可以到达的边界,停止移动。方便,对吧?

代码

HTML:

<div id="container">
    <div class="cornerLeft"></div>
    <div class="cornerRight"></div>
    <div class="shadow"></div>
    <div class="left">
        <div class="contentLeft">
            <div class="header"></div>
            <div class="headerbehind"></div>
            <div class="text textLeft">Praesent id metus massa, ut blandit odio. Proin quis tortor orci. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus.</div>
        </div>
        <div class="dummy"></div>
        <div class="dummybg"></div>
    </div>
    <div class="divider"></div>
    <div class="right">
        <div class="contentRight">
            <div class="header"></div>
            <div class="headerbehind"></div>
            <div class="text textRight">Praesent id metus massa, ut blandit odio. Proin quis tortor orci. Etiam at risus et justo dignissim congue. Donec congue lacinia dui, a porttitor lectus.</div>
        </div>
        <div class="dummy"></div>
        <div class="dummybg"></div>
    </div>
</div>

CSS(层叠样式表):
* {
    -webkit-text-size-adjust:none;
}
#container {
    position:fixed;
    left:0;
    right:0;
    bottom:0;
    top:0;
    background-color:#000;
    -webkit-transform: translateZ(0);
    -webkit-perspective: 1000;
}
.left {
    -webkit-transform:translate3d(0, 0, 0);
    position:absolute;
    left:-3000px;
    right:50%;
    top:0;
    bottom:0;
    border-right:1px solid #000;
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
}
.right {
    -webkit-transform:translate3d(0, 0, 0);
    position:absolute;
    left:50%;
    right:-3000px;
    top:0;
    bottom:0;
    border-left:1px solid #000;
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
}
.divider {
    width:24px;
    height:40px;
    border-left:2px solid #000;
    border-right:2px solid #000;
    position:absolute;
    left:50%;
    z-index:3;
    margin-left:-14px;
    margin-top:-20px;
    top:50%;
    -webkit-transform:translate3d(0, 0, 0);
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
}
.contentLeft {
    position:absolute;
    right:0;
    bottom:0;
    top:0;
    -webkit-transform: translateZ(0);
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
}
.contentRight {
    position:absolute;
    left:0;
    bottom:0;
    top:0;
    -webkit-transform: translateZ(0);
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
}
.cornerLeft:after {
    content:"";
    height:5px;
    position:absolute;
    left:0;
    width:5px;
    background: -webkit-linear-gradient(top, #F0F2F4 0%, #EAEBEE 100%);
    z-index:700;
    border-top-left-radius:5px;
    box-shadow:inset 0 1px 0 #fff;
}
.cornerLeft {
    position:absolute;
    z-index:700;
    left:0;
    width:5px;
    height:5px;
    background-color:#000;
}
.cornerRight:after {
    content:"";
    height:5px;
    position:absolute;
    right:0;
    width:5px;
    background: -webkit-linear-gradient(top, #F0F2F4 0%, #EAEBEE 100%);
    z-index:700;
    border-top-right-radius:5px;
    box-shadow:inset 0 1px 0 #fff;
}
.cornerRight {
    position:absolute;
    z-index:700;
    right:0;
    width:5px;
    height:5px;
    background-color:#000;
}
.header, .dummy {
    position: absolute;
    left:0;
    right:0;
    height:35px;
    background: -webkit-linear-gradient(top, #f4f5f7 0%, #a7abb7 100%);
    border-top-left-radius: 5px;
    border-top-right-radius: 5px;
    font-size: 17px;
    font-family: Helvetica;
    font-weight: bold;
    letter-spacing: .2px;
    text-align: center;
    padding-top:9px;
    color:#71787F;
    text-shadow: 0 1px 0 #E3E5E9;
    word-break: break-all;
    box-shadow:inset 0 1px 0 #fff, inset 0 -1px 0 #7A8090;
}
.shadow {
    height:44px;
    position:absolute;
    left:0;
    right:0;
    box-shadow:0 1px 2px rgba(0, 0, 0, .2);
    z-index:600;
}
.header {
    z-index:500;
}
.dummy {
    z-index:100;
}
.headerbehind {
    position:absolute;
    background-color:#000;
    left:0;
    right:0;
    height:44px;
    z-index:499;
}
.text, .dummybg {
    margin-top:44px;
    background-color:#fff;
    position:absolute;
    top:0;
    right:0;
    left:0;
    bottom:0;
}
.text {
    z-index:2;
    padding:20px 40px;
    -webkit-animation-duration:200ms;
    -webkit-animation-timing-function:ease;
}
.contentLeft, .contentRight {
    z-index:300;
}
.leftOut {
    -webkit-transform:translate3d(-100%, 0, 0);
    -webkit-animation-name:leftOut;
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
}
.leftIn {
    -webkit-transform:translate3d(0, 0, 0);
    -webkit-animation-name:leftIn;
    -webkit-perspective: 1000;
    -webkit-backface-visibility: hidden;
}
@-webkit-keyframes leftOut {
    0% {
        -webkit-transform:translate3d(0, 0, 0);
    }
    100% {
        -webkit-transform:translate3d(-100%, 0, 0);
    }
}
@-webkit-keyframes leftIn {
    0% {
        -webkit-transform:translate3d(-100%, 0, 0);
    }
    100% {
        -webkit-transform:translate3d(0, 0, 0);
    }
}
.rightOut {
    -webkit-transform:translate3d(100%, 0, 0);
    -webkit-animation-name:rightOut;
}
.rightIn {
    -webkit-transform:translate3d(0, 0, 0);
    -webkit-animation-name:rightIn;
}
@-webkit-keyframes rightOut {
    0% {
        -webkit-transform:translate3d(0, 0, 0);
    }
    100% {
        -webkit-transform:translate3d(100%, 0, 0);
    }
}
@-webkit-keyframes rightIn {
    0% {
        -webkit-transform:translate3d(100%, 0, 0);
    }
    100% {
        -webkit-transform:translate3d(0, 0, 0);
    }
}

JS:

minimum = 100;
$(".contentLeft").css("width", ($("#container").width() / 2) - 1);
$(".contentRight").css("width", ($("#container").width() / 2) - 1);

$("div").on("touchstart", ".divider", function (e) {
    $(".textLeft").removeClass("leftIn");
    $(".textLeft").addClass("leftOut");
    $(".textRight").removeClass("rightIn");
    $(".textRight").addClass("rightOut");
    setTimeout(function () {
        $(".contentLeft, .contentRight").hide();
    }, 200);
});

$("div").on("touchmove", ".divider", function (e) {
    e.preventDefault();
    if ($(".contentLeft").css("display", "none")) {
        var page = $("#container").width();
        var left = e.originalEvent.touches[0].pageX;
        var right = page - left;
        updateWidth(page, left, right);
    }
});

//$(".contentLeft, .contentRight").hide();

$("div").on("touchend", ".divider", function (e) {
    setTimeout(function () {
        $(".textLeft").removeClass("leftOut");
        $(".textLeft").addClass("leftIn");
        $(".textRight").removeClass("rightOut");
        $(".textRight").addClass("rightIn");
        $(".contentLeft, .contentRight").show();
    }, 200);
});

$(window).on('orientationchange', function (e) {
    var page = $("#container").width();
    var leftWidth = $(".contentLeft").width();
    var rightWidth = $(".contentRight").width();
    var previousWidth = (leftWidth + rightWidth);
    if (leftWidth + rightWidth + 2 < page) {
        var left = (page / 2) - (previousWidth / 2) + leftWidth;
    } else if (leftWidth + rightWidth + 2 > page) {
        var left = leftWidth - ((previousWidth / 2) - (page / 2));
    }
    var right = page - left;
    updateWidth(page, left, right);
});

function updateWidth(page, left, right) {
    if (left < minimum) {
        var finalLeft = minimum;
        var finalRight = (-1 * (page - minimum));
        var finalRightWidth = (page - minimum);
    } else if (right < minimum) {
        var finalLeft = (page - minimum);
        var finalRight = (-1 * minimum);
        var finalRightWidth = minimum;
    } else {
        var finalLeft = (left);
        var finalRight = (0 - right);
        var finalRightWidth = (right);
    }
    $(".divider").css({
        "-webkit-transform": "translate3d(" + finalLeft + "px, 0, 0)",
            "left": "auto",
    });
    $(".left").css({
        "-webkit-transform": "translate3d(" + finalLeft + "px, 0, 0)",
            "right": "100%",
    });
    $(".right").css({
        "-webkit-transform": "translate3d(" + finalRight + "px, 0, 0)",
            "left": "100%",
    });
    $(".contentLeft").css("width", finalLeft);
    $(".contentRight").css("width", finalRightWidth);
}

是的,我尝试了134次。

你的小提琴似乎没有做任何事情? - trusktr
@trusktr 自 2013 年以来,情况可能已经发生了变化。 - Charlie
啊,原来是因为我在桌面上没有触摸功能。打开仿真模式就可以了。我认为用CSS动画实现宽度和高度的变化不会硬件加速,对吧?它是在CPU上运行而不是GPU上吗?在我的情况下,我解决这个问题的一种方法是使用CSS变换来伪造元素的大小扩展/收缩(但内容不需要重新排版)。 - trusktr
1
@trusktr 如果你看一下我的解决方案,我是通过使用CSS变换来实现类似的效果,然后在动画后设置宽度/高度。 - Charlie
转换的问题在于阴影可能变得棘手。 - trusktr

0

我通过将这两个元素推入硬件加速堆栈中取得了一些成功:

#leftColumn,
#rightColumn {
  -webkit-transform: translate3d(0,0,0);
} 

看起来调整大小更加流畅了。并不是width本身得到了优化,而是元素本身重新渲染的速度更快了。

我在这里设置了一个plunk:http://plnkr.co/edit/5RMtCl1Sql8f3CmQLHFz


你是在模拟器上测试还是实际设备上测试?如果是实际设备,请问是哪种设备? - Charlie
我在运行iOS 6.1.3的iPhone上进行了测试。 - couzzi

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