simple css animation not smooth in Safari - html

I have a weird behaviour on a website in Safari. I want to expand a menu from height 0px to height 100% with a css transition. This works properly in Firefox, Chrome and Edge. However, in Safari, there is always a breakpoint where the animation stops for a really short period, causing a laggy animation. I checked that no element is on the same z-index. I found a "fix" on a homepage, which is indicated by a comment in the css, but that does not changes anything.
.dropdown-nav{
position: fixed;
display: block;
z-index: 21;
width: 100%;
height: 0;
left: 0;
background-color: white;
top: 0;
padding: 0;
transition: height 0.6s ease-out;
-webkit-transition: height 0.6s ease-out;
-webkit-backface-visibility: hidden;
-webkit-transform-style: preserve-3d;
/* Enable hardware acceleration to fix laggy transitions */
-webkit-transform: translateZ(0);
-moz-transform: translateZ(0);
-ms-transform: translateZ(0);
-o-transform: translateZ(0);
transform: translateZ(0);
}
.dropdown-nav-visible{
height: 100%;
}
In my js-script, I simply toggle the class .dropdown-nav-visible onto the .drop-down-nav
$('#nav-icon4').click(function(e){
e.preventDefault();
$(".dropdown-nav").toggleClass("dropdown-nav-visible");
$(this).toggleClass('open');
});
Here you find the laggy behaviour: https://magnavoce.ch
and here the same setup, but it works: http://dev5.raphael-rapior.com/.
I also tried using animation-duration like suggested in a similiar question on SO. I also tried removing every other part of the site, still the same.
Edit: safari 9 seems to not have this problem, but safari 12

Height transitions are heavy (they recalculate too many things at each frame), if possible you should use transform instead. Other than that, you may try to add will-change: height
ex:
.myNav {
transform: translateY(-100%);
transition: transform 0.15s;
}
.myNavActive {
transform: translateY(0%);
}

Related

Why does this CSS a::after show transition after hovering?

I found some code for a link hover effect and while it works fine, I don't understand why it works.
Specifically:
#navbar a:after {
content: '';
position: absolute;
left: 0;
display: inline-block;
height: 1em;
width: 100%;
border-bottom: 1px solid;
margin-top: 10px;
opacity: 0;
-webkit-transition: opacity 0.35s, -webkit-transform 0.35s;
transition: opacity 0.35s, transform 0.35s;
-webkit-transform: scale(0,1);
transform: scale(0,1);
}
#navbar a:hover:after {
opacity: 1;
-webkit-transform: scale(1);
transform: scale(0.9);
}
This produces an underline effect on the link when hovering.
My question is:
1.) Why doesn't the transition/transform on the a:after take place when the page loads? Why does it only occur when hovering over the element (even though it's not within the hover)?
Although I can obviously see what is occurring from viewing the page, trying to better understand how exactly this works.
I have added one fiddle where you can go and check the code
[https://jsfiddle.net/vickykumarui/96xw3fzv/][1]
Now let me explain what is happening on hover
Initially you have add this code for pseudo element after
transform: scale(0.1); // The scale() function is specified with either one or two values, which represent the amount of scaling to be applied in each direction.
opacity: 1; // initially after element is not visible
Now on hover this property changes to
transform: scale(0.9);
opacity: 1;
When these properties changes it does not changes suddenly but it changes slowly in .35s in animated way from this code
transition: opacity 0.35s, transform 0.35s;
transition is applied on both property opacity and transform and 0.35s is time of transition
Note: Based on your comment if you change initial property to
opacity: 1;
transform: scale(0.9);
You see that coming initially also
It does happen. Change the opacity to 1 in the first rule. You don't see it because it's technically hidden when the page loads. When you hover, the opacity becomes one and becomes visible.

Transform scale() Safari bug when used on elements with border

CSS transform scale() function appears to have a bug on Safari when it's used on elements with a border.
I'm trying to zoom an image on mouse over using transform: scale() function but if the image has a border then it gets pixelated when scaled.
Here is a sample of the same element with the same CSS rules applied (except the border):
Code example: https://jsfiddle.net/m6g4kw30/
div {
text-align: center;
}
img {
height: 100px;
-webkit-transition: all .3s ease;
-moz-transition: all .3s ease;
-o-transition: all .3s ease;
-ms-transition: all .3s ease;
transition: all .3s ease;
border: 1px solid #000;
margin: 20px;
}
img.noborder {
border: none;
}
img:hover {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transform: translateZ(0) scale(5);
-moz-transform: scale(5);
-ms-transform: scale(5);
-o-transform: translateZ(0) scale(5);
transform: translateZ(0) scale(5);
}
<div>
<img src="https://via.placeholder.com/1000.png" alt="">
<img src="https://via.placeholder.com/1000.png" class="noborder" alt="">
</div>
CSS transform scale() function appears to have a bug on Safari when it's used on elements with a border.
You can say that again! Unfortunately, the reported bug(s) for this (and similar) issues go back many years, with the following bug referenced in most:
https://bugs.webkit.org/show_bug.cgi?id=27684 (Opened in 07/2009)
If you didn't catch the date, it's a 10 year old bug that's still causing developers issues today! YIKES.
Basically, the issue comes down to Safari rasterizing the layer. On transform/scale, it resizes the layer, however it does not re-render the rasterized layer. In your use-case, the rasterized image is scaled up, but the text/image is blurry.
As for a workaround/fix? There are a couple ways you can "address" this:
1) Force a re-render
A quick/easy fix is to force Safari to re-render your layer when you transform. One way this can be achieved is by applying a CSS property which you then change after transforming (some people have success changing a background-color, for example). For your specific use case, I had luck with the following combination:
img {
outline: 1px solid #000;
border: none;
}
img:hover {
outline: none;
border: 1px solid #000;
}
By toggling those specific values, I was able to force Safari to re-render the rasterized layer, thus rendering a sharp image (similar to the non-border example). Here's a JSFiddle with the full code example: https://jsfiddle.net/gc56brfh/
2) Scale down, then up
Another workaround, documented here, is to set the element's initial size to the "scaled up" dimensions, and then scale down the element until you're ready to scale it up. That way, the element is rasterized to the correct dimensions.
CSS wise, that may look like:
img {
-webkit-transform: translateZ(0) scale(0.2);
height: 250px;
}
img:hover {
-webkit-transform: translateZ(0) scale(1);
}
In the above, we've set the initial size of the img to 250px (this is based on your original css, with images being 50px and then scaled up 5). We then scale down the image by 0.2, resulting in 50px. On hover, we then scale back up to 250px by setting scale(1).
Here's an updated JSFiddle: https://jsfiddle.net/df2zqgnx/
One thing to note is that other CSS properties might need to be updated with this workaround. For example, you'll notice in the fiddle I also needed to update the border from 1px to 5px to compensate for the scaling down.
Anyway, hope this was helpful and one of the solutions works for you!

