I'm trying to get a smooth transition on an element with transform: translate() and some other properties. (Yes, I've read about matching vendor prefixes.)
It works fine in Chrome and FF, but in Safari it doesn't animate the transform smoothly (jumps at the end). It seems to animate the width and padding fully before applying the translation. Note: I do not want to use transform: scale().
(Same as this question, which hasn't been answered in over a year.)
See this stripped-down fiddle:
https://jsfiddle.net/aikenst/og2kLf31/
var square = document.getElementById("square"),
bool = true;
setInterval(function() {
if (bool) {
square.className = "transformed";
} else {
square.className = "";
}
bool = !bool;
}, 3000);
#square {
position: absolute;
top: 10px;
left: 10%;
width: 100px;
height: 0;
padding-top: 80px;
background: pink;
-webkit-transform: translate(-50%, 5%);
transform: translate(-50%, 5%);
-webkit-transition: -webkit-transform 2s, width 2s, padding 2s;
transition: transform 2s, width 2s, padding 2s;
}
#square.transformed {
width: 50px;
padding-top: 30px;
-webkit-transform: translate(-50%, 65%);
transform: translate(-50%, 65%);
}
<div id="square"></div>
Related
I currently have the following code in my angular app (2+):
.header {
background: rgba(white, 0);
&.fixed-top {
background: rgba(white, 1);
border-bottom: solid whitesmoke 1px;
position: fixed;
top: 0;
right: 0;
left: 0;
z-index: 1030;
}
}
<nav class="navbar navbar-toggleable-sm header" [class.fixed-top]="stickyHeader" (scroll)="scrollHandler()">...</nav>
The handleScroll() function simply sets stickyHeader to true after the user scrolls down "enough" pixels, and so the header menu becomes sticky. Here it is:
stickyHeader = false;
#HostListener('window:scroll', [])
scrollHandler() {
this.stickyHeader = window.scrollY > 90;
}
My question is: how can I make that menu appear to slide (animated) from top, as if it descended from above the browser?!
I am able to get the desired result by animating transform: translate using CSS animations
I have set animation-iteration-count to infinite for demo purposes. In your case it would be 1
To control the speed use animation-duration
I also use animation-fill-mode and set it to forwards to stop the animation at the end and not have it revert to the original state.
I added transform: translate(0, -20px) to .fixed-top to move it out of the display area until the animation starts.
Finally, I added animation-timing-function: ease; to control how the animation plays.
body {
margin: 0 auto;
padding: 0;
}
.fixed-top {
background: red;
z-index: 1030;
width: 100%;
height: 50%;
animation-name: slide;
animation-duration: 2s;
animation-timing-function: ease;
animation-iteration-count: infinite;
animation-fill-mode: forwards;
transform: translate(0, -20px)
}
#keyframes slide {
0% {
transform: translate(0, -20px);
opacity:.1
}
100% {
transform: translate(0, 0);
opacity:1
}
}
<div class="fixed-top">test</div>
Animation i've created works fine on Chrome and Firefox, but is pixelated on Safari (version 10.1.1) and IE11.
Tried using translateZ() / translate3d() so the gpu can render the animations but nothing happened.
I've avoided using top, left props. Had an idea of using the will-change prop but it doesn't take animation as a value.
Removing the border radius would fix the rendering issue.
Can someone explain the cause of this and is there a solution to fix this issue?
https://codepen.io/imrdev/pen/awBZOW
html ->
<div class="dot"></div>
css - >
/* KEYFRAME ANIMATION */
#keyframes ease {
0% {
transform: scale(0) rotate(0);
}
50% {
transform: scale(4)
rotate(.01deg);
}
100% {
transform: scale(0) rotate(0);
}
}
#keyframes ease2 {
0% {
transform: scale(0) rotate(0);
}
50% {
transform: scale(6)
rotate(.01deg);
}
100% {
transform: scale(0) rotate(0);
}
}
.dot {
$scale-duration: 15s;
background-color: black;
position: relative;
width: 7px;
height: 7px;
border-radius: 50%;
&::before,
&::after {
content: "";
background: red;
width: 7px;
height: 7px;
border-radius: inherit;
opacity:.3;
position: absolute;
transform: translate(0px, 0px);
}
&::before {
animation: ease 5s ease-in-out infinite;
}
&::after {
animation: ease2 5s ease-in-out infinite both $scale-duration/15;
}
}
Thanks :-)
I have not enough reputation so i can't comment yet, so sorry if this doesn't qualify as a proper answer, but have you tried changing the size to something bigger than 7px and use eg scale(1) instead of scale(4)?
if you need to scale the width and height up by 4 or 6, why not just double the original size and scale up by 2 ?
I wouldn't be surprise if safari doesn't really scale the size up, but kinda like "zooms in" and since the original size is just 7 x 7 px it gets pixelated when "zoomed in"
and regarding to the will-change: you wouldn't use "animation" but "transform"
I'm trying to create a 3d navbar using pure CSS with transforms, transitions and perspective.
Here is my code:
.navbar-fixed-bottom {
background: transparent;
}
.navbar-perspective {
width: 100%;
height: 100%;
position: relative;
-webkit-perspective: 1100px;
-moz-perspective: 1100px;
perspective: 1100px;
-webkit-perspective-origin: 50% 0;
-moz-perspective-origin: 50% 0;
perspective-origin: 50% 0;
}
.navbar-perspective > div {
margin: 0 auto;
position: relative;
text-align: justify;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
transition: all 0.5s;
height: 50px;
font-size:20px;
}
.navbar-primary {
background-color: #cccccc;
z-index: 2;
-webkit-transform-origin: 0% 100%;
-moz-transform-origin: 0% 100%;
transform-origin: 0% 100%;
}
.navbar .navbar-secondary,
.navbar .navbar-tertiary {
background-color: #bfbfbf;
width: 100%;
-webkit-transform-origin: 0% 0%;
-moz-transform-origin: 0% 0%;
transform-origin: 0% 0%;
z-index: 1;
-webkit-transform: rotateX(-90deg);
-moz-transform: rotateX(-90deg);
transform: rotateX(-90deg);
-webkit-transition: top 0.5s;
-moz-transition: top 0.5s;
transition: top 0.5s;
position: absolute;
top: 0;
}
.navbar .navbar-tertiary {
background-color: #b3b3b3;
}
.navbar-rotate-primary {
height: 50px;
}
.navbar-rotate-primary .navbar-primary {
-webkit-transform: translateY(0%) rotateX(0deg);
-moz-transform: translateY(0%) rotateX(0deg);
transform: translateY(0%) rotateX(0deg);
}
.navbar-rotate-primary .navbar-secondary,
.navbar-rotate-primary .navbar-tertiary {
top: 100%;
-webkit-transition: -webkit-transform 0.5s;
-moz-transition: -moz-transform 0.5s;
transition: transform 0.5s;
-webkit-transform: rotateX(-90deg);
-moz-transform: rotateX(-90deg);
transform: rotateX(-90deg);
}
.navbar-rotate-secondary,
.navbar-rotate-tertiary {
height: 50px;
}
.navbar-rotate-secondary .navbar-primary,
.navbar-rotate-tertiary .navbar-primary {
-webkit-transform: translateY(-100%) rotateX(90deg);
-moz-transform: translateY(-100%) rotateX(90deg);
transform: translateY(-100%) rotateX(90deg);
}
.navbar-rotate-secondary .navbar-secondary,
.navbar-rotate-tertiary .navbar-secondary {
top: 100%;
-webkit-transition: -webkit-transform 0.5s;
-moz-transition: -moz-transform 0.5s;
transition: transform 0.5s;
-webkit-transform: rotateX(0deg) translateY(-100%);
-moz-transform: rotateX(0deg) translateY(-100%);
transform: rotateX(0deg) translateY(-100%);
}
.navbar-rotate-secondary-fallback .navbar-primary,
.navbar-rotate-tertiary-fallback .navbar-primary {
display: none;
}
.navbar-rotate-tertiary .navbar-secondary {
-webkit-transform: translateY(-100%) rotateX(90deg);
-moz-transform: translateY(-100%) rotateX(90deg);
transform: translateY(-100%) rotateX(90deg);
}
.navbar-rotate-tertiary .navbar-tertiary {
top: 100%;
-webkit-transition: -webkit-transform 0.5s;
-moz-transition: -moz-transform 0.5s;
transition: transform 0.5s;
-webkit-transform: rotateX(0deg) translateY(-100%);
-moz-transform: rotateX(0deg) translateY(-100%);
transform: rotateX(0deg) translateY(-100%);
}
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
<nav id="navigation-bottom" class="navbar navbar-fixed-bottom">
<div class="navbar-perspective">
<div class="navbar-primary">
Rotate To Face 2
</div>
<div class="navbar-secondary">
Rotate To Face 3
</div>
<div class="navbar-tertiary">
Rotate Back To Face 1
</div>
</div>
</nav>
</body>
</html>
I've got the first two faces to rotate properly using a 3d effect, but the third face does not look right. You will notice as you rotate from second to third that the top does not rotate correctly and looks flat.
Any help is greatly appreciated.
Fiddle with a flipping box
This is vastly different from where you started, but let me post my CSS and show you the fiddle, and then I'll edit in a longer explanation of how and why this works:
HTML
<section class="container">
<nav id="nav-box" class="show-front">
<div class="front">
Show Bottom
</div>
<div class="bottom">
Show Back</div>
<div class="back">
Show Top</div>
<div class="top">
Show Front</div>
</nav>
</section>
CSS
.container {
position: relative;
perspective: 1000px;
transform: scale(0.95);
}
#nav-box {
width: 100%;
height: 50px;
position: absolute;
transform-origin: center center;
transform-style: preserve-3d;
transition: transform 0.5s;
}
#nav-box div {
width: 100%;
height: 50px;
display: block;
position: absolute;
transition: background-color 0.5s;
}
#nav-box .front { transform: rotateX( 0deg ) translateZ( 25px ); background-color: #ccc; }
#nav-box .back { transform: rotateX( 180deg ) translateZ( 25px ); background-color: #ccc; }
#nav-box .top { transform: rotateX( 90deg ) translateZ( 25px ); background-color: #ccc; }
#nav-box .bottom { transform: rotateX( -90deg ) translateZ( 25px ); background-color: #ccc; }
#nav-box.show-front { transform: rotateY( 0deg ); }
#nav-box.show-front .bottom { background-color: #a0a0a0; }
#nav-box.show-front .top { background-color: #e0e0e0; }
#nav-box.show-back { transform: rotateX( -180deg ); }
#nav-box.show-back .bottom { background-color: #e0e0e0; }
#nav-box.show-back .top { background-color: #a0a0a0; }
#nav-box.show-top { transform: rotateX( -90deg ); }
#nav-box.show-top .front { background-color: #a0a0a0; }
#nav-box.show-top .back { background-color: #e0e0e0; }
#nav-box.show-bottom { transform: rotateX( 90deg ); }
#nav-box.show-bottom .front { background-color: #e0e0e0; }
#nav-box.show-bottom .back { background-color: #a0a0a0; }
Explanation of the HTML/CSS
Setting up our box
You started thinking about this the wrong way, I hate to say. You approached this as "How can I treat these four sides like a box" rather than "How can I make a box in CSS?"
So let's learn how to make a box.
First, we establish a box container. Since this is a navigation box, let's call it nav-box. All the transforms we apply (save for the shading, which we'll get to later) will be done on our nav-box.
The rules on our nav-box will determine how it behaves as an object. Let's discuss two in particular: transform-origin and transform-style
transform-origin defaults to center center, but I wanted to call it out here. This is basically going to tell our box: Hey, we need you to pivot around your absolute center. If we set this up as transform-origin: center bottom' it would look like the box is spinning around its bottom edge. center top` and it would spin around its top edge. I don't think that's what you want, though.
transform-style needs to be set to preserve-3d. What this does is instruct the browser to not fuss with the elements with transform underneath it. Other options include flat which tells the browser to ignore rotates underneath it. The reason we want to set preserve-3d on our nav-box here is to ensure the transforms we applied to the box sides are preserved when we transform the parent. Neat stuff, huh?
Setting up our sides
We're setting our sides as children of our nav-box and just positioning them in the order that they should be in using rotateX:
0 rotation for the front
180deg for the back
-90deg for the bottom
90deg for the top
We could also set a left and right side right now with .left { transform: rotateY(-90deg); } .right { rotateY(90deg); }. Note that we used the Y axis for those two examples.
Secondly, we set a translateZ value of 25px. So what the hell is this doing? It's telling our boxes they need to move 25px from the center of the parent relative to their respective rotations. Why did we choose 25px? Because it's exactly half the height of each of our boxes. This means that it will flush up nicely with the sides at either edge.
And then the fun part:
We shade the boxes based on their position and what is facing the screen. The background colors are relative to what side of the box we're showing with show-front, show-back, etc. The side on the bottom gets darker, the side on the top gets lighter. I just liked that – totally not necessary to accomplish this task but makes it look a little more realistic.
Hope that helps!
Update for IE
Fiddle Example
So, there's not much pretty about this once we get through fixing it up for IE, but here it is. All preserve-3d is doing is applying the transforms for you when we rotate a container, instead of flattening them. If we can't use preserve-3d, we have to calculate based on the amount of total rotation.
This solution does that. I won't go as in-depth on this one, rather than to highlight how much more JavaScript this requires, and to highlight the .rewind class:
#nav-box.rewind div {
backface-visibility: hidden;
}
Because we have to manually rewind this solution, we'll have to prevent the z-index reordering to be applied at the wrong times. That's where backface-visibility comes in.
Example showing depth in IE
Another example without the need for the rewind class
Hope that solves IE for you.
First of all, thank you to all that commented and answered to this question, especially Josh!
Josh, your example works perfectly for browsers that support preserve-3d. The update you posted without preserve-3d appears flat on IE so it was still not perfected for all browsers.
After three days of headaches, I realized the problem. The origin of the sides was not being set correctly. The sides need to rotate around a point that is half way in on the Z axis.
Once I've updated the origin to :
transform-origin: 25px 25px -25px;
Once this was correct, all you really need to do is update the rotation of the object. No need to use any transformation of the X,Y,Z coordinates.
Here's the fiddle and the solution for a 3D Navigation bar that rotates and works for all browsers including IE10+.
http://jsfiddle.net/tx0emcxe/
I'm making a notification system. I want this notification to show up like a box that turns, somewhat like some notifications in iOS that the top of the screen rotates like a cube.
Now, the front and back of the cube should be transparant/same color as background. When it turns, a shadow should fall over the sides that are not parallel to the front of the viewer, as if there is a lamp shining on the box. Can this be done?
To make more clear: Since the front & back of the box are the same as the background-color, when turning the box it wouldn't seem like a box turning but rather a slice of paper that rotates into place. So what I want is that faces of the cube get a shadow to it depending on their angle as opposed to the viewer.
For example, once the front-face (which you can't really see since it's the same color as the background-color) is rotated 1 degree, it should get a little darker/lighter. Another degree, a little more. So that the true color of the face is only shown when it's directly parallel to the user. This will create the illusion of there being a box, rather than a sliver of paper.
I'm using this tutorial on the cube: http://desandro.github.io/3dtransforms/docs/cube.html
Here is a fiddle: http://jsfiddle.net/BqJMW/3/
Another issue is that currently the text seems a bit stretched, if you know what I mean. Normally the transform: translateZ(-25px); (see code below) on the #cube should solve this, but it still seems out of proportion.
CSS
body {
background: #ebebeb;
}
.container {
width: 200px;
height: 50px;
position: relative;
-webkit-perspective: 1000px;
perspective: 1000px;
}
#cube {
width: 100%;
height: 100%;
position: absolute;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-transition: -webkit-transform 1s;
transition: transform 1s;
-webkit-transform: translateZ(-25px);
transform: tranlateZ(-25px);
}
#cube figure {
margin:0;
display: block;
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
#cube .front {
background: transparant;
-webkit-transform: translateZ(25px);
transform: translateZ(25px);
}
#cube .top {
background: green;
-webkit-transform: rotateX(-90deg) translateZ(25px);
transform: rotateX(-90deg);
}
#cube .back {
background: transparant;
-webkit-transform: rotateX(180deg) translateZ(25px);
transform: rotate(180deg);
}
#cube.show-front {
-webkit-transform:translateZ(-25px);
tranform: translateZ(-25px);
}
#cube.show-top {
-webkit-transform: translateZ(-25px);
transform: translateZ(-25px);
-webkit-transform: rotateX(90deg);
transform: rotateX(90deg);
}
#cube.show-back {
-webkit-transform: translateZ(-25px);
transform: translateZ(-25px);
-webkit-transform: rotateX(180deg);
transform: rotateX(180deg);
}
HTML
<section class="container">
<div id="cube">
<figure class="front">Front</figure>
<figure class="top">Your notification</figure>
<figure class="back">Back</figure>
</div>
</section>
By setting the initial colour of the notification face to a darker version of the final color, we can use a CSS3 transition on the color attribute of that face to animate it to a lighter colour as the face is rotated.
I've added a new class with the lighter "green" that will be added/removed to/from the notification face and changed the initial color added a new transition to #cube .top.
I've also corrected some typos in the CSS (tranform → transform, transparant → transparent) and removed the duplicate -webkit-transform:translateZ(-25px); and non-prefixed version from the .show-front|top|back classes as they are being overridden in the same class.
Lastly, since the notification face is translated towards the viewer by 25px the text looks blurry (on Chrome). This seems to go away by removing the -webkit-perspective: 1000px; for me. I'll leave that up to you if you want to remove it.
See the demo or following code:
CSS
body {
background: #ebebeb;
}
.container {
width: 200px;
height: 50px;
position: relative;
-webkit-perspective: 1000px;
perspective: 1000px;
}
#cube {
width: 100%;
height: 100%;
position: absolute;
-webkit-transform-style: preserve-3d;
transform-style: preserve-3d;
-webkit-transition: -webkit-transform 1s;
transition: transform 1s;
-webkit-transform: translateZ(-25px);
transform: translateZ(-25px);
}
#cube figure {
margin:0;
display: block;
position: absolute;
width: 100%;
height: 100%;
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
#cube .front {
background: transparent;
-webkit-transform: translateZ(25px);
transform: translateZ(25px);
}
#cube .top{
background-color:darkgreen;
-webkit-transform: rotateX(-90deg) translateZ(25px);
transform: rotateX(-90deg);
-webkit-transition:background-color .5s;
}
#cube .top.show {
background-color:green;
}
#cube .back {
background: transparent;
-webkit-transform: rotateX( 180deg ) translateZ(25px);
transform: rotate(180deg);
}
#cube.show-front{
}
#cube.show-top {
-webkit-transform: rotateX(90deg);
transform: rotateX(90deg);
}
#cube.show-back {
-webkit-transform: rotateX(180deg);
transform: rotateX(180deg);
}
JavaScript
$('.showfront').click(function () {
$('.top').removeClass('show');
$('#cube').removeClass().addClass('show-front');
});
$('.showtop').click(function () {
$('.top').addClass('show');
$('#cube').removeClass().addClass('show-top');
});
$('.showback').click(function(){
$('.top').removeClass('show');
$('#cube').removeClass().addClass('show-back');
});
I have to elements and I want to animate them seperatly. Element one should play animation one and element two should play animation two.
But when I test it element one plays both animations and element two none.
This is not happening if I start the animation of element two with a delay, but this is no solution...
Here's element one:
#wrapper_splashscreen #logo {
position: absolute;
left: 50%;
top: 50%;
width: 200px;
height: 200px;
margin-top: -100px;
margin-left: -100px;
-webkit-animation: logoIntro 0.5s 1; }
#-webkit-keyframes logoIntro
{
0% {
-webkit-transform: scale(0, 0);
opacity: 0;
}
80% {
-webkit-transform: scale(1.4, 1.4);
}
90% {
-webkit-transform: scale(1.1, 1.1);
}
100% {
-webkit-transform: scale(1, 1);
opacity: 1;
}
}
and here's element two:
#wrapper_splashscreen #menu {
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
height: 40px;
background: #151515;
-webkit-animation-name: menuIntro 1s 1; }
#-webkit-keyframes menuIntro
{
0%, 30% {
bottom: -40px;
}
100% {
bottom: 0px;
}
}
The logo (element one) is fadeing in and moving down and the menu (element two) is doing nothing.
In the second element you've an error:
-webkit-animation-name: menuIntro 1s 1;
It should be -webkit-animation.
I'm not sure what's the problem with the first element (please add a fiddle/demo), buy maybe setting a transform-origin will help
It seems like the animation becomes buggy when you navigate to the animated element with an anchor. The browser navigates to the element while its moving and the animation gets broken.