# 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% (or from): start of the animation
    • 100% (or to): end of the animation
    • 50%: 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 the changeColor at-keyframe rule
    • The animation runs in 4 seconds (4s) and has an infinite loop
  • Result: the background color of the p tag changes in 4s from orange to skyblue 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
    
<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

  1. Change 0% to from and 100% to to: nothing changes

 


 




@keyframes changeColor {
    from {
      background-color: orange;
    }
    to {
      background-color: skyblue;
    }
}
1
2
3
4
5
6
7
8
  1. Add a new keyframe at 50% to change the in-between background-color to greenyellow




 
 
 





@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
  1. Add a pause of two seconds in the middle of the animation where the background-color stays greenyellow




 







@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
  1. Move background-color: orange; to the p tag and delete the from 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

# 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 the animation-delay, the animation-duration ALWAYS comes first!
  • animation-duration is comparable (does exactly the same for animations) to transition-duration
  • animation-timing-function is comparable to transition-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!

# 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

# animation-iteration-count

  • Theanimation-iteration-count property defines how many times the animation repeats itself
    • The possible values are positive numbers (also .5 for half an animation) or infinite
    • The default value is 1
  • 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 a return 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
    
<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 from 0% to 100%
    • reverse: animation runs from 100% to 0%
    • alternate: animation runs from 0% to 100% and back to 0%
    • alternate-reverse: animation runs from 100% to 0% and back to 100%

 


 


 


 





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
    
<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 of 100px wide (the default values)

 

 




div {
    background-color: black;
    color: whitesmoke;
    width: 100px;
    padding: .5rem;
    margin-bottom: .5rem;
}
1
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
  • To make all possible values for animate-fill-mode visible, the animations start with a delay of 2 seconds when you hover the div 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 is running (default) or paused
  • In the example below:
    • All circles running by default
    • As long as you are hovering a div tag, the animation-play-state is set to paused


 


 














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
    
<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 the span goes forth and back infinitely with animation-timing-function: ease-in-out. Notice the use of animation-fill-mode:both (or backwards) 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 over orange, yellow and green to blue
    
<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 at 1rem from the left
    • When we translate with 100vw in the last keyframe, it goes 1rem outside the viewport
    • The span tag itself is 3rem wide
    • To stop the animation 1rem before the right side, we have to move it 1rem + 3rem + 1rem = 5rem back
  • Change the transform property at the 100% keyframe:
    • from: translateX(300px)
    • to: translateX(100vw) translateX(-5rem)
      (Yes, the browser does the calculation 100vw - 5rem for two or more identical methods!)
  • It can be even shorter because you can use the calc() method of CSS: translateX(calc(100vw - 5rem))

# Exercises

  1. Remove the object-fit property from the img tag and observe how the animation changes
    • Set theobject-fit property back to none
  2. Change the object-position from top left to top right
    • Set the object-position property back to top left
  3. Change the opacity inside the keyframes instead of the width

 
 


 
 


@keyframes grow {
  from {  opacity: 0; }
  to { opacity: 1; }
}
@keyframes shrink {
  from { opacity:1; }
  to { opacity: 0; }
}
1
2
3
4
5
6
7
8

# Moving background images

  • In the next example, we have a section tag and a div.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 of 80vw and is centred at the middle of the viewport
    • has a repeating background image (800px x 150px)
    • 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
  • div.car:
    • is absolutely positioned inside the section tag
    • has a non-repeating background image (car1.png)
    • is initially placed -150px outside the section by the translateX() property
      (you can't see it because the overflow property of the section is set to hidden)
    • is moved from left to right by the translateX() property
      (the right side is set to 90vw so the car is moving outside the section which has a width of 80vw)






     
     
     



     



    .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

# 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() or rotate() and can be omitted
  • The keyframes at 0% and at 100% 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

# 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) and translateY(200px) should be replaced by
      • translateX(calc(100vw - 4rem - 38px))
      • translateY(calc(100vh - 4rem - 38px))

# 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

# Progress bar

    
<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 and height of 100px
  • The SVG contains a square box of 80px with a nearly white background and a second square box of 60px with an orangered 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 a height of 0px
    • After 1.5 seconds (50%), it starts to grow to its full height (60px)

     


     
     


    #orangeBox {
      animation: increaseHeight 3s linear infinite;
    }
    @keyframes increaseHeight {
      from { height: 0; }
      50%, to { height: 60px; }
    }
    
    1
    2
    3
    4
    5
    6
    7
  • Step 2: Animate svg#outline
    • Rotate svg#outline by 180deg 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
  • 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 to none and the object-position to left






 
 


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
  • When we hover the figure tag, the width of the color image goes from 100% to 0

 


 
 


figure:hover img:last-child {
    animation: shrink 1s both;
}
@keyframes shrink {
    from { width: 100%; }
    to { width: 0; }
}
1
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 from 0 to 100%

 


 
 


figure img:last-child {
    animation: grow 1s;
}
@keyframes grow {
    from { width: 0; }
    to {  width: 100%; }
}
1
2
3
4
5
6
7
Last Updated: 12/7/2021, 3:18:41 PM