How to re-attach animation [duplicate] - html

This question already has answers here:
CSS animation triggered through JS only plays every other click
(2 answers)
Closed 2 years ago.
How can I re-attach the following animation?
So that every time the text is been clicked, the animation will be activated?
Currently, if I click the text, for the first time the animation will be activated, but no longer when clicking again.
Here is my code:
<!DOCTYPE html>
<html>
<head>
<style>
.shakeit {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
#keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
</style>
</head>
<body>
<h2 id='xxx' class="">JavaScript Numbers</h2>
<script>
const x = document.getElementById('xxx');
x.addEventListener('click', () => {
x.classList.add("shakeit");
});
</script>
</body>
</html>

You can use the onanimationend event to remove the class again after its finished.
<!DOCTYPE html>
<html>
<head>
<style>
.shakeit {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
#keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
</style>
</head>
<body>
<h2 id='xxx' class="">JavaScript Numbers</h2>
<script>
const x = document.getElementById('xxx');
x.addEventListener('click', () => {
x.classList.add("shakeit");
});
x.onanimationstart = function(event) {
console.log("Animation started", event);
}
x.onanimationend = function(event) {
x.classList.remove("shakeit");
};
</script>
</body>
</html>

Related

Apple Animation in React with inline CSS problem

I try to recreate this animation with HTML and CSS in React with Typescript using inline styling.I am creating an object with styling information, and refer to it in the style attribute. The code is down below. It does not work and I am not sure what I am doing wrong, I suspect the styles are not defined and referred correctly?
Here is the original Codepen example I try to rewrite: Apple Animation
And here is my code
import React from 'react';
const styles = {
'#keyframes showTopText': {
'0%': { transform: 'translate3d(0, 100%, 0)' },
'40%, 60%': { transform: 'translate3d(0, 50%, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
},
'#keyframes showBottomText': {
'0%': { transform: 'translate3d(0, -100%, 0)' },
'100%': { transform: 'translate3d(0, 0, 0)' },
},
animatedTitle: {
color: '#222',
fontFamily: 'Roboto, Arial, sans-serif',
height: '90vmin',
left: '50%',
top: '50%',
transform: 'translate(-50%, -50%)',
width: '90vmin',
},
'animatedTitle > div': {
height: '50%',
overflow: 'hidden',
position: 'absolute',
width: '100%',
},
'animatedTitle > div div': {
fontSize: '12vmin',
padding: '2vmin 0',
position: 'absolute',
},
'animatedTitle > div div span': {
display: 'block',
},
'animated-title > div.text-top': {
borderBottom: '1vmin solid #000',
top: 0,
},
'animatedTitle > div.text-top div': {
animation: 'showTopText 1s',
animationDelay: '0.5s',
animationFillMode: 'forwards',
bottom: 0,
transform: 'translate(0, 100%)',
},
'animatedTitle > div.text-top div span:first-child': {
color: '#767676',
},
'animatedTitle > div.text-bottom': {
bottom: 0,
},
'animatedTitle > div.text-bottom div': {
animation: 'showBottomText 0.5s',
animationDelay: '1.75s',
animationFillMode: 'forwards',
top: 0,
transform: 'translate(0, -100%)',
},
};
function Design() {
return (
<div style={styles.animatedTitle}>
<div style={styles['animatedTitle > div.text-top div']}>
<div>
<span>mimicking</span>
<span>apple's design</span>
</div>
</div>
<div style={styles['animatedTitle > div.text-bottom']}>
<div>for the win!</div>
</div>
</div>
);
}
export { Design };
You can try using styled-components to keep your component more flexible and compatible. I replicated the codepen example with styled-components and made it easily extensible.
So, TopAnimateBlock and BottomAnimateBlock have numOfLine property hence how many lines is inside the block. The second property in BottomAnimateBlock is delayTopLine, it should have the same numbers as a numOfLine in TopAnimateBlock, because we need to wait for top lines to play.
Also you can easily change the text color with color property in TextStyle and pass as color value, HEX color or rgba() / hsla().
TextAnimation.tsx
import styled, { keyframes } from 'styled-components';
const showTopText = keyframes`
0% { transform: translate3d(0, 100% , 0); }
40%, 60% { transform: translate3d(0, 50%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
const showBottomText = keyframes`
0% { transform: translate3d(0, -100%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;
const Section = styled.section`
width: calc(100% + 10vmin);
display: flex;
flex-flow: column;
padding: 2vmin 0;
overflow: hidden;
&:last-child {
border-top: 1vmin solid white;
}
`;
const Block = styled.div<{ numOfLine: number }>`
position: relative;
`;
const TopAnimateBlock = styled(Block)`
animation: ${showTopText} calc(0.5s * ${props => props.numOfLine}) forwards;
animation-delay: 0.5s;
transform: translateY(calc(100% * ${props => props.numOfLine}));
`;
const BottomAnimateBlock = styled(Block)<{ delayTopLine: number }>`
animation: ${showBottomText} calc(0.5s * ${props => props.numOfLine}) forwards;
animation-delay: calc(0.7s * ${props => props.delayTopLine});
transform: translateY(calc(-100% * ${props => props.numOfLine}));
`;
const TextStyle = styled.p<{ color: string }>`
font-family: Roboto, Arial, sans-serif;
font-size: 12vmin;
color: ${props => props.color};
`;
export const TextAnimation = () => {
return (
<>
<Section>
<TopAnimateBlock numOfLine={2}>
<TextStyle color="grey">mimicking</TextStyle>
<TextStyle color="white">apple's design</TextStyle>
</TopAnimateBlock>
</Section>
<Section>
<BottomAnimateBlock numOfLine={1} delayTopLine={2}>
<TextStyle color="white">for the win!</TextStyle>
</BottomAnimateBlock>
</Section>
</>
);
};
If we want to animate 3 lines instead of 2, just add / change:
Add new TextStyle component
Change from 2 to 3 numOfLine in TopAnimateBlock and delayTopLine in BottomAnimateBlock
And replace keyframes to this fragment:
const showTopText = keyframes`
0% { transform: translate3d(0, 100% , 0); }
25%, 40% { transform: translate3d(0, 66%, 0); }
60%, 75% { transform: translate3d(0, 33%, 0); }
100% { transform: translate3d(0, 0, 0); }
`;

CSS: Infinitely looping animation doesn't interpolate between the first and last keyframes

I'm fairly certain I'm noticing this right and I can't seem to find a proper solution anywhere. I have a CSS animation that's meant to loop infinitely, it already works but has a problem.
// CSS
#keyframes myanim {
0% { transform: translate(0, 0) rotate(0deg) skewX(0deg); }
25% { transform: translate(5px, 5px) rotate(1deg) skewX(1deg); }
50% { transform: translate(0, 0) rotate(0eg) skewX(0deg); }
75% { transform: translate(-5px, -5px) rotate(-1deg) skewX(-1deg); }
100% { transform: translate(0, 0) rotate(0deg) skewX(0deg); }
}
// JS
element.style.animation = "myanim " + mytimer + "s infinite";
Keyframe interpolation doesn't seem to work around the seams. Between points 0% and 100% there's no interpolation: When the animation just started or is approaching the end each iteration, it slows down / sets into place / speeds up instead of maintaining its constant rhythm like between the other keyframes. It it possible to tell the browser to interpret all keyframes in a circular manner for the loop to work as intended?
# MirceaKitsune is right you need to use the linear animation property.
I tried out the animation and it works, here is the fiddle:
.animated-heading {
animation: linear myanim 2s infinite;
}
#keyframes myanim {
0% {
transform: translate(0, 0) rotate(0deg) skewX(0deg);
}
12.5% {
transform: translate(2.5px, 2.5px) rotate(0.5deg) skewX(0.5deg);
}
25% {
transform: translate(5px, 5px) rotate(1deg) skewX(1deg);
}
37.5% {
transform: translate(2.5px, 2.5px) rotate(0.5deg) skewX(0.5deg);
}
50% {
transform: translate(0, 0) rotate(0eg) skewX(0deg);
}
62.5% {
transform: translate(-2.5px, -2.5px) rotate(-0.5deg) skewX(-0.5deg);
}
75% {
transform: translate(-5px, -5px) rotate(-1deg) skewX(-1deg);
}
87.5% {
transform: translate(-2.5px, -2.5px) rotate(-0.5deg) skewX(-0.5deg);
}
100% {
transform: translate(0, 0) rotate(0deg) skewX(0deg);
}
}
<h1 class="animated-heading">animated-heading</h1>

addClass and removeClass trigger with Waypoints

I am making a website that is horizontal parallax scroller. My intention is to have environmetal effect from dawn to night by moving gradient (div 100vw, 300vh) up and down while changing opacity. I want javascript to trigger desired action when a certain div comes to viewport.
I am using jquery, Waypoints and jInvertScroll.
I get this from console:
<div class="dawndusk"></div> when everything is in the beginning. <div class="dawndusk lettherebenight"></div> when I scroll to #dawn. And nothing changes when I scroll to other divs. Why?
This is my HTML
<div class="dawndusk"></div>
<div class="flex" id="birth">
...content...
</div>
<div class="flex" id="dawn">
...content...
</div>
<div class="flex" id="day">
...content...
</div>
<div class="flex" id="dusk">
...content...
</div>
<div class="flex" id="night">
...content...
</div>
This is my CSS
.dawndusk {
z-index:750;
opacity: .9;
background: #4B79A1;
background: -webkit-linear-gradient(to top, #f6d365 0%, #fda085 50%, #283E51 50%, #0A2342 100%);
background: -olinear-gradient(to top, #f6d365 0%, #fda085 50%, #283E51 50%, #0A2342 100%);
background: linear-gradient(to top, #f6d365 0%, #fda085 30%, #283E51 60%, #0A2342 100%);
width:100vw;
height:300vh;
position:fixed;
}
#birth,
#dawn,
#day,
#dusk,
#night {
position: absolute;
top:0;
}
#birth {
left:0;
}
#dawn {
left:100vw;
}
#day {
left:200vw;
}
#dusk {
left:300vw;
}
#night {
left:400vw;
}
.lettherebebirth,
.lettherebedawn,
.lettherebeday,
.lettherebedusk,
.lettherebenight {
transition: all 4s;
-webkit-transition: all 4s;
}
.lettherebebirth {
opacity: .9;
transform: translateY(0);
}
.lettherebedawn {
opacity: .2;
transform: translateY(-200vh);
}
.lettherebeday {
opacity: 0;
transform: translateY(0);
}
.lettherebedusk {
opacity: .5;
transform: translateY(-200vh);
}
.lettherebenight {
opacity: .8;
transform: translateY(-200vh);
}
Javascript is like this:
$('#birth').waypoint(function(direction) {
if (direction === 'up') {
$('.dawndusk').removeClass('lettherebedawn');
$('.dawndusk').addClass('lettherebebirth');
}
});
$('#dawn').waypoint(function(direction) {
if (direction === 'down') {
$('.dawndusk').removeClass('lettherebebirth');
$('.dawndusk').addClass('lettherebedawn');
}
});
$('#day').waypoint(function(direction) {
if (direction === 'down') {
$('.dawndusk').removeClass('lettherebedawn');
$('.dawndusk').addClass('lettherebeday');
}
});
$('#dusk').waypoint(function(direction) {
if (direction === 'down') {
$('.dawndusk').removeClass('lettherebeday');
$('.dawndusk').addClass('lettherebedusk');
}
});
$('#night').waypoint(function(direction) {
if (direction === 'down') {
$('.dawndusk').removeClass('lettherebedusk');
$('.dawndusk').addClass('lettherebenight');
}
});
How do I get the wanted classes to trigger on and off when I want to?
I figured it out myself.
jInvertscroll mimics horizontal scrolling on vertical scrolls behalf. So though everything seemed ok on screen and on code, the problem was that waypoints triggers were all placed on horizontal axis. It was supposed be on vertical axis.
#birth {
top:0;
}
#dawn {
top:100vw;
}
#day {
top:200vw;
}
#dusk {
top:300vw;
}
#night {
top:400vw;
}

