How can I simultaneously apply multiple rotate transforms by toggling classes? - css

I'm trying to apply various transforms to a given element or set of elements by clicking buttons to toggle classes on and off. This works for most things, but not when I try to toggle on more than one rotate transform. Once an element has two separate rotate transform classes (rotateX, rotateZ), the transforms stop working as I expect.
Please see my example here: http://jsfiddle.net/XTVd7/9/
CSS
#box {
width: 100px;
height: 100px;
border: 1px solid blue;
transition: all 1s;
}
.flip {
-webkit-transform: rotateX(360deg);
-moz-transform: rotateX(360deg);
-ms-transform: rotateX(360deg);
-o-transform: rotateX(360deg);
}
.spin {
-webkit-transform: rotateZ(360deg);
-moz-transform: rotateZ(360deg);
-ms-transform: rotateZ(360deg);
-o-transform: rotateZ(360deg);
}
jQuery
$("#flipBtn").on("click", function() {
$("#box").toggleClass("flip");
});
$("#spinBtn").on("click", function() {
$("#box").toggleClass("spin");
});
I'd expect that I could flip or spin the box at any time by toggling either class on or off. This is not the case once both classes have been added to it.

If you want to toggle multiple values for the same property, you have to manually construct the property text (fiddle):
function createToggleFunction(element$) {
var transforms = {
flip: 'rotateX(120deg)',
spin: 'rotateZ(180deg)',
};
var state = {};
return function(transform) {
state[transform] ^= true;
var text = "";
for(var key in transforms) {
if(state[key]) text += transforms[key] + ' ';
}
element$.css('transform', text);
};
}
var toggle = createToggleFunction($('#box'));
$("#flipBtn").on("click", function() {
toggle('flip');
});
$("#spinBtn").on("click", function() {
toggle('spin');
});
A simpler solution would be to have two nested elements to represent the "box", with one of the transformations applying to the outer one and one applying to the inner one:
(See this edited version of jblasco's fiddle)
#spinner {
width: 100px;
height: 100px;
transition: all 1s;
}
#flipper {
width: 100%;
height: 100%;
transition: all 1s;
border: 1px solid blue;
}
HTML:
<div id="spinner">
<div id="flipper">
a
</div>
</div>
JQuery:
$("#flipBtn").on("click", function() {
$("#flipper").toggleClass("flip");
});
$("#spinBtn").on("click", function() {
$("#spinner").toggleClass("spin");
});

Related

How can I use animation in styled-components css``?

I read documents, and I used keyframes`` for declare css animation.
But I got error message like this:
Please wrap your string in the css`` helper which ensures the styles are injected correctly.
So, I wrapped animation with css`` instead keyframes``, but animation didn't work.
const rippleAnimation = css`
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(var(--material-scale));
opacity: 0;
}
`;
This didn't work well.
I don't know I wrote perfectly...
ordinary code(error about keyframes) :
const rippleAnimation = keyframes`
0% {
transform: translate(-50%, -50%) scale(1);
}
100% {
transform: translate(-50%, -50%) scale(var(--material-scale));
opacity: 0;
}
`;
You can do it like this. Create keyframes in CSS. And add the keyframes with js. You can also add this keyframe after clicking in js.
const hello = document.querySelector(".hello")
hello.style.animation = "5s colorChange linear infinite"
.hello {
color: #000000;
}
#keyframes colorChange {
0% {
color: black;
}
50% {
color: red;
}
100% {
color: black;
}
}
<h1 class="hello">Hello World</h1>

How can I add two transition transforms but one after one?

