# Animations
- Just like transitions, animations allow you to gradually or smoothly change one or more properties from one style to another
- Where transitions can only be triggered on state changes (like
:hover
,:active
,:focus
,:checked
, ...), animations can be used in a wider context - Every animation consists of two parts:
- an animation style that describes the duration, timing, direction, ... of the animation
- a set of keyframes with a start, end and intermediate points that describe the changes itself
- Take a look at one of the oldest animations: the Spiderman example from 2010
# @keyframes
- The @keyframes at-rule contains all the different keyframes (states) of the animation
- Each keyframe contains the properties of an element at a specific moment in time
- Because the actual duration of the animation is determined by the animation properties, the different keyframe selectors (stops or breaking points) within @keyframes are written in percentages
0%
(orfrom
): start of the animation100%
(orto
): end of the animation50%
: in the middle of the animation- ...
- You can omit the
0%
keyframe if the start of the animation contains no values
# Quick example
- Let us discuss a simple animation (before we dive into the details of the animation style)
- The animation on the
p
tag uses thechangeColor
at-keyframe rule - The animation runs in 4 seconds (
4s
) and has aninfinite
loop
- The animation on the
- Result: the background color of the
p
tag changes in4s
fromorange
toskyblue
and then starts over again
p {
...
animation: changeColor 4s infinite;
}
@keyframes changeColor {
0% {
background-color: orange;
}
100% {
background-color: skyblue;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Saepe quod totam est iure ad suscipit harum eum distinctio in nobis vero error blanditiis fuga reprehenderit ex animi libero architecto, optio similique quibusdam numquam, perspiciatis tempore expedita pariatur dolorem! Sed nihil ut voluptatem, dolore vitae harum expedita eum sunt voluptate labore!</p>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; line-height: 1.5; padding: 2rem; background-color: aliceblue; }
p { text-align: justify; border: 1px solid #000; padding: 1rem; background-color: #fff; animation: changeColor 4s infinite; }
@keyframes changeColor { 0% { background-color: orange; } /* 50% { background-color: greenyellow; } */ 100% { background-color: skyblue; } }
# Exercises
- Change
0%
tofrom
and100%
toto
: nothing changes
@keyframes changeColor {
from {
background-color: orange;
}
to {
background-color: skyblue;
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
- Add a new keyframe at
50%
to change the in-betweenbackground-color
togreenyellow
@keyframes changeColor {
from {
background-color: orange;
}
50% {
background-color: greenyellow;
}
to {
background-color: skyblue;
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- Add a pause of two seconds in the middle of the animation where the
background-color
staysgreenyellow
@keyframes changeColor {
from {
background-color: orange;
}
25%, 75% {
background-color: greenyellow;
}
to {
background-color: skyblue;
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
- Move
background-color: orange;
to thep
tag and delete thefrom
keyframe
p {
...
animation: changeColor 4s infinite;
background-color: orange;
}
@keyframes changeColor {
25%, 75% {
background-color: greenyellow;
}
to {
background-color: skyblue;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Animation properties
- The animation properties are:
EMMET instruction | result | default | description |
---|---|---|---|
animn + TAB | animation-name: ; | the names of one or more @keyframes at-rules | |
animdur + TAB | animation-duration: ; | 0 | duration of the animation in seconds (s ) or milliseconds (ms ) |
animdel + TAB | animation-delay: ; | 0 | delay (s orms ) before the animation starts |
animtf + TAB | animation-timing-function: ; | ease | speed curve of the animation effect |
animic + TAB | animation-iteration-count: ; | 1 | number of times the animation plays |
animdir + TAB | animation-direction: ; | normal | direction the animation plays |
animfm + TAB | animation-fill-mode: ; | none | sets how the animation applies styles before and/or after its execution |
animps + TAB | animation-play-state: ; | running | sets whether the animation is running or paused |
anim + TAB | animation: ; | shorthand for all the above properties | |
anim- + TAB | animation: ; | shorthand for all the above properties but this time with hints |
REMARKS
animation
shorthand:- It is good practice (not required) to place the
amimation-name
always first - When using the
animation-duration
AND theanimation-delay
, theanimation-duration
ALWAYS comes first!
- It is good practice (not required) to place the
animation-duration
is comparable (does exactly the same for animations) totransition-duration
animation-timing-function
is comparable totransition-timing-function
animation-delay
- Comparable to
transition-delay
- In combination with an infinite loop, the
transition-delay
is the time before the animation starts and not a delay inside each loop!
- Comparable to
# animation-name
- Sets the name of the
@keyframes
at-rule(s) to be applied to a specific element - You can animate an element with two or more
@keyframes
at-rules by separating the names with a comma - For example:
div {
animation: animation1 1s, animation2 2s infinite;
}
1
2
3
2
3
# animation-iteration-count
- The
animation-iteration-count
property defines how many times the animation repeats itself- The possible values are positive numbers (also
.5
for half an animation) orinfinite
- The default value is
1
- The possible values are positive numbers (also
- In the example below:
- All balls use the same at-keyframes rule
- The first ball rolls only once, the second ball rolls two times and the last ball rolls infinitely
(TIP: Put areturn
inside the HTML part of the embedded CodePen to reload the page)
div:first-child {
animation: roll 2s;
}
div:nth-child(2) {
animation: roll 2s 2;
}
div:nth-child(3) {
animation: roll 2s infinite;
}
@keyframes roll {
to { transform: translateX(300px) rotate(360deg); }
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
<div>1</div> <div>2</div> <div>∞</div>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; line-height: 1.5; padding: 1rem; background-color: aliceblue; text-align: center; }
div { width: 3rem; line-height: 3rem; font-size: 2rem; font-weight: bold; margin: 1rem; border: 1px solid #000; background-color: lightgreen; border-radius: 50%; }
div:first-child { animation: roll 2s; }
div:nth-child(2) { animation: roll 2s 2; }
div:nth-child(3) { animation: roll 2s infinite; }
@keyframes roll { to { transform: translateX(300px) rotate(360deg); } }
# animation-direction
- Possible values for
animation-direction
are:normal
: animation runs from0%
to100%
reverse
: animation runs from100%
to0%
alternate
: animation runs from0%
to100%
and back to0%
alternate-reverse
: animation runs from100%
to0%
and back to100%
div:first-child {
animation: roll 2s infinite; /* default direction: normal */
}
div:nth-child(2) {
animation: roll 2s infinite reverse;
}
div:nth-child(3) {
animation: roll 2s infinite alternate;
}
div:nth-child(4) {
animation: roll 2s infinite alternate-reverse;
}
@keyframes roll {
to { transform: translateX(300px) rotate(360deg); }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div>1</div> <div>2</div> <div>3</div> <div>4</div>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; line-height: 1.5; padding: 1rem; background-color: aliceblue; text-align: center; }
div { width: 3rem; line-height: 3rem; font-size: 2rem; font-weight: bold; margin: 1rem; border: 1px solid #000; background-color: coral; border-radius: 50%; }
div:first-child { animation: roll 2s infinite; /* default direction: normal */ }
div:nth-child(2) { animation: roll 2s infinite reverse; }
div:nth-child(3) { animation: roll 2s infinite alternate; }
div:nth-child(4) { animation: roll 2s infinite alternate-reverse; }
@keyframes roll { to { transform: translateX(300px) rotate(360deg); } }
# animation-fill-mode
- The
animation-fill-mode
sets which values are applied BEFORE the animation starts and/or AFTER the animation is ended - To demonstrate this behavior, we start with a
black
box of100px
wide (the default values)
div {
background-color: black;
color: whitesmoke;
width: 100px;
padding: .5rem;
margin-bottom: .5rem;
}
1
2
3
4
5
6
7
2
3
4
5
6
7
- At the start of the animation, the box is
50%
wide and red - At the end of the animation, the box is
100%
wide and blue
@keyframes color {
from {
width: 50%;
background-color: firebrick;
}
to {
width: 100%;
background-color: dodgerblue;
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- To make all possible values for
animate-fill-mode
visible, the animations start with a delay of 2 seconds when you hover thediv
tags
fill-mode | BEFORE animation starts | AFTER animation ends |
---|---|---|
none | starts with default value (black , 100px ) | goes back to the default value (black , 100px ) |
forwards | starts with default value (black , 100px ) | stays at the last keyframe (dodgerblue , 100% ) |
backwards | starts with the first keyframe (firebrick , 50% ) | goes back to the default value (black , 100px ) |
both | starts with the first keyframe (firebrick , 50% ) | stays at the last keyframe (dodgerblue , 100% ) |
- Now, hover a
div
tag and don't move your mouse until the animation ends!
<div>normal</div> <div>forwards</div> <div>backwards</div> <div>both</div>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; line-height: 1.5; padding: 1rem; background-color: aliceblue; }
div { background-color: black; color: whitesmoke; width: 100px; padding: .5rem; margin-bottom: .5rem; }
div:first-child:hover { animation: color 1s 2s; }
div:nth-child(2):hover { animation: color 1s 2s forwards; }
div:nth-child(3):hover { animation: color 1s 2s backwards; }
div:nth-child(4):hover { animation: color 1s 2s both; }
@keyframes color { from { width: 50%; background-color: firebrick; } to { width: 100%; background-color: dodgerblue; } }
# animation-play-state
- The
animation-play-state
property sets whether an animation isrunning
(default) orpaused
- In the example below:
- All circles running by default
- As long as you are hovering a
div
tag, theanimation-play-state
is set topaused
div {
...
animation: rotate 1s linear infinite; /* default animation-play-state is running */
}
div:hover {
animation-play-state: paused;
}
@keyframes rotate {
from {
background-color: coral;
}
50% {
background-color: greenyellow;
}
to {
background-color: coral;
transform: rotate(360deg);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div>1</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: Arial, "Helvetica Neue", Helvetica, sans-serif; line-height: 1.5; padding: 1rem; background-color: aliceblue; text-align: center; }
div { display: inline-block; width: 3rem; height: 3rem; font-size: 1.2rem; font-weight: bold; margin: 1rem; border: 1px solid #000; border-radius: 50%; animation: rotate 1.5s linear infinite; /* default animation-play-stateis running */ }
div:hover { animation-play-state: paused; }
@keyframes rotate { from { background-color: coral; } 50% { background-color: greenyellow; } to { background-color: coral; transform: rotate(360deg); } }
# Examples
# Animated block
- In this example, the animation of the
span
tag starts after 1.5 seconds. The animation lasts 2 seconds and thespan
goes forth and back infinitely withanimation-timing-function: ease-in-out
. Notice the use ofanimation-fill-mode:both
(orbackwards
) to start the animation correctly. - The keyframes differ in three ways:
- The
span
tag moves from left to right - Its shape changes from a circle to a rectangle
- The color changes from
red
overorange
,yellow
andgreen
toblue
- The
<div> <span></span> </div>
* { margin: 0; padding: 0; box-sizing: border-box; }
div { padding: 1rem; height: 100vh; display: flex; align-items: center; }
span { display: inline-block; height: 3rem; width: 3rem; border: 1px solid #000; animation: moveSpan 2s 1.5s ease-in-out infinite alternate both; }
@keyframes moveSpan { 0% { border-radius: 50%; background-color: red; }
25% { background-color: orange; }
50% { background-color: yellow; }
75% { background-color: green; }
100% { border-radius: 0; background-color: blue; transform: translateX(300px); /* transform: translateX(100vw) translateX(-5rem); */ /* transform: translateX(calc(100vw - 5rem)); */ } }
REMARKS
- The animation is not very responsive as the animation always ends at
300px
(even on very small or very large screens) - You can use some mathematics to animate over the full
width
of the viewport- The
span
tag starts at1rem
from the left - When we translate with
100vw
in the last keyframe, it goes1rem
outside the viewport - The
span
tag itself is3rem
wide - To stop the animation
1rem
before the right side, we have to move it1rem + 3rem + 1rem
=5rem
back
- The
- Change the
transform
property at the100%
keyframe:- from:
translateX(300px)
- to:
translateX(100vw) translateX(-5rem)
(Yes, the browser does the calculation100vw - 5rem
for two or more identical methods!)
- from:
- It can be even shorter because you can use the
calc()
method of CSS:translateX(calc(100vw - 5rem))
# Exercises
- Remove the
object-fit
property from theimg
tag and observe how the animation changes- Set the
object-fit
property back tonone
- Set the
- Change the
object-position
fromtop left
totop right
- Set the
object-position
property back totop left
- Set the
- Change the
opacity
inside the keyframes instead of thewidth
@keyframes grow {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes shrink {
from { opacity:1; }
to { opacity: 0; }
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# Moving background images
- In the next example, we have a
section
tag and adiv.car
<section> <div class="car"></div> </section>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; background-color: aliceblue; display: flex; justify-content: center; align-items: center; }
section { width: 80vw; height: 150px; position: relative; border: 1px solid #000; background-image: url(https://itf-web-essentials.netlify.app/assets/landscape.jpg); background-repeat: repeat-x; animation: moveLandscape 8s linear infinite; overflow: hidden; }
@keyframes moveLandscape { to { background-position: -800px 0; } }
.car { position: absolute; left: 0; bottom: 3px; width: 67px; height: 30px; background-image: url(https://itf-web-essentials.netlify.app/assets/car1.png); transform: translateX(-150px); animation: moveCar 4s linear infinite; }
@keyframes moveCar { to { transform: translateX(90vw); } }
- The
section
tag:- has a
width
of80vw
and is centred at the middle of the viewport - has a repeating background image (
800px
x150px
) - is moved from right to left using the
background-position
property
section { width: 80vw; height: 150px; position: relative; border: 1px solid #000; background-image: url(https://itf-web-essentials.netlify.app/assets/landscape.jpg); background-repeat: repeat-x; animation: moveLandscape 8s linear infinite; overflow: hidden; } @keyframes moveLandscape { to { background-position: -800px 0; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 - has a
div.car
:- is absolutely positioned inside the
section
tag - has a non-repeating background image (
car1.png
) - is initially placed
-150px
outside thesection
by thetranslateX()
property
(you can't see it because theoverflow
property of thesection
is set tohidden
) - is moved from left to right by the
translateX()
property
(the right side is set to90vw
so the car is moving outside thesection
which has a width of80vw
)
.car { position: absolute; left: 0; bottom: 3px; width: 67px; height: 30px; background-image: url(https://itf-web-essentials.netlify.app/assets/car1.png); transform: translateX(-150px); animation: moveCar 4s linear infinite; } @keyframes moveCar { to { transform: translateX(90vw); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15- is absolutely positioned inside the
# Pac man
<figure> <img src="https://itf-web-essentials.netlify.app/assets/pacman.gif" alt="Pac Man"> </figure>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background-color: aliceblue; }
img { padding: 2rem; animation: pacman 10s infinite linear; }
@keyframes pacman { 25% { transform: translateX(360px); } 25.01% { transform: translateX(360px) rotate(90deg); } 50% { transform: translateX(360px) translateY(200px) rotate(90deg); } 50.01% { transform: translateX(360px) translateY(200px) rotate(180deg); } 75% { transform: translateY(200px) rotate(180deg); } 75.01% { transform: translateY(200px) rotate(270deg); } 99.99% { transform: rotate(270deg); } }
- The animated gif (
pacman.gif
) runs inside a rectangle box with the following properties:
keyframe | translateX() | translateY() | rotate() | action |
---|---|---|---|---|
0% | 0px | 0px | 0deg | start position |
25% | 360px | 0px | 0deg | move pac man to the right |
25.01% | 360px | 0px | 90deg | rotate pac man 90deg |
50% | 360px | 200px | 90deg | move pac man down |
50.01% | 360px | 200px | 180deg | rotate pac man 180deg |
75% | 0px | 200px | 180deg | move pacman to the left |
75.01% | 0px | 200px | 270deg | rotate pac man 270deg |
99.99% | 0px | 0px | 270deg | move pac man up |
100% | 0px | 0px | 0deg | same as start position |
- All the red values are the default values for
translateX()
,translateX()
orrotate()
and can be omitted - The keyframes at
0%
and at100%
can be left out entirely, because they only contain default values - The optimised result for the keyframes:
@keyframes pacman {
25% {
transform: translateX(360px);
}
25.01% {
transform: translateX(360px) rotate(90deg);
}
50% {
transform: translateX(360px) translateY(200px) rotate(90deg);
}
50.01% {
transform: translateX(360px) translateY(200px) rotate(180deg);
}
75% {
transform: translateY(200px) rotate(180deg);
}
75.01% {
transform: translateY(200px) rotate(270deg);
}
99.99% {
transform: rotate(270deg);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Exercise (optional)
- Like in the previous example, let pac man use the full viewport to move around
- The space between pac man and the browser border is
2rem
- Pac man has a square box of
38px
- As such,
translateX(360px)
andtranslateY(200px)
should be replaced bytranslateX(calc(100vw - 4rem - 38px))
translateY(calc(100vh - 4rem - 38px))
- The space between pac man and the browser border is
# Advanced examples (optional)
NEED-TO-KNOW
These examples are NOT PART OF THE SUBJECT MATTER to be mastered for this course, but they may challenge you or inspire you for future projects
# Moving background images
- Let's retake the basic example with moving background images
- In the middle of the animation the yellow car (car1.png) is switched to a red car (car2.png), riding in the opposite direction
<section> <div class="car"></div> </section>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; background-color: aliceblue; display: flex; justify-content: center; align-items: center; }
section { width: 80vw; height: 150px; position: relative; border: 1px solid #000; background-image: url(https://itf-web-essentials.netlify.app/assets/landscape.jpg); background-repeat: repeat-x; animation: moveLandscape 8s linear infinite; overflow: hidden; }
@keyframes moveLandscape { to { background-position: -800px 0; } } .car { position: absolute; left: 0; bottom: 3px; width: 67px; height: 30px; background-image: url(https://itf-web-essentials.netlify.app/assets/car1.png); transform: translateX(-150px); animation: moveCar1 4s linear infinite; }
@keyframes moveCar1 { 50% { transform: translateX(90vw); background-image: url(https://itf-web-essentials.netlify.app/assets/car1.png); } 50.01% { transform: translateX(90vw) translateY(-15px) rotateY(180deg); background-image: url(https://itf-web-essentials.netlify.app/assets/car2.png); } to { transform: translateX(-150px) translateY(-15px) rotateY(180deg); background-image: url(https://itf-web-essentials.netlify.app/assets/car2.png); } }
keyframe | translateX() | translateY() | rotateY() | image | action |
---|---|---|---|---|---|
0% | -150px | 0px | 0deg | car1.jpg | start position |
50% | 90vw | 0px | 0deg | car1.jpg | Move the yellow car to the right |
50.01% | 90vw | -15px | 180deg | car2.jpg | Switch to the red car, rotate is 180deg , lift it 15px up and drive back |
100% | -150px | -15px | 180deg | car2.jpg | end position |
- The keyframe at
0%
can be left out entirely, because this only contains the default values - The optimised result for the keyframes:
@keyframes moveCar1 {
50% {
transform: translateX(90vw);
background-image: url(https://itf-web-essentials.netlify.app/assets/car1.png);
}
50.01% {
transform: translateX(90vw) translateY(-15px) rotateY(180deg);
background-image: url(https://itf-web-essentials.netlify.app/assets/car2.png);
}
to {
transform: translateX(-150px) translateY(-15px) rotateY(180deg);
background-image: url(https://itf-web-essentials.netlify.app/assets/car2.png);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# Progress bar
- This example is a Bootstrap progress bar clone, built with a repeating background gradient pattern
<div class="preloader"></div>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: Verdana, Geneva, sans-serif; line-height: 1.5; height: 100vh; background-color: aliceblue; display: flex; justify-content: center; align-items: center; }
.preloader { display: inline-block; width: 80vw; height: 1rem; border: 1px solid #f95f5f; border-radius: .25rem; background-color: #f95f5f; background-image: linear-gradient(-45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent 100%); background-size: 2rem 2rem; animation: slide 20s linear infinite; }
@keyframes slide { from { background-position: 0 0; }
to { background-position: 50rem 0; } }
# Scrolling text
TIP
Disable the overfow
property on the div
tag to see how (based on adjusting the top margin of the first paragraph) this animation really works
<div> <p>my</p> <p>cool</p> <p>animation</p> </div>
@import url('https://fonts.googleapis.com/css2?family=Rubik+Mono+One&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; }
html { font-size: 16px; }
body { font-family: 'Rubik Mono One', sans-serif; line-height: 1.5; height: 100vh; background-color: aliceblue; display: flex; justify-content: center; align-items: center; }
div { text-align: center; height: 4.5rem; border: 1px solid #000; overflow: hidden; }
p { font-size: 3rem; padding: 0 1rem; margin: 4.5rem 0; color: white; background-color: #F7544A; }
p:first-child { background-color: #7CBCF7; }
p:last-child { background-color: #A1AB33; }
div p:first-child { animation: roll 5s linear infinite; }
@keyframes roll { 0% { margin-top: 4.5rem; }
5%, 33% { margin-top: 0; /* MY visible */ }
38%, 66% { margin-top: -9rem; /* COOL visible */ }
71%, 99.99% { margin-top: -18rem; /* ANIMATION visible */ }
100% { margin-top: 4.5rem; } }
# Animated SVG
- The SVG has a
width
andheight
of100px
- The SVG contains a square box of
80px
with a nearly white background and a second square box of60px
with anorangered
background - Let's make an animation of 3 seconds with this SVG
<svg id="outline" width="100" height="100"> <rect id="whiteBox" x="10" y="10" width="80" height="80" fill="#ffbea6" /> <rect id="orangeBox" x="20" y="20" width="60" height="60" fill="orangered" /> </svg>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background-color: orangered; }
/* #orangeBox { animation: increaseHeight 3s linear infinite; }
@keyframes increaseHeight { from { height: 0; } 50%, to { height: 60px; } } */
/* #outline { animation: rotate180deg 3s linear infinite; }
@keyframes rotate180deg { form { transform: rotate(0); } 17%, to { transform: rotate(180deg); } } */
- Step 1: Animate
rect#orangeBox
- the
rect#orangeBox
starts with aheight
of0px
- After 1.5 seconds (
50%
), it starts to grow to its fullheight
(60px
)
#orangeBox { animation: increaseHeight 3s linear infinite; } @keyframes increaseHeight { from { height: 0; } 50%, to { height: 60px; } }
1
2
3
4
5
6
7 - the
- Step 2: Animate
svg#outline
- Rotate
svg#outline
by180deg
within 0.5 seconds (17%
)
(The child elements are also rotated!)
#outline { animation: rotate180deg 3s linear infinite; } @keyframes rotate180deg { form { transform: rotate(0); } 17%, to { transform: rotate(180deg); } }
1
2
3
4
5
6
7 - Rotate
- Step 3: Add a delay of 1 second to
rect#orangeBox
#orangeBox { animation: increaseHeight 3s linear infinite 1s; }
1
2
3
# Image overlay
- Let's try an animated overlay like we saw in the Photoshop tutorial gettotallyrad.com
- Because we don't know anything about JavaScript yet, let's do it with a simple hover effect
<figure> <img src="https://picsum.photos/id/63/300/200?grayscale" alt="cup of coffee - grayscale"> <img src="https://picsum.photos/id/63/300/200" alt="cup of coffee - color"> </figure>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { height: 100vh; background-color: aliceblue; display: flex; justify-content: center; align-items: center; }
figure { width: 300px; height: 200px; position: relative; border: 1px solid black; box-shadow: 0 0 1rem rgba(0, 0, 0, .7); }
img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: none; object-position: top left; }
figure img:last-child { animation: grow 1s; }
@keyframes grow { from { width: 0; } to { width: 100%; } }
figure:hover img:last-child { animation: shrink 1s both; }
@keyframes shrink { from { width: 100%; } to { width: 0; } }
- Both images (the color image on top) are positioned absolutely inside the
figure
tag - It's very important to set the
object-fit
tonone
and theobject-position
toleft
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: none;
object-position: top left;
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- When we hover the
figure
tag, thewidth
of the color image goes from100%
to0
figure:hover img:last-child {
animation: shrink 1s both;
}
@keyframes shrink {
from { width: 100%; }
to { width: 0; }
}
1
2
3
4
5
6
7
2
3
4
5
6
7
- To make a smooth transition back when we leave the
figure
, it's also necessary to make a transition back from0
to100%
figure img:last-child {
animation: grow 1s;
}
@keyframes grow {
from { width: 0; }
to { width: 100%; }
}
1
2
3
4
5
6
7
2
3
4
5
6
7