Multiple animations with CSS3 not working as expected

I have a set of divs that I'm animating in different ways. The first one swings/flips onto the stack using:
#-webkit-keyframes cardflip {
from {
-webkit-transform: perspective(2000) rotateY(90deg);
-webkit-transform-origin: 100% 0%;
}
to {
-webkit-transform: perspective(2000) rotateY(0deg);
-webkit-transform-origin: 100% 0%;
-webkit-transform: translate3d(0, 0, 0);
}
}
While the others are using transforms:
#cards .card:nth-child(2) { -webkit-transform: translate3d(0, 171px, 0); transform: translate3d(0, 183px, 0); }
#cards .card:nth-child(3) { -webkit-transform: translate3d(0, 342px, 0); transform: translate3d(0, 352px, 0); }
#cards .card:nth-child(4) { -webkit-transform: translate3d(0, 513px, 0); transform: translate3d(0, 521px, 0); }
#cards .card:nth-child(5) { -webkit-transform: translate3d(0, 684px, 0); transform: translate3d(0, 690px, 0); }
#cards .card:nth-child(6) { -webkit-transform: translate3d(0, 855px, 0); transform: translate3d(0, 859px, 0); }
#cards .card:nth-child(7) { -webkit-transform: translate3d(0, 1026px, 0); transform: translate3d(0, 1028px, 0); }
What I expect to happen is when I add a new div in the first position the other 'cards' slide down, and the first one flips into the top position. But it seems that the way I have it set up, the sliding animation doesn't happen when I add the new div on top of the stack, it just snaps to its new position. How can I fix this?
By the way, I'm only working in Chrome, hence the lack of non-webkit prefixes.
Fiddle.
You have to do it with a little bit of javascript, toggling a class instead. This is because the CSS selector of first child is immediately used and because transferring from an animation to a transition doesn't work the way you might think it might
var count = 0;
setInterval(function() {
$('#cards').prepend('<div class="card">testadd' + count++ + '</div>' );
setTimeout(function() {
document.getElementsByClassName("card")[0].className = "card first";
}, 10); // Fire just after it's added so it transitions
$('#cards .card:last').remove();
}, 5000);
CSS
#cards .first { -webkit-transform:translate3d(0,0,0) rotateY(0deg); }
#cards .card:nth-child(1) { z-index: 1000; }
Demo
(The count was for testing purposes)

CSS animation similar to Mac OS X 10.8 invalid password "shake"?

On the Mac OS X 10.8 "password" screen, if you enter an invalid password, it will "shake" back and forth (a.k.a. left and right). I am trying to achieve an similar effect for an HTML password input field using CSS animations.
I created a "Password Shake" jsfiddle that seems to emulate this behavior. However, it doesn't seem quite right. I'm not sure the explicit keyframes and the linear timing function are the right approach. This is my first attempt at a CSS animation, and I'm looking for feedback on the right way to achieve this.
HTML
<div class="box">
<input type="password" id="demo-password" placeholder="password" autofocus />
</div>
JavaScript
$('#demo-password').on('keyup', function (e) {
var $input = $(this);
var val = $.trim($input.val());
$input.removeClass("invalid");
if (e.which !== 13 || !val) {
return;
}
if (val != "password") {
$input.select();
$input.addClass("invalid");
}
});
CSS
#demo-password.invalid {
outline-color: red;
/* also need animation and -moz-animation */
-webkit-animation: shake .6s linear;
}
/* also need keyframes and -moz-keyframes */
#-webkit-keyframes shake {
0% {
left:-10px;
}
16% {
left:9px;
}
33% {
left:-6px;
}
50% {
left:5px;
}
66% {
left:-2px;
}
83% {
left:1px;
}
100% {
left: 0px;
}
}
Edit
I did find Animate.css which has a shake animation. I've included the (non browser prefixed) CSS below. Instead of setting left is does a transform: translateX(), which likely has a better chance for hardware acceleration.
.animated {
animation-duration: 1s;
animation-fill-mode: both;
}
#keyframes shake {
0%, 100% {transform: translateX(0);}
10%, 30%, 50%, 70%, 90% {transform: translateX(-10px);}
20%, 40%, 60%, 80% {transform: translateX(10px);}
}
.shake {
animation-name: shake;
}
I used my iPad camera to record the Mac pasword screen. It looks like it shakes 3 times each direction, with the first 2 going the full distance and the last 1 time a lesser distance.
#demo-password.invalid {
outline-color: red;
/* also need animation and -moz-animation */
-webkit-animation: shake .5s linear;
}
/* also need keyframes and -moz-keyframes */
#-webkit-keyframes shake {
8%, 41% {
-webkit-transform: translateX(-10px);
}
25%, 58% {
-webkit-transform: translateX(10px);
}
75% {
-webkit-transform: translateX(-5px);
}
92% {
-webkit-transform: translateX(5px);
}
0%, 100% {
-webkit-transform: translateX(0);
}
}
I'd give jRumble a shot too, it has a very large set of shakes and they can be combined to make all sorts of crazy stuff happen. Some fun examples:
Shake based on a percentage
Time based shake