I want to add 2 transition transforms
But I want to start the second transform after the end of the first transform
the element should go to a point slowly and after that it should go to another point
transform: translate(0%, 300%), translate(15%, -136%);
You cannot do this with just a single element using transition because when you put more than one translate within the transform, the transform property on the whole is transitioned and not one by one.
With pure CSS transition using an extra wrapper element:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
.wrapper {
position: relative;
height: 100px;
width: 100px;
transition: all 1s 1s;
}
.content {
position: absolute;
height: 100%;
width: 100%;
border: 1px solid;
transition: all 1s;
}
body:hover .content {
transform: translate(15%, -136%);
transition: all 1s 1s;
}
body:hover > .wrapper {
transform: translate(0%, 300%);
transition: all 1s;
}
body {
min-height: 100vh;
}
<div class='wrapper'>
<div class='content'>Some text</div>
</div>
Transition with a bit of JS/jQuery without any extra elements:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
$(document).ready(function() {
var isHover; /* variable to track state */
$('body').hover(function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
}, function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
});
$('.content').on('transitionend', function() {
if (isHover) {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)');
} else {
$('.content').css('transform', 'none');
}
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
transition: all 1s;
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>
With animation and no extra element:
Using animations this can be done using a single element but the reverse effect is tough to achieve. We would have to write extra code for this and even then it will be complex.
.content {
position: relative;
height: 100px;
width: 100px;
border: 1px solid;
}
body:hover > .content {
animation: move 1s forwards;
}
#keyframes move {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
body {
min-height: 100vh;
}
<div class='content'>Some text</div>
Animations with reverse effect:
Below is a snippet which produces the reverse effect also using CSS animations. But as you can see it is a bit complex. We can do this using a single animation also but it would become more complex.
$(document).ready(function() {
$('body').hover(function() {
$('.content').css('transform', 'none');
$('.content').removeClass('hover-out').addClass('hover-in');
}, function() {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)'); /* as soon as an animation is removed, the element would snap back to original state, to avoid that we have to add final state via inline style */
$('.content').removeClass('hover-in').addClass('hover-out');
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
}
.hover-in {
animation: hover-in 1s forwards;
}
.hover-out {
animation: hover-out 1s forwards;
}
#keyframes hover-in {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
#keyframes hover-out {
0% {
transform: translate(0%, 300%) translate(15%, -136%);
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: none;
}
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>

Rotate multiple elements at the same time

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>

CSS Transition for only one type of transform?

Is it possible to animate (using transitions) only one type of css transform?
I have css:
cell{
transform: scale(2) translate(100px, 200px);
transition: All 0.25s;
}
Now, I want only scale to be animated.
In this case I could use position:absolute and left/right properties but I far as I remember, translate() is much better in performance.
I would also like to avoid using additional html elements.
Fiddle: http://jsfiddle.net/6UE28/2/
No! you cannot use transition only for certain value of transform like scale(2).
One possible solution would be as follows: (sorry, you have to use additional html)
HTML
<div class="scale">
<div class="translate">
Hello World
</div>
</div>
CSS
div.scale:hover {
transform: scale(2);
transition: transform 0.25s;
}
div.scale:hover div.translate {
transform: translate(100px,200px);
}
Yes! You separate it into two selectors, one of them with transition: none, then trigger CSS reflow in between to apply the change (otherwise it will be considered as one change and will transition).
var el = document.getElementById('el');
el.addEventListener('click', function() {
el.classList.add('enlarged');
el.offsetHeight; /* CSS reflow */
el.classList.add('moved');
});
#el { width: 20px; height: 20px; background-color: black; border-radius: 100%; }
#el.enlarged { transform: scale(2); transition: none; }
#el.moved { transform: scale(2) translate(100px); transition: transform 3s; }
<div id="el"></div>
Yes, why not. In order to do that, you have to actually use only one transform.
This is what is confusing you: you don't apply transform on the element itself. You apply that to the change-state (by means of a pseudo class like :hover or another class using styles that you want in the changed state). Please see #David's comment on your question. You change state for only that property which you want to change and that will animate.
So, effectively you change them selectively based on changed-state.
Solution 1: Using Javascript (based on the fiddle you provided in your question)
Demo: http://jsfiddle.net/abhitalks/6UE28/4/
Relevant CSS:
div{
-webkit-transition: all 1s;
-moz-transition: all 1s;
transition: all 1s;
/* do NOT specify transforms here */
}
Relevant jQuery:
$('...').click(function(){
$("#trgt").css({
"-webkit-transform": "scale(0.5)"
});
});
// OR
$('...').click(function(){
$("#trgt").css({
"-webkit-transform": "translate(100px, 100px)"
});
});
// OR
$('...').click(function(){
$("#trgt").css({
"-webkit-transform": "scale(0.5) translate(100px, 100px)"
});
});
Solution 2: Using CSS Only
Demo 2: http://jsfiddle.net/abhitalks/4pPSw/1/
Relevant CSS:
div{
-webkit-transition: all 1s;
-moz-transition: all 1s;
transition: all 1s;
/* do NOT specify transforms here */
}
div:hover {
-webkit-transform: scale(0.5);
}
/* OR */
div:hover {
-webkit-transform: translate(100px, 100px);
}
/* OR */
div:hover {
-webkit-transform: scale(0.5) translate(100px, 100px);
}
Yes! Now you can, because translate, scale and rotate have been moved to a separate css properties! (MDN: translate, scale, rotate)
So now you can do:
div{
scale: 1;
translate: 0;
transition: scale 0.4s;
}
div.clicked{
scale: 2;
translate: 100px 200px;
}
Example: http://jsfiddle.net/bjkdthr5/1/ (only scale will animate)
Instead of forcing a reflow, you can instead use setTimeout.
var el = document.getElementById('el');
el.addEventListener('click', function() {
el.classList.add('enlarged');
setTimeout(function() {el.classList.add('moved');})
});
#el { width: 20px; height: 20px; background-color: black; border-radius: 100%; }
#el.enlarged { transform: scale(2); transition: none; }
#el.moved { transform: scale(2) translate(100px); transition: transform 3s; }
<div id="el"></div>

Looking for improvement of slide down animation with angular.js

I do have a running code sample (http://jsfiddle.net/Fuunq/) which shows a slide down animation using angular.js (1.2.0). But there are two issues that I don't know how to solve:
a) When clicking on the 'Add item' link, the animation first moves down the 'Add item' link and then slides in the new item from the top. How can this be changed, that the 'Add item' link slides down together with the new appearing item?
b) How can I prevent that the items do fade in when the page gets loaded for the first time?
HTML
<div ng-controller="MyCtrl">
<div ng-repeat="item in items" class="animation-slide-down">
<div class="item">
<div>Name: {{item.name}}</div>
<div>Color: {{item.color}}</div>
</div>
</div>
Add item
</div>
CSS
.item {
margin-top: 5px;
padding: 15px;
background-color: #34ac54;
border: 1px solid black;
}
#-webkit-keyframes slide_down {
0% {
-webkit-transform: translateY(-100%);
opacity: 0;
z-index: -100;
}
99% {
z-index: -100;
}
100% {
-webkit-transform: translateY(0);
opacity: 1;
z-index: 0;
}
}
#keyframes slide_down {
0% {
transform: translateY(-100%);
opacity: 0;
z-index: -100;
}
99% {
z-index: -100;
}
100% {
transform: translateY(0);
opacity: 1;
z-index: 0;
}
}
.animation-slide-down.ng-enter {
-webkit-animation: slide_down 3s ease-in;
animation: slide_down 4s ease-in;
}
JS
var myApp = angular.module('myApp', ['ngAnimate']);
function MyCtrl($scope) {
$scope.name = 'foo';
$scope.items = [
{name: 'Item 1', color: 'blue'},
{name: 'Item 2', color: 'red'}
]
$scope.addItem = function() {
$scope.items.push({name: 'Another item', color: 'black'})
}
}
Thanks for your help!
a) as far as i know this can only be achieved with a negative margin-top transitions to your target value 5px. There is one problem with this: you have to know the exact height of one item.
#-webkit-keyframes slide_down {
0% {
margin-top: -68px;
opacity: 0;
z-index: -100;
}
99% {
z-index: -100;
}
100% {
margin-top:5px;
opacity: 1;
z-index: 0;
}
}
b) you can use the $animate service. The service has an enabled method, so you may disable or enable the animations at any time. here is your controller code that disables the animation on start and after the start enables the animation. the trick is the $timeout service. with that you defer activating the animations until the next digest cycle happens:
function MyCtrl($scope, $animate, $timeout) {
$animate.enabled(false);
$scope.name = 'foo';
$scope.items = [
{name: 'Item 1', color: 'blue'},
{name: 'Item 2', color: 'red'}
]
$scope.addItem = function() {
$scope.items.push({name: 'Another item', color: 'black'})
}
$timeout(function(){
$animate.enabled(true);
});
}
here is the working example:
http://jsfiddle.net/CKF47/

Resources