CSS translatey to move to the next DIV using anchors

I have a bootstrap template which include a navigation bar with 3 links. Each link point to an anchor.
Each anchor is a SECTION with height set to 100%. The scrollbar is hidden so the only way to navigate to the next section is by using the navigation bar.
I want to add an animation while the anchor is change.
I setup my template and the animation but I don't understand why my DIVS goes offset instead of scrolling as expected.
My JSFiddle is https://jsfiddle.net/raffaeu/qu4skwf4/
I set the transitions as following:
#home:target{
-webkit-transform: translateY( 0px);
transform: translateY( 0px );
}
#about:target{
-webkit-transform: translateY(-100%);
transform: translateY(-100%);
}
#contact:target{
-webkit-transform: translateY(-200%);
transform: translateY(-200%);
}
And this is how I set the animation for each section
section {
width: 100%;
height: 100%;
z-index:0;
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-transition: -webkit-transform 0.6s ease-in-out;
transition: transform 0.6s ease-in-out;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
I belive that in this case CSS translate is unnecessary and even plain wrong approach. Scrollbar is invented for a reason, and you should not take it away from page visitors. Also, think about mobile users.
I suggest better using jQuery.offset function and add animation, like this:
function scrollToAnchor(aid){
var aTag = $("a[name='"+ aid +"']");
$('html,body').animate({scrollTop: aTag.offset().top},'slow');
}
scrollToAnchor('id3');

Phonegap transitions on CSS transforms not rendering.

I'm having a bit of trouble working with CSS transitions on phonegap. We're transitioning a transform: translate3d property to have content views slide in and out of view, as well as sliding to reveal a side bar.
Here's an example of the webapp (note the transitions work in browser):
http://ferriesapp.ca/app/
You will see that the transitions work perfectly in browser both on phone and on desktop. But as soon as I "phonegap" it the transitions just don't render. The final position shows up, for example if I hit a departing list item it will show the corresponding arrival list items. But for some reason the animation just doesn't happen on phone gap. The only exception is that it works on iOS7 when put through phonegap, but not iOS 6 or any version of Android.
Here's a small snippet of the code:
Here is what the CSS on the sliding element looks like
#depart-content {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
transform: translate3d(0,0,0);
-webkit-transform: translate3d(0,0,0);
-moz-transform: translate3d(0,0,0);
-ms-transform: translate3d(0,0,0);
-o-transform: translate3d(0,0,0);
}
Here's the animation class:
.animate {
transition: transform 0.2s ease-out;
transition: -webkit-transform 0.2s ease-out;
transition: -moz-transform 0.2s ease-out;
transition: -ms-transform 0.2s ease-out;
transition: -o-transform 0.2s ease-out;
}
And finally the JS that calls it (note this is not the whole function):
$("#depart-content").css({"transform":"translate3d(-100%,0,0)","-webkit-transform":"translate3d(-100%,0,0)","-moz-transform":"translate3d(-100%,0,0)","-o-transform":"translate3d(-100%,0,0)","-ms-transform":"translate3d(-100%,0,0)"});
If anyone has any suggestions they would be hugely appreciated
Thanks!
P.S. I know there is a CSS3 #keyframes rule that works quite well, but I'd rather avoid that for simplicity of code
Your statement about it working on iOS7 but not on iOS6 leads me to believe that this is happening due to your translate3d statement.
I am assuming you put in this line to enable hardware acceleration -
transform: translate3d(0,0,0);
However, there are several reports that this does NOT infact trigger hardware acceleration in iOS 6 devices.
In the article I quoted, the author mentions that using any one of the following commands, will trigger hardware acceleration.
-webkit-transform: translateZ(0);
-webkit-perspective: 1000;
-webkit-backface-visibility: hidden;

Hide div after CSS3 Animation

Would like to know how to hide an div after a set of css3 animation. Here's my code:
#box {
position: absolute;
top: 200px;
left: 200px;
width: 200px;
height: 150px;
background-color: red;
}
#box:hover {
-webkit-animation: scaleme 1s;
}
#-webkit-keyframes scaleme {
0% {
-webkit-transform: scale(1);
opacity: 1;
}
100% {
-webkit-transform: scale(3);
opacity: 0;
display: none;
}
}
<div id='box'>
hover me
</div>
Here's the jsfiddle sample for better illustration:
http://jsfiddle.net/mochatony/Pu5Jf/18/
Any idea how to do hide the box permanently, best without javascript?
Unfortunately there is no best solution using only CSS3. Animations always return to theirs default values (see at Safari Developer Library).
But you can try to play with -webkit-animation-fill-mode property.
For example:
#box:hover{
-webkit-animation:scaleme 1s;
-webkit-animation-fill-mode: forwards;
}
It's at least not immediately return a box to display:block; state.
Using JavaScript you can do this by using webkitAnimationEnd event.
For example:
var myBox = document.getElementById('box');
myBox.addEventListener('webkitAnimationEnd',function( event ) { myBox.style.display = 'none'; }, false);
Example on jsFiddle
Change your animation definition to:
-webkit-animation:scaleme 1s forwards;
This is a value for the animation fill mode. A value of 'forwards' tells the animation to apply the property values defined in its last executing keyframe after the final iteration of the animation, until the animation style is removed.
Of course in your example the animation style will be removed when the hover is removed. At the moment I can see the need for a small piece of JavaScript to add a class which triggers the animation. Since the class would never be removed (until the page is reloaded) the div would stay hidden.
Since elements of CSS animations end in their original CSS state, make the original state hidden by scaling it to zero or removing its opacity:
div.container {
transform: scale(0);
-webkit-transform: scale(0);
}
or
div.container {
opacity: 0;
}
Once the animation is completed, the div will go back to its original CSS, which is hidden.
That can (kind of) be solved without using JavaScript. Since animations use keyframes, what you ask for is possible by setting the duration time to a way too high value, say 1000s, and letting you transition end at a low frame, for example 0.1%.
By doing this, the animation never ends and therefore stay in shape.
#box:hover {
-webkit-animation:scaleme 1000s;
}
#-webkit-keyframes scaleme {
0% { -webkit-transform: scale(1); opacity: 1; }
0.1%, 100% { -webkit-transform: scale(3); opacity: 0;display:none; }
}
1000s is not necessary in this particular example though. 10s should be enough for hover effects.
It is, however, also possible to skip the animation and use basic transitions instead.
#box2:hover {
-webkit-transition: all 1s;
-moz-transition: all 1s;
-o-transition: all 1s;
transition: all 1s;
-moz-transform: scale(3);
-webkit-transform: scale(3);
opacity: 0;
}
I forked your fiddle and altered it, adding the two for comparison: http://jsfiddle.net/madr/Ru8wu/3/
(I also added -moz- since there is no reason not to. -o- or -ms- might also be of interest).