CSS Transition Flickering Patch #4129

Closed
wants to merge 2 commits into
from

Projects

None yet

6 participants

@xibxor
xibxor commented Apr 20, 2012

I'm developing a PhoneGap / jQuery Mobile app for my company, Filter Foundry. For some reason, sliding right worked fine but sliding left was flickery and laggy. This patch uses width with overflow:hidden to simulate the same effect, with better performance.

xibxor added some commits Apr 20, 2012
@xibxor xibxor Overflow:hidden is needed for the revised transition of width 100% to…
… width 0%.
3d62698
@xibxor xibxor Using width on the slideouttoleft fixes flicker problem on Chrome, Sa…
…fari, and iOS Safari.


It seems that it's less intensive for the cpu to do a width 100% to width 0% with overflow:hidden on than to do the translate.
b347009
@dcarrith
Contributor

I spent a few hours last night tearing apart the logic behind the transitions and think I've gotten a good handle on the startOut, doneOut, startIn and doneIn functions as well as the scrollPage function. However, there is something happening during the logic behind going back that first scrolls the page down to the to page's scrollTo setting before any of the aforementioned functions are called. If anyone knows what is doing that, I would love to know. I'll be working on it some more tonight. Anyway, this patch sounds interesting.

@xibxor
xibxor commented Apr 20, 2012

I spent hours trying to find a solution to this problem because the app I'm developing is mainly for slideshows. Without a proper slide effect, I would have been down and out. I just don't want you to waste your time, know that I already spent 30+ hours messing around with all kinds of transforms to find what would work without flickering on iOS, this seems to be it.

@xibxor
xibxor commented Apr 20, 2012

I messed around with that scroll though when trying to fix this bug, commented out all scrolls had NO effect on performance, just a heads up if you plan to delve into the scroll looking for solutions.

@dcarrith
Contributor

@iandrewfuchs: the code you pointed to is the code I've been hacking away at. And, line 114 is the scroll I already have control over. That line 114 is actually part of the doneIn phase of the transition which I have good control over at this point. However, there is another scroll that happens somewhere between the click on a back button and the first phase of the transition when going back to a page that had resulted in a lastscroll being tracked for that page. It seems like a bug.

