I have list of items (li) that are stacked around in circle using transformation. Items have different angle depending on its initial position in the circle:
transform: rotate(0deg) translateY(-75px) rotate(-0deg)
transform: rotate(45deg) translateY(-75px) rotate(-45deg)
... etc
last negative rotate is to rotate ccw to keep element in up position.
translate Y is radius offset.
What I would like to accomplish is to rotate all items around for various number of degrees or let's say if one item gets clicked I would like all items to rotate so that clicked item will be on the top position (0deg).
What would be best way to do this? Looking for some clever ways.. All items should ofcourse rotate at the same time - like a dialer on the old phone.
With some jQuery, you can change the class of the container and assign the rotation value to the container :
fiddle
$('.one').click(function() {
$('#container').removeClass('second third fourth').addClass('first');
});
$('.two').click(function() {
$('#container').removeClass('first third fourth').addClass('second');
});
$('.three').click(function() {
$('#container').removeClass('first second fourth').addClass('third');
});
$('.four').click(function() {
$('#container').removeClass('first second third').addClass('fourth');
});
#container {
position: relative;
width: 30%;
padding-bottom: 30%;
margin: 10% auto;
transition: transform .5s ease-out;
}
.elt {
position: absolute;
padding: 2%;
background: teal;
cursor: pointer;
transition: transform .5s ease-out;
}
.one { top: 0; left: 47.5%; }
.two { top: 47.5%; right: 0; }
.three { bottom: 0; left: 47.5%; }
.four { top: 47.5%; left: 0; }
#container.first { transform: rotate(0deg); }
#container.first div { transform: rotate(0deg); }
#container.second { transform: rotate(-90deg); }
#container.second div { transform: rotate(90deg); }
#container.third { transform: rotate(180deg); }
#container.third div { transform: rotate(-180deg); }
#container.fourth { transform: rotate(90deg); }
#container.fourth div { transform: rotate(-90deg); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="elt one">1</div>
<div class="elt two">2</div>
<div class="elt three">3</div>
<div class="elt four">4</div>
</div>
Related
I have a CSS animation, for example, like this:
#keyframes my-animation {
0% { opacity: 0; visibility: visible; transform: scale(0,0); }
50% { transform: scale(1.15, 1.15); }
100% { transform: none; }
}
And I want to apply it to a DIV that has an arbitrary rotation e.g. like this:
<div style="width:100px; height:100px; transform: rotate(45deg)"/>
When I apply the CSS animation, keyframes have another transform attribute that only sets scale. As a result, my DIV is rotated back to 0 during the animation and, at the end, it is restored back to 45 degree rotation.
But I want it to keep its arbitrary original rotation. So the question is: is there a way to specify in transform property of the keyframes that it should keep existing (arbitrary) rotation?
Something like transform: scale(1.15, 1.15) rotate(keep) ?
Use CSS variables
.x {
transform: rotate(var(--r,0deg));
height: 50px;
width: 50px;
display:inline-block;
background: green;
animation: my-animation 5s;
margin: 20px;
}
#keyframes my-animation {
0% {
opacity: 0;
transform: scale(0) rotate(var(--r,0deg));
}
50% {
transform: scale(1.15) rotate(var(--r,0deg));
}
}
<div class="x" style="--r:80deg"></div>
<div class="x" ></div>
<div class="x" style="--r:60deg"></div>
Or like below so you can append any transformation to the one defined in the keyframes
:root {
--r: rotate(0deg); /* Use any null transform (ex: translate(0), skew(0deg), etc)*/
}
.x {
transform: var(--r);
height: 50px;
width: 50px;
display:inline-block;
background: green;
animation: my-animation 5s;
margin: 20px;
}
#keyframes my-animation {
0% {
opacity: 0;
transform: scale(0) var(--r);
}
50% {
transform: scale(1.15) var(--r);
}
}
<div class="x" style="--r:rotate(80deg) skew(20deg)"></div>
<div class="x" ></div>
<div class="x" style="--r:rotate(60deg) translate(20px,20px)"></div>
Here's a simple solution without variables - I would just wrap your div and do the scaling on the wrapper, keeping the inner div rotated arbitrarily. Trivial, but does the trick I think.
.box {
height: 50px;
width: 50px;
background: green;
margin: 50px;
}
.scale-me {
animation: my-animation;
animation-duration: 10s;
}
#keyframes my-animation {
0% {
opacity: 0;
transform: scale(0);
}
50% {
transform: scale(1.15);
}
}
<div class="scale-me">
<div class="box" style="transform: rotate(45deg)"></div>
<div class="box" style="transform: rotate(60deg)"></div>
<div class="box" style="transform: rotate(120deg)"></div>
</div>
I'm trying to animate a line that underlines from left to right on 'mouseenter' and then to disappear from left to right on 'mouseleave' instead of the current behaviour where it disappears right to left.
Example of what I'm trying to achieve (but with animations not transitions):
https://jsfiddle.net/1gyksyoa/
I have tried to reverse the 'draw' animation but this doesn't achieve what I'm trying to accomplish.
#keyframes draw-reverse {
100% {
width: 0;
background-color: red;
}
0% {
width: 47px;
background-color: red;
}
}
I have put together this to give a better understanding of the problem;
https://jsfiddle.net/Lq560be9/
Currently, I have the line animating from left to right as desired on 'mouseenter', but on 'mouseleave' it disappears from right to left, whereas I am trying to get the line to also disappear from left to right.
But the problem isn't animation's ability it's the properties that you're animating. Instead of animating the width of an object you should animate its "X" position using translate. (this is much more performant too)
Simply put you need to MOVE the bar from left to center to right instead of trying to scale it.
(there's lots of code here to show the different states the only one you really need to follow is .ex4)
document.querySelector('#animate').addEventListener('mouseenter', function(){
this.classList.toggle('over');
})
document.querySelector('#animate').addEventListener('mouseleave',function(){
this.classList.toggle('out');
})
.example {
margin: 30px auto;
padding: 10px;
background: #dadada;
max-width: 50%;
position: relative;
}
.example:after {
content:'';
position: absolute;
width: 50%;
height: 5px;
background-color: #333;
left:0;
bottom:0;
}
.ex1:after {
transform: translateX(-100%);
}
.ex3:after {
transform: translateX(200%);
}
.ex4 {
overflow: hidden;
}
.ex4:after {
transform: translateX(-100%);
}
.ex4.over:after {
animation: animate-in 1s ease-in-out 1 normal forwards;
}
.ex4.out:after {
animation: animate-out 1s ease-in-out 1 normal forwards;
}
#keyframes animate-in {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0);
}
}
#keyframes animate-out {
0% {
transform: translateX(0);
}
100% {
transform: translateX(200%);
}
}
<div class="example ex1">Object State 1</div>
<div class="example ex2">Object State 2</div>
<div class="example ex3">Object State 3</div>
<div id="animate" class="example ex4">Full example (hover)</div>
As a follow on from above, an alternative solution without using the translate property.
The new animation for mouseleave is;
#keyframes draw-reverse {
0% {
width: 47px;
}
25% {
width: calc(100% - 16px);
}
26% {
width: auto;
right: 8px;
left: 8px;
}
100% {
width: auto;
right: 8px;
left: calc(100% - 8px);
}
}
Full solution can be seen here - https://jsfiddle.net/1wq25tg7/
I have a page transition I'm trying to work into my site. I have 2 50% height, 100% width elements, one placed before and after the body (with pseudo-selectors). I would like the 2 elements to slide to the middle of the screen, covering the background content. The transition is triggered when the "is-changing" class is added to the body, via Javascript.
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
transform: translateY(0);
}
.loading-bar {
position: fixed;
height: 2px;
width: 90%;
}
.loading-bar::before {
position: absolute;
background-color: aqua;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.is-changing .loading-bar::before {
transform: scaleX(1);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
It looks to me like you're running into two issues.
The first issue is that you forgot to include the content attribute in your pseudo elements (often this will be empty, like content: ""). Without this attribute, your pseudo elements will not exist in the DOM. Running your code snippet and inspecting it confirms this, since the pseudo elements are nowhere to be found.
Second, you're creating multiple pseudo elements. body::before is it's own pseudo element, and body.is-changing::before is a separate pseudo element. If you are hoping to create a constant set of elements that act as "doors" for a loading display, you may want to consider creating two real elements that sit in position: fixed above and below the viewport, and then slide in or out when a class is added. Perhaps these could be div.upper-door and div.lower-door.
Also, it looks to me like you're in need of a transition for your transform, or else the pseudo elements will just "snap" back and forth. You can take control of the position of your elements at different points during this transition by using a css animation. Your JavaScript would largely remain the same, except for targeting the .upper-door and .lower-door divs using document.querySelector(), or simply using IDs rather than classes and targeting with getElementById(), if that makes more sense for you. Your css might look like this:
div.upper-door {
top: 0;
transform: translateY(-100%);
}
div.upper-door.is-changing {
animation-duration: 5s;
animation-name: upper-door-closeopen;
}
div.lower-door {
bottom: 0;
transform: translateY(100%);
}
div.lower-door.is-changing {
animation-duration: 5s;
animation-name: lower-door-closeopen;
}
#keyframes upper-door-closeopen {
0% {
transform: translateY(-100%);
}
25% {
transform: translateY(0);
}
75% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
#keyframes lower-door-closeopen {
0% {
transform: translateY(100%);
}
25% {
transform: translateY(0);
}
75% {
transform: translateY(0);
}
100% {
transform: translateY(100%);
}
}
The css animation will be triggered when .is-changing is added to the element. As you experiment, you may find different permutations of this solution (such as using event listeners if a button click is triggering the loading screen) to be ideal.
There is a great resource on MDN for css animations if you would like more information.
You missed to add the content property on the pseudo-elements which is mandatory to make them available on the page. You also missed to add the transition property on the pseudo-elements to achieve your animation of sliding up/down.
Here's a snippet containing a working demo, I only used the code that is related to your issue:
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
position: relative; /* not really related to your issue but, to make sure that the body's pseudo-elements are positioned relative to the body */
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
content: ""; /* make the pseudo-elements available */
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
transition: all .8s ease-out; /* allow the animation, change this rule per your requirements */
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
transform: translateY(0);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
Learn more about after pseudo-element.
Learn more about before pseudo-element.
You can use the following
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
content:'';
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
}
body::before {
content:'';
top: 0;
transform: translateY(-100%);
transition: .5s all;
}
body::after {
content:'';
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
content:'';
transform: translateY(0);
}
.loading-bar {
position: fixed;
height: 2px;
width: 90%;
}
.loading-bar::before {
content:'';
position: absolute;
background-color: aqua;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.is-changing,.loading-bar::before {
transform: scaleX(1);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
When I use a webkit 3d transform on hover, only the top 50% of the hover area works, while the bottom 50% is unstable. I'm currently testing on Chrome (31.0.1650.63). Is it a bug? Is there any workaround?
Try to place your mouse on the top of the div and slowly bring it to the bottom.
HTML
<div class="hoverArea"></div>
<div class="flip">
<div class="front">front</div>
<div class="back">back</div>
</div>
CSS
.hoverArea, .flip, .front, .back {
width: 200px;
height: 200px;
position: absolute;
top: 0;
left: 0;
}
.hoverArea {
z-index: 10;
}
.flip {
-webkit-transform-style: preserve-3d;
-webkit-transition: 0.5s;
-webkit-perspective: 800;
z-index: 9;
}
.front {
background-color: #f00;
-webkit-backface-visibility: hidden ;
}
.back {
background-color: #f0f;
-webkit-transform: rotatex(-180deg);
-webkit-backface-visibility: hidden ;
}
.hoverArea:hover + .flip {
-webkit-transform: rotatex(-180deg);
}
http://jsfiddle.net/4P53y/
You can fix it by removing the .hoverArea element and instead apply the :hover event on the .flip element.
.flip:hover {
-webkit-transform: rotatex(-180deg);
}
Demo
If you want to still use the .hoverArea element then you can use transform:translateZ(1px); on .hoverArea to make it function correctly. It makes the browser render the element more carefully
.hoverArea {
z-index: 10;
-webkit-transform:translateZ(1px);
}
Demo
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.