I would like to use a single #keyframes rule to animate an element from one state to another and then to get back to the original state when I do an action (with the same animation). I saw that using animation-direction: reverse; is a way to play the animation in reverse. However, when I try to use it, the transitions on my element disappear. If I set a new #keyframes with the reversed state it works fine.
What is the point of animation-direction in this case? I am misunderstanding something?
Is there a way to play an animation in both directions with a single #keyframes rule without loosing the transitions? I can't use transition, I need animation.
Here is a example to play with (hover the squares):
div {
width: 100px;
height: 100px;
animation: fade 0.6s ease-in-out forwards;
margin: 15px;
display: inline-flex;
align-items: center;
justify-content: center;
color: white;
}
#box-1:hover {
animation: fade 0.6s ease-in-out forwards;
animation-direction: reverse;
}
#box-2:hover {
animation: fadeReverse 0.6s ease-in-out forwards;
}
#keyframes fade {
0% { background: red; }
100% { background: blue; }
}
#keyframes fadeReverse {
0% { background: blue; }
100% { background: red; }
}
<div id="box-1">:(</div>
<div id="box-2">:)</div>
It's because you apply the same animation to the element on hover as the animation that is on the default state of the element.
So the element already had that animation with the default direction but then you apply it again with the reverse. But it won't work. I don't really know why this happens. But applying the same animation on an element twice, won't work. So you need 2 different keyframes.
You can use a reverse animation or duplicate the existing one and use it with direction: reverse
Read more here
restart animation
more info here
another article here
If you REALLY want to use just 1 animation this can be solved with javascript by removing and adding an 'animate-me' class . But it still wouldn't be ideal
div {
width: 100px;
height: 100px;
animation: fade 0.6s ease-in-out forwards;
margin: 15px;
display: inline-flex;
align-items: center;
justify-content: center;
color: white;
}
#box-1:hover {
animation: fade2 0.6s ease-in-out forwards;
animation-direction: reverse;
}
#box-2:hover {
animation: fadeReverse 0.6s ease-in-out forwards;
}
#keyframes fade {
0% { background: red; }
100% { background: blue; }
}
#keyframes fade2 {
0% { background: red; }
100% { background: blue; }
}
#keyframes fadeReverse {
0% { background: blue; }
100% { background: red; }
}
<div id="box-1">:(</div>
<div id="box-2">:)</div>
I have a blinking cursor animation set up with two lines of text.
I want to have the cursor appear as the text appears, and vanish at the end of the first line – but leave it blinking at the end of the second line.
Someone asked a very similar question, but the solution makes the cursor completely invisible:
Stopping a blinking cursor at end of css animation
Tested this answer code (on several browsers), and it just doesn't work.
Here's what I have:
Code:
.typewriter1 p {
overflow: hidden;
border-right: .15em solid #00aeff;
white-space: nowrap;
margin: 0 auto;
letter-spacing: 0;
color: #fff;
padding-left: 10px;
animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite;
}
.typewriter2 p {
overflow: hidden;
/* Ensures the content is not revealed until the animation */
border-right: .15em solid #00aeff;
white-space: nowrap;
margin: 0 auto;
letter-spacing: 0;
color: #fff;
padding-left: 10px;
opacity: 0;
animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite, slidein 1s ease 3.5s forwards;
animation-delay: 3.5s;
}
/* The typing effect */
#keyframes typing {
from {
width: 0
}
to {
width: 100%
}
}
#keyframes slidein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* The typewriter cursor effect */
#keyframes blink-caret {
from,
to {
border-color: #00aeff
}
50% {
border-color: transparent;
}
}
<div class="typewriter1">
<p>A well defined plan will identify problems,</p>
</div>
<div class="typewriter2">
<p> address challenges, and help restore confidence.</p>
</div>
Only Example 2 is fully explained at the moment. Example 3 is exactly the same HTML and CSS as the question with minor changes.
Example 1 — Redesign for background images and gradients
HTML
First, we can clean up the HTML. This is a single paragraph, so let's wrap it in one paragraph element:
<p class="typewriter">
A well defined plan will identify problems,
address challenges, and help restore confidence.
</p>
Second, we need to reveal each line individually, so we wrap each line in a nested span element and manually break the line with a line break:
<p class="typewriter">
<span class="slide">
<span class="inner-slide">A well defined plan will identify problems,
</span>
</span><br>
<span class="slide">
<span class="inner-slide">address challenges, and help restore confidence. </span>
</span>
</p>
Full Example 1
Current Limitation: We have to set a fixed pixel width for left.
.typewriter {
position: relative;
height: 500px;
margin: 0 auto;
width: 310px;
overflow: hidden;
}
.typewriter .slide,
.inner-slide {
display: inline-block;
overflow: hidden;
height: 1.1em;
}
.typewriter .slide {
position: relative;
animation: typing 2s steps(30, end) forwards, blink-caret .75s step-end infinite;
left: -310px;
border-right: .15em solid transparent;
}
.typewriter .slide:nth-of-type(1) {
animation: typing 2s steps(30, end) forwards, blink-caret .75s step-end 2.6;
}
.inner-slide {
position: relative;
animation: typing2 2s steps(30, end) forwards;
white-space: nowrap;
left: 310px;
}
.typewriter .slide:nth-of-type(2),
.typewriter .slide:nth-of-type(2) .inner-slide {
animation-delay: 2s;
}
#keyframes typing {
from {
left: -310px;
}
to {
left: 0;
}
}
#keyframes typing2 {
from {
left: 310px;
}
to {
left: 0;
}
}
/*The typewriter cursor effect */
#keyframes blink-caret {
0,
100% {
border-color: transparent
}
50% {
border-color: #00aeff
}
}
body {
background: linear-gradient(to bottom right, #CCC 0, #F00 100%) no-repeat;
}
<p class="typewriter">
<span class="slide">
<span class="inner-slide">A well defined plan will identify problems,
</span>
</span><br>
<span class="slide">
<span class="inner-slide">address challenges, and help restore confidence.</span>
</span>
</p>
Example 2 — Original. Suitable for solid colour backgrounds
The HTML
First, we can clean up the HTML. This is a single paragraph, so let's wrap it in one paragraph element:
<p class="typewriter">
A well defined plan will identify problems,
address challenges, and help restore confidence.
</p>
Second, we need to reveal each line individually, so we wrap each line in a span element and manually break the line with a line break:
<p class="typewriter">
<span>A well defined plan will identify problems,</span><br>
<span> address challenges, and help restore confidence.</span>
</p>
The CSS
Now we need an element that will cover our text and act as an animated cursor. We can use a pseudo-element that will start at 100% width and have a left border, like so:
.typewriter > span::before {
content: '';
border-left: .15em solid #00aeff;
position: absolute;
background: white;
height: 1.1em;
right: -5px;
width: 100%;
}
The height is just enough to cover all the text including below the baseline.
The right negative value will pull it outside its parent so the cursor doesn't show on the first line thanks to overflow-hidden on the parent.
It starts at 100% width which is animated to 0.
It is positioned absolute to the span which has a relative position.
In order to keep the cursor on the last line, we need to give it a 0 right value:
.typewriter > span:last-of-type::before {
right: 0;
}
Now it will no longer be pulled outside the parent.
The second line needs to be delayed by the same amount of time as the animation run time:
.typewriter > span:nth-of-type(2)::before {
animation-delay: 2s;
}
Because we want the paragraph widths to be determined by the width of the text and the span to accept widths, we need to make them inline-block:
.typewriter,
.typewriter > span {
display: inline-block;
}
Lastly, we reverse the typing animation to go from 100% to 0:
#keyframes typing {
from {
width: 100%
}
to {
width: 0
}
}
Full Example 2
.typewriter,
.typewriter > span {
display: inline-block;
}
.typewriter > span {
position: relative;
overflow: hidden;
padding-right: 4px;
}
.typewriter > span::before {
content: '';
position: absolute;
border-left: .15em solid #00aeff;
background: white;
height: 1.1em;
right: -5px;
width: 100%;
animation: blink-caret .75s step-end infinite, typing 2s steps(30, end) forwards;
}
.typewriter > span:nth-of-type(2)::before {
animation-delay: 2s;
}
.typewriter > span:last-of-type::before {
right: 0;
}
/* The typing effect*/
#keyframes typing {
from {
width: 100%
}
to {
width: 0
}
}
/*The typewriter cursor effect */
#keyframes blink-caret {
from,
to {
border-color: #00aeff
}
50% {
border-color: transparent
}
}
<p class="typewriter">
<span>A well defined plan will identify problems,</span><br>
<span> address challenges, and help restore confidence.</span>
</p>
Example 3 — Using exactly the example from the question
Change the iteration count as appropriate for the first line caret. In this example the value is 4.1. This animation will iterate 4.1 times and then stop:
animation: blink-caret .75s step-end 4.1
The border that creates the caret is changed to transparent:
border-right: .15em solid transparent
and the animation is flipped:
#keyframes blink-caret {
0,
100% {
border-color: transparent
}
50% {
border-color: #00aeff
}
}
Now the stopped state is transparent and the first line will disappear on the first line.
Full Example 3
body {
width: 330px;
}
.typewriter1 p {
overflow: hidden;
border-right: .15em solid transparent;
white-space: nowrap;
margin: 0 auto;
letter-spacing: 0;
padding-left: 10px;
animation: typing 3.5s steps(40, end), blink-caret .75s step-end 4.1;
}
.typewriter2 p {
overflow: hidden;
/* Ensures the content is not revealed until the animation */
border-right: .15em solid transparent;
white-space: nowrap;
margin: 0 auto;
letter-spacing: 0;
padding-left: 10px;
opacity: 0;
animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite, slidein 1s ease 3.5s forwards;
animation-delay: 3.5s;
}
/* The typing effect */
#keyframes typing {
from {
width: 0
}
to {
width: 100%
}
}
#keyframes slidein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* The typewriter cursor effect */
#keyframes blink-caret {
0,
100% {
border-color: transparent
}
50% {
border-color: #00aeff
}
}
<div class="typewriter1">
<p>A well defined plan will identify problems,</p>
</div>
<div class="typewriter2">
<p> address challenges, and help restore confidence.</p>
</div>
I just changed infinite from .typewriter1 p { to 5.
.typewriter1 p {
overflow: hidden;
border-right: .15em solid #00aeff;
white-space: nowrap;
margin: 0 auto;
letter-spacing: 0;
color: #fff;
padding-left: 10px;
animation: typing 3.5s steps(40, end), blink-caret .75s step-end 5;
}
.typewriter2 p {
overflow: hidden;
/* Ensures the content is not revealed until the animation */
border-right: .15em solid #00aeff;
white-space: nowrap;
margin: 0 auto;
letter-spacing: 0;
color: #fff;
padding-left: 10px;
opacity: 0;
animation: typing 3.5s steps(40, end), blink-caret .75s step-end infinite, slidein 1s ease 3.5s forwards;
animation-delay: 3.5s;
}
/* The typing effect */
#keyframes typing {
from {
width: 0
}
to {
width: 100%
}
}
#keyframes slidein {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* The typewriter cursor effect */
#keyframes blink-caret {
from,
to {
border-color: #00aeff
}
50% {
border-color: transparent;
}
}
<div class="typewriter1">
<p>A well defined plan will identify problems,</p>
</div>
<div class="typewriter2">
<p> address challenges, and help restore confidence.</p>
</div>
If you're not necessarily glued to writing your own animations for this, TypeIt's (https://typeitjs.com) API makes it possible w/ a lot less custom code:
https://codepen.io/alexmacarthur/pen/MWWEPxa
const secondInstance = new TypeIt('.typewriter2 p');
const firstInstance = new TypeIt('.typewriter1 p', {
afterComplete: function (instance) {
document.querySelector('.typewriter1 p .ti-cursor').remove();
secondInstance.go();
}
}).go();
The only downside to this approach is that you have less control over the animation itself (you'd need to override the CSS animation provided by the library).
Using the #keyframes in CSS I can get animations where the properties are changed gradually over the animation-duration. But I want something to change the properties instantly. So that throughout 0% to 25% property of a tag would be one and then it would instantly change, not gradually. How do I do that?
.move-me {
display: inline-block;
padding: 20px;
color: white;
position: relative;
margin: 0 0 10px 0;
}
.move-me-1 {
animation: move-in-steps 8s steps(4) infinite;
}
.move-me-2 {
animation: move-in-steps 8s steps(4, start) infinite;
}
.move-me-3 {
animation: move-in-steps 8s infinite;
}
body {
padding: 20px;
}
#keyframes move-in-steps {
0% {
left: 0;
background: blue;
}
100% {
left: 100%;
background: red;
}
}
<div class="move-me move-me-1">steps(4, end)</div>
<br>
<div class="move-me move-me-2">steps(4, start)</div>
<br>
<div class="move-me move-me-3">no steps</div>
what you need might be steps() in css animation
The code snippet I refer from this page
https://css-tricks.com/using-multi-step-animations-transitions/
The code below is a part of my code :
.myBox:hover::after {
animation-name: underline;
animation-duration: 350ms;
animation-fill-mode: forwards;
}
#keyframes underline {
from { width: 0; }
to { width: 100%; }
}
It works nicley, but I want to do it vice versa when animation completed, I mean when it finished then width should be 0 again, In fact for this part I want to do it when my element is not hovered. Which property can help me ?
You need to use alternate and run 2 iterations of the animation:
.box {
height:200px;
background:red;
animation: underline 500ms alternate 2 forwards;
}
#keyframes underline {
from { width: 0; }
to { width: 100%; }
}
<div class="box">
</div>
Or consider the use of transition if you want the effect on hover:
.box {
height: 200px;
background: red;
width: 0;
transition: 500ms;
}
body:hover .box {
width: 100%;
}
<div class="box">
</div>
You can specify multiple values for animations rather then from and to using percentage:
#keyframes underline {
0%, 100% { width: 0; }
50% { width: 100%; }
}
More detailed information can be found here.
.myBox:hover::after {
animation-name: underline infinite;
animation-duration: 350ms;
animation-fill-mode: forwards;
}
#keyframes underline {
from { width: 0; }
to { width: 100%; }
}
You infinite for this
I have the following code that does almost everything I need it to do.
.mouse {
margin-bottom:20px;
padding:5px;
overflow:hidden;
width:200px;
}
.mouse:after {
content:"";
height:2px;
display:block;
background:red;
margin-top:5px;
transform:translateX(100%);
animation: hoverOut 1.5s ease 1;
}
.mouse:hover:after {
animation: hoverIn 1.5s ease 1;
}
#keyframes hoverOut {
0% {transform:translateX(0%);}
100% {transform:translateX(100%);}
}
#keyframes hoverIn {
0% {transform:translateX(-100%);}
100% {transform:translateX(0%);}
}
<div class="mouse">watch the line below</div>
Basically when you mouse over the text, you see a line slide in from the left. When you mouse out, you see the line slide out to the right.
My problem is if you go to your browser and press the refresh button to cause a page load, you'll see a line travel off to the right. I do not want this animation effect on page load. Is there something I can do with just HTML and CSS ? I do not want to introduce Javascript unless I absolutely have to.
Instead of using an animation use a transition with the property transform and a value of scaleX.
The "trick" here is to change transform-origin on the hover state.
Basically you do this:
.mouse:after {
content: "";
transform: scaleX(0);
transition: transform 1.5s ease;
transform-origin: right;
}
.mouse:hover:after {
transform: scaleX(1);
transform-origin: left;
}
Code Snippet:
.mouse {
margin-bottom: 20px;
padding: 5px;
overflow: hidden;
width: 200px;
}
.mouse:after {
content: "";
height: 2px;
display: block;
background: red;
margin-top: 5px;
transform: scaleX(0);
transition: transform 1.5s ease;
transform-origin: right;
}
.mouse:hover:after {
transform: scaleX(1);
transform-origin: left;
}
<div class="mouse">watch the line below</div>
If you however want to use your animation as it is, you would need to use javascript.
You need to add a class once the user hovers your element, in this case .running.
The basics are this:
.mouse:after {
content: "";
transform: translateX(100%);
animation: hoverOut 1.5s ease 1;
animation-play-state: paused; /* Initial state of the animation, paused. */
animation-delay: -1.5s; /* Get the first frame of the animation, this value has to be the total duration of the animation with a negative value */
}
.mouse.running:after {
animation-play-state: running; /* When the class is added, the animation state is changed to running */
animation-delay: 0s /* Set back to normal flow. */;
}
Code Snippet:
document.querySelector(".mouse").addEventListener("mouseenter", function() {
this.classList.add("running");
});
.mouse {
margin-bottom: 20px;
padding: 5px;
overflow: hidden;
width: 200px;
}
.mouse:after {
content: "";
height: 2px;
display: block;
background: red;
margin-top: 5px;
transform: translateX(100%);
animation: hoverOut 1.5s ease 1;
animation-play-state: paused;
animation-delay: -1.5s;
}
.mouse.running:after {
animation-play-state: running;
animation-delay: 0s;
}
.mouse:hover:after {
animation: hoverIn 1.5s ease 1;
}
#keyframes hoverOut {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(100%);
}
}
#keyframes hoverIn {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(0%);
}
}
<div class="mouse">watch the line below</div>
Animations always run when the element becomes visible.(or the page loads)
For something like this you want to use a transition. much more streamlined.
Here is your code using transition.
.mouse {
margin-bottom:20px;
padding:5px;
overflow:hidden;
width:200px;
}
.mouse:after {
content:"";
height:2px;
display:block;
background:red;
margin-top:5px;
transition: transform 1s;
transform:translateX(100%);
}
.mouse:hover:after {
transform:translateX(-100%);
}
</style>
</head>
<body>
<div class="mouse">watch the line below</div>
</body>
</html>