For example, if I have one listview that is really long, so if I scroll down pretty far and cause the lastscroll to be set to something like 4000 for that active page, then I select an item, on the next page, if I click the Back button, which should kick off the scroll to top (if I'm scrolled down at all) before the startOut phase of the transition, but it doesn't...it actually scrolls to the scrollTo that was set for the previous page (now the $to page since I'm going back - in this case 4000). That causes a jump 4000 pixels down, then it scrolls up as part of the startOut phase of the transition. If I could just figure out where that bug exists in the code, I would be close to having a nice handle on each phase of the transition.

By the way, another oddity was being caused by the toggleViewportClass() function call. It seemed to be causing scrollTop to ONLY return between 0 and 80 on Firefox or 0 and 78 if on Chrome. I just commented out the two or three calls to that function for now. Did you notice that too?

@dcarrith
Contributor

Well, in tracing through the code to find out where the mystery scroll I mentioned above is happening, I came to the window.history.back(); call in $( document ).bind( "click", function( event ) handler (since my back button uses data-rel="back"). I'm not sure where to go from here. Other than replacing all the calls to window.history.back(); I'm not sure what to do. It's the only scroll I can't get a handle on.

@dcarrith
Contributor

By the way... @landrewfuchs, have you tried applying Scott Jehl's opacity magic that addresses this issue: #4024

@xibxor
xibxor commented Apr 22, 2012

@dcarrith I haven't tried the opacity fix yet, my headers/footers aren't position:fixed, would it make a difference in that case?

My app doesn't use scrolling directly on pages, I just have "-webkit-overflow-scrolling: touch" on content boxes within the pages. To stop any scrolling problems JQM might cause, I have this code added "window.scrollTo = function(){};"

I will give the scrolling a look in a bit with some simple test cases, not my app, to see if I can help you. But if you can't find a solution and I can't either, maybe you might want to do something similar to what I'm doing with the indirect scrolling, you'll have to write your own technique to remember the scrollHeights unfortunately if you need them to be persistent

@dcarrith
Contributor

@iandrewfuchs, Thanks for the insight. I figured it out. Take a look at the discussion I started: http://bit.ly/IcJAYX. I'd be interested in hearing what you think.

@xibxor
xibxor commented Apr 22, 2012

@dcarrith I took a look and though it is very nice, I feel that the long scrolls are not user-friendly at all. The optimal solution would be to have the page scrolled before the user even sees it, would you not agree? With this in mind, I set up a page on my server to figure this issue out and came up with a temporary fix. Maybe with your help, we can come up with a real patch.

Test case is here: http://scrollto.com/test.html

There are 2 pages which look like documentation pages, try any transition and then use the back/forward button of your browser to see the scrolling in action. You shouldn't actually see any scrolling because it happens before the page transition.

This is the code for the fix:

    (function(){
        var wst = window.scrollTo;
        window.scrollTo = function(x,y){
            $(document.body).height(y+$.mobile.getScreenHeight());
            setTimeout(function(){
                wst.call(window, x, y);
            }, 0);
        };
    })();

In my past experiences with building web apps, changing height of elements will not allow the browser to immediately scroll to that height. To circumvent this, I have overridden the scrollTo calls to first adjust the body height THEN scroll on the next frame of JS execution, after CSS changes have taken effect. Without the timeout, the scrollTo will be effectiveless.

I see this comment in the jQuery Mobile Code:

// In some browsers (iOS5), 3D transitions block the ability to scroll to the desired location during transition
                // This ensures we jump to that spot after the fact, if we aren't there already.
                if( $( window ).scrollTop() !== toScroll ){
                    scrollPage();
                }

Seems like the devs have it a bit wrong, it's not the 3D transformation blocking the scrollTo. It's that most browsers, webkit namely, are not allowing a scrollTo immediately after height change, setTimeout(func, 0) needs to be used to get around this limitation.

@dcarrith
Contributor

@iandrewfuchs did you just happen to have scrollto.com in your domain stash? Funny.

I looked at your test page and unfortunately, I'm still seeing the abrupt scrolltop before the transitions. I understand your perspective that the long scrolls are not user friendly, but to a degree, I think it comes down to user preference. Personally, I prefer the smooth controlled scrolls over the abrupt and jumpy scrolls. However, I do agree that if we can make it so the user does not ever see the scrollTop (whether it's scrolling to the top, or it's scrolling back down to last scroll position when returning to a page) then that would be preferred over the animated scrolls.

The way I see it, if we can control the scroll and each phase of the transition, then we can pretty much do what we want with it. The way it is currently in v1.1, it seems to sometimes jump to scroll positions before it should (for example, the FROM page scrolls/jumps to the lastscroll of the TO page when going back - because of the call to window.history.back()) or it jumps to scroll positions that don't make any sense (for example, the call to toggleViewportClass() causes a scroll to 80 if in Chrome or 78 if in Firefox). I still haven't looked into that...I just commented it out.

It would be nice to be able to slow everything down so that we can observe what is happening when. The work I did with adding the animated scrolls is a good start I think. But, a step-through debugger would be nice too. Let's see what we can do.

@xibxor
xibxor commented Apr 22, 2012

@dcarrith I've had the domain for a while, yeah :)

I totally agree, transparent > smooth > jumpy

Just made some changes to test.html to make it easier to see the difference between page1 and page2.

For me, the scrolling is working fine, however there is a problem with some of the transitions flickering (youll see blue/red)

Maybe you are confusing the animation flickering with the scroll jumping, I believe it's fixed at the test.html page I've linked.

If not, what environment are you using to test? I'm using Chrome.

@xibxor
xibxor commented Apr 22, 2012

Actually, you are right, I do notice the initial scroll on the $from page. It happens really quick and is hard to spot though, is this the jump you are talking about?

@xibxor
xibxor commented Apr 22, 2012

The only way I can see to fix the initial jump on the $from page from being seen by the user is to totally redo the way scrolling is done/remembered in JQM. The problem is that there's no real way to hide a change in scrollTo, either $from will be effected or the $to page, even if it's done in the middle of the transition, it'll just cause some graphical ill-looking glitches.

The ideal way is to webkit-transform/margin-top/top/etc.. the $from page along with the change in scrollTo, basically to neutralize the jump. Then remove the webkit-transform/margin-top/top/etc once the $to page is shown.

How does that sound?

This code seems to work

       (function(){
        var wst = window.scrollTo;
        window.scrollTo = function(x,y){
            if ($.mobile.activePage){
                if ($.mobile.activePage.hasClass(".ui-page-pre-in")){
                    $.mobile.activePage.css("margin-top", "-" + y + "px");
                }else{
                    $.mobile.activePage.css("", "");
                }
            }
            $(document.body).height(y+$.mobile.getScreenHeight());
            setTimeout(function(){
                wst.call(window, x, y);
            }, 0);
        };
    })();

It's currently saved here: http://scrollto.com/test.html

@dcarrith
Contributor

Yes, the initial scroll to top if going forward and the jump down to lastscroll if going back. Those are the jumps I'm referring to. It gets worse the longer the list. And, really, I have to wonder why the call to window.history.back is causing the jump down to last scroll of the $to page while still on the $from page. It shouldn't scroll to last scroll until after the doneIn phase of the transition of the $to page. To remove that scroll, I had to remove the call to window.history.back in the click event handler (line 3586) and use a custom method using the urlHistory stack instead. Anyway, yeah, let's work on this patch. Well, It's almost 3AM here, so I'll be logging off now. Cheers.

@xibxor
xibxor commented Apr 22, 2012

Ah, I had some major problems with PhoneGap and JQM with the back button as well.

Currently using this hand-rolled code:

(function(){
var cp = $.mobile.changePage;
var stack = [];
$.mobile.path.set = function(){};
$.mobile.changePage = function( toPage, options ) {
if (!stack.length || toPage[0] != stack[stack.length - 1][0]){
    stack.push(toPage);
}
cp.apply($.mobile, arguments);
};
window.history.back = function(){stack.pop();cp.call($.mobile,stack[stack.length-1],$.mobile.activePage.is(".ui-dialog")?({transition:$.mobile.defaultDialogTransition,reverse:true}):({}));};
})();

I edited that test.html with some new code so do test that and let me know if you solve it. Cheers, good night!

@dcarrith
Contributor

Your hand-rolled code for window.history.back looks interesting. In other news, I think I made some progress in controlling the blinkyness caused by the fixed toolbars during transitions (fade in particular). I created two more videos showing the difference and then replied to my own post here: http://bit.ly/IcJAYX briefly explaining what I did. I have since made two other tweaks that improved it a bit more too. I still like the smooth scroll, but would also like to perfect the instant scroll top and last scroll to make it transparent to the user. Your final example a couple posts above almost has it...but, I did still see the jump to top a few times while testing it out.

@toddparker
Contributor

Hi guys - This is all a very interesting conversation, we're keep tabs on this. I don't know if we'd end up adding this smooth scroll idea as part of the core handers, if there are tweaks to timing or CSS that we can layer in, we'd be open to that. Might make sense to publish a more polished version of this in a repo somewhere if people want to go with this approach.

@dcarrith
Contributor

FYI, I've submitted a pull request to fix one of the jumps caused by the call to window.history.back for links with data-rel="back" attributes.

#4535

I also started a small repo with some files that people can use if they want to use the smooth scroll to top. It's more than that though, the jquery.mobile.defaults.overrides.js contains a heavily modified createHandler function that includes a lot of changes that seem to have gotten rid of a lot of the jumpiness and blinkiness on Android. See the README for some notable exceptions. Some of the blinks were addressed by slowing down the transitions.

http://dcarrith.github.com/jquery.mobile.smooth.scrollTo

@pwalczyszyn

Just wanted to share a workaround I found for transitions flickering and jumps when using jQM 1.1.0 and PhoneGap, it is here: http://outof.me/fixing-flickers-jumps-of-jquery-mobile-transitions-in-phonegap-apps/

@arschmitz
Member

no updates in over a year and conflicts closing

@arschmitz arschmitz closed this Aug 6, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment