Jump to content
Search Community

Page not updating correctly in Safari and iOS

danehansen test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hey so i'm not sure if this is a greensock bug or a browser bug or me just pushing the limits too much, but what i have noticed on a couple projects i've started lately is that where i may have a bunch of tweens going on successfully in Chrome or Firefox, that when i test in Safari or on my iPhone, the animations dont seem to fully update visually. if i resize the window or anything like that, the screen corrects itself. where i've run into it, the problem doen'st happen if only 1 or two things are tweening at once, but if a more than a handful are tweening at once, it seems that the browser cant keep up or something. if you zoom in or out or rotate the screen or something to get the browser to react, the end of the animation will rectify itself after its been messed up. even with the window resize listener turned off.

 

you can check out one project currently in progress where you can view this. to see them problem, in safari or on iphone open the link, then click between "bird" and "big bang". you will see that the divs and the nav elements will linger in Safari and iOS, yet not in Firefox or Chrome. if you step across the nav one at a time, (big bang, liquid, snakes, globe, wedges, candle, bird...) then it tweens fine in all browsers.

http://danehansen.com/temp/gs

Link to comment
Share on other sites

Well, after plenty of frustrating trial and error, I have isolated what I believe is the issue and it is indeed a bug in Safari. Here is proof:

 

<!DOCTYPE html>
<html>
<head>
<title>iOS Safari Bug Proof</title>
<script type="text/javascript">

	function init() {
		var d1 = document.getElementById("d1"),
			top = 0,
			rotation = 0;

		//uncomment the next line and it'll work.
	    //d1.style.WebkitBackfaceVisibility = "hidden";

		setInterval(function() {
			rotation += 1;
			top += 1;
			d1.style.top = top + "px";
			d1.style.WebkitTransform = "rotate(" + rotation + "deg)";
		}, 50);
	}

</script>
</head>
<body onload="init()">
<div id="d1" style="width:100px; background-color:#FF0000; height:60px; position:absolute;"></div>
</body>
</html>

 

That's a simple chunk of code that should move the box down the screen while rotating it. Works fine in Chrome, but in Safari (both on the desktop and in iOS) it doesn't move down! The "top" property change is basically ignored when it is altered on the same frame as any transform property (rotation, scale, skew, etc.). The workaround, strangely enough, is to make sure you set a WebkitBackfaceVisibility to "hidden" on all your elements. Setting the zIndex too is a good practice because I saw some cases where Safari delayed rendering transform changes to objects that didn't have a zIndex defined until after you scroll the screen which seems to force a refresh. What a pain!

 

As you can see, this has nothing to do with TweenLite/Max or CSSPlugin (none of those are used in the example).

 

I just updated CSSPlugin so that it'll do that extra work for you. If it senses you're using Safari and you tween an element that has no WebkitBackfaceVisibility applied, it'll set it to "hidden". It will also ensure that the object has a zIndex.

 

Side note: Setting WebkitPerspective to a non-zero value also seemed to fix the funky render delays and ignoring "top" and "left" changes, but it actually causes some odd random flickering in Safari on iOS!

 

Gotta love wrestling with browser bugs!

 

Anyway, please download the latest version of GSAP and let me know if that resolves things for you.

  • Like 1
Link to comment
Share on other sites

Update: setting WebkitPerspective apparently triggers another iOS Safari bug that makes things randomly flicker when changing size, particularly in elements that have a background-image! Again, this is unrelated to TweenLite/CSSPlugin - it's a browser bug. So I switched to setting WebkitBackfaceVisibility to "hidden" and zIndex to a number which seems to work around all 3 bugs as described here: http://forums.greensock.com/topic/6390-animation-not-playing-on-mobile-safari-refresh/page__p__23135#entry23135

 

Please give the latest version a shot and let me know if it works well for you.

Link to comment
Share on other sites

  • 1 month later...

I came across this thread because I was trying to figure out why my code was working differently on ios safari vs chrome or osx safari. It turns out that your fix of adding a z-index to the style attribute messes up the ability to manage z-index sorting via classes. The z-index on the style tag has a greater specificity than the classes and overrides the z-index in the classes.

 

Here's what I'm doing. I have a bunch of divs that I'm tweening across the screen. You can hover/click on them to bring them to the front and give them focus, and you can also click a zoom icon that is added on hover/click to enlarge the image and view a div of details about it. The positioning is all dynamic but I am trying to separate my code as much as possible and am using classes for depth management. The divs are given different classes to manage their z-index and size.

 

.top {
width:15%;
z-index:500;
}
.middle {
width:20%;
z-index:300;
}
.bottom {
width:30%;
z-index:100;
}

 

On click or hover, a "hover" class is added to the div, the timeScale is tweened to 0 and a zoom icon is added as well. The Hover class changes the z-index of the div so other animating divs pass below it and you can focus on it. Additionally, the objects can be dragged over a drop target (z-index:1100) and zoomed in.

 

.hover {
z-index: 1000;
}
.drag {
z-index: 1200;
}
.zoom {
z-index:1300;
}

 

On the desktop this was all working fine. On iOS, I was having some weird depth management issues. I ended up creating a function to write to a div the classes and depth of each of the animating divs. Turned out that after a drag or zoom, the z-index on the style attribute would be set to 0, which overwrites the styles being applied in the classes.

 

I understand the need for the hack but now it looks like I'll have to take a bunch of style information out of the stylesheets where it belongs and manage styles in the .js.

 

Hard coding the z-index in the style tag seems like a pretty big casualty and will have a lot of people scratching their heads when things break on their iPad. If this is something that needs to be done, would it be possible to find out if the z-index is being set in a class already, use that value in the style tag during the animation, and remove it on complete?

 

Thanks Jack, and thanks for bringing the library to .js.

Link to comment
Share on other sites

Hey Jack,

 

Worked like a charm! Thanks for the quick fix.

 

When I run the zoom function on my objects, they do a slight rotation. After I zoomed an image out on my ipad, I used a bookmarklet to view the source after the zoom. The z-index was removed from the style attribute, but there were still other attributes that were injected and tweened during the zoom in/out.

 

<div class="top ui-draggable hover" style="-webkit-backface-visibility: hidden; width: 153px; -webkit-transform: translate(0px, 0px); left: 568px; top: 65.75675724572541%; "><img src="assets/images/photos/small/dock.jpg"></div>
<div class="middle ui-draggable" style="left: 25%; top: 3.1960432057430097%; "><img src="assets/images/photos/small/massage.jpg"></div>
<div class="bottom ui-draggable" style="left: 13%; top: 39.46143888516876%; "><img src="assets/images/photos/small/resort.jpg"></div>

 

As mentioned previously, I'm using TweenMax to animate the top and left css attributes and trying to handle all other styling via stylesheets. Removing the z-index fixes my specific problem, but it seems that since we now have other styles injected into the style attribute that could cause unexpected problems down the road.

 

-webkit-backface-visibility: hidden;
width: 153px;
-webkit-transform: translate(0px, 0px);

 

Say I have a class I want to add to that "dirty" element that affects the width or rotation. It won't get updated because the style attribute on the div will override it. It seems that it would make sense to have some kind of a cleanup function to track what is managed by css and what is added by the javascript so you can reliably add classes and get expected results. Or would that affect performance too much to track and check all of that?

 

Thanks Jack,

Paul

Link to comment
Share on other sites

Have you ruled out adding !important to the styles of your class that need to take precedence over the animation values?

 

I understand what you mean about trying to contain all of your styles in classes, but it seems like a source of confusion (and potential performance drain) for TweenMax to manage 'clean up' as well. What constitutes a style that should be removed vs one that should be kept (just helpers like -webkit-backface-visibility / all styles the tween has affected / all styles that match the values of current class styles?)? Should a clean up wipe a tweened style completely or restore it to the value it had when the tween was initiated?

 

e.g.1. Your element currently has a class giving width:200px.

You tween to width:150px.

Now you add a class to change to width:250px, and would like TweenMax to completely remove the style it tweened i.e width:150px;

 

e.g.2. Your element currently has a class giving width:200px.

You tween to width:150px.

You then tween it from width:150px to width:100px.

Now you add a class to change to width:250px, and would like TweenMax to completely remove the style it tweened i.e width:100px;, but should it instead revert to width:150px?

 

Maybe I'm overthinking this and Jack has an awesome idea about how to do this well.

 

It might be easy in your case to just remove the specific styles you are worried you'll need to also change from a class in an onComplete function. You seem to be using jQuery so clearing these styles should be simple.

 

You could also take a look at CSSPlugin's className property. Using it to add and remove classes might help overcome this issue with cascading styles.

Link to comment
Share on other sites

idahotallpaul, I'm curious to see an example of this "injected" data. I have no idea why (or how) TweenLite would be suddenly tweening "width", for example, if you didn't specifically request that. Or maybe you were talking about the -webkit-backface-visibility? Could you post the example so that I can see exactly what you're talking about?

 

jamiejefferson, I don't think you're over-thinking things at all. You've got it exactly right - it can quickly become very murky when trying to figure out what the intent of the developer is and also balance performance concerns, etc.

 

Tweens should affect the style attribute of the element so that they take precedence, otherwise they may not work at all. I don't think it would be reasonable to expect a class-level style to then (after the tween) take precedence over the tweened property. And I'm not sure what kind of "clean up" you'd want the tween to do after it's done that wouldn't negatively affect its appearance. Like if it set -webkit-backface-visibility:hidden in order to work around a bug in iOS, would you really want that removed after the tween, thus exposing the browser bug? I'm certainly open to ideas here and maybe I'm missing something obvious, so please do chime in if you have some recommendations for specific logic that we could implement.

 

And Jamie makes a good point about using className which at the end of the tween will attempt to clear out the style attributes and just apply the class, but the down side is that there's a performance penalty at the start of the tween because it has to analyze every property to see which ones changed so that only those are tweened. Probably not noticeable unless you're doing LOTS of simultaneous tweens.

Link to comment
Share on other sites

Thanks Jamie and Jack. I've attached a zip showing what I'm trying to do. All .js, .css and html are in index.html in the folder.

 

So basically, I'm creating an interactive slideshow that lets the user interact with the images, zoom them in for a better look, and save them to their folder.

 

I have a bunch of images in divs. A class on each image div determines it's z-index and width as a percentage of the browser width. Images float from bottom to top of screen. The x position is determined as a percentage of the browser width and is set randomly within a range. I use jquery to loop through the image divs, create a bunch of tweens, and then fire the tweens on interval so that no more than 6 tweens are running at once.

 

As the tweens move from bottom to top, you can click on them to pause them and pop them above the other images. Clicking on the zoom icon creates a new tween to scale the image up via setting the width of it's parent div. It also adds a rotation. Eventually there will be an info pane next to it. Clicking the close button reverses that tween, creates a new bottom to top tween, and uses the progress saved when the tween was stopped before to make the tween continue on it's way.

 

Additionally, you can drag the images to the folder at the bottom of the screen. Later, you'll be able to bring focus to that folder, which will cover the screen and show you the images you'd saved before and let you sort them, remove them, etc. That will mean different styles being applied to the same elements.

 

So the zoom tween is setting the width of the image, and although it is declared as a percentage, and the class that originally sets the width of the image is a percentage, after that zoom tween is reversed, there is a pixel width still sitting on the style attribute of the div. Intuitively, I'd think that if a tween is reversed it'd go back to the original state, which didn't have a width in the style. Additionally, the left is now set to a pixel value and not a percentage. Resizing the browser shows that any images that have been zoomed are now fixed, as the other images scale their widths and locations.

 

I put the top, left, width and rotation along with the z-index in the .zoom class but the top and left won't update for me there. Not sure what the deal is.

 

To summarize, I create a tween, stop it, run a new tween for the zoom, reverse it, and then continue the original tween. After that, values on the style attribute are set to pixels instead of percentages, css properties that have been tweened and reversed are still present and they override stylesheets previously applied.

 

If tweening the class worked reliably and it removed the data on the style attribute on complete, it seems that might solve the problem. Otherwise, it looks like I'll have to write an onComplete method to search for and remove styles manually.

 

It seems it'd be good to have an optional GSAP method to call, or a flag to set on an animation to clear all styles not being animated or clear specific styles. (cleanOnComplete/cleanOnCompleteParams? or TweenMax.cleanStyles(targetObject, [targetStyles])?) That could be set on the tween or called on complete to tidy things up.

 

Again, thanks for your input and help.

image-collection.zip

Link to comment
Share on other sites

Just testing on Chrome here:

 

As far as I can see, zoomin/zoomout is not removing your left:xx% from the style. It is animating from a percentage, to 10px, and then back to the percentage. I'm only seeing the problem after dragging the picture around, and this is because jQueryUI draggable works in pixels. You can probably just run a bit of extra code in dragStop() to convert from the pixel position back to %.

 

Intuitively, I'd think that if a tween is reversed it'd go back to the original state, which didn't have a width in the style.

This is the same as discussed before, where getting the tweening engine to accurately reset it's target is a problem that, unless you have a bright idea, doesn't seem feasable for greensock to handle.

 

Say you run your zoom in tween and width is animated up to 60%, then before you 'reverse' the zoom in tween you manually set (or tween) the width to 70%. Should a 'TweenLite clean up' be aware of this change? Is it still correct for TweenLite to remove the width completely from the style now that you could have another existing tween affecting width, or have made the explicit choice to manually set it?

 

When should clean up be run? onComplete? onReverseComplete? what about pausing the tween? or killing it? If the tween cleans itself up can it still be interacted with and played again?

 

I'm not suggesting that these situations are going to necessarily apply in your program, but the amount of variables for this to become a feature of greensock seems far too convoluted for it to be managed effectively, considering the ease it gives you to manage this yourself. You already have setup onReverseComplete:zoomPhotoOut, and it's not difficult to clean up there by adding

targetObject.css('width', '');
targetObject.css('transform', '');

  • Like 3
Link to comment
Share on other sites

  • 1 month later...
  • 1 year later...

I was having trouble with Safari glitching out crazy style when I was animating some divs within a pinned div, and setting -webkit-backface-visibility: hidden !important; and that solved the problem. I don't know if this is directly related to the bug referred to in this thread, but reading this thread helped me figure out how to solve the problem, so <3 :) 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...