MrPowerShell

Keyframes

CSS Keyframes

CSS Keyframes are a surprisingly simple way to write animations.

They take the form of:

`@keyframes name { 
    keyframe { key: value; }
    keyframe2 { key: value; }
}

For example, to create a simple animation that moves an element from left to right, you could write:

@keyframes left-to-right {
    from { left: 0; }
    to { left: 100%; }
}

Keyframes can also specify multiple steps in the animation, using percentages:

@keyframes bounce {
    0%, 20%, 50%, 80%, 100% {
        transform: translateY(0);
    }
    40% {
        transform: translateY(-30px);
    }
    60% {
        transform: translateY(-15px);
    }
}

Keyframes are quite cool, and most of this page is a demonstration of the keyframes defined in the $keyframe variable above.

Before we get to that, let's talk about how we can generate the keyframes in PowerShell. Eagle eyed readers will notice that keyframes, like all of CSS, are basically hashtables.

Making things even easier, keyframes are limited in depth, so we don't even have to use recursion.

Here's a simple function that will convert a dictionary to keyframes:

function dictionaryToKeyframes {

    param(
        [Collections.IDictionary]$keyframe
    )
    process {
        foreach ($keyframeName in $keyframe.Keys) {
            $keyframeKeyframes = $keyframe[$keyframeName]
            "@keyframes $keyframeName {"
            foreach ($percent in $keyframeKeyframes.Keys) {
                "  $percent {"
                $props = $keyframeKeyframes[$percent]
                foreach ($prop in $props.Keys) {
                    $value = $props.$prop
                    "    ${prop}: $value;"
                }
                "  }"
            }
            "}"
            ".$keyframeName { animation-name: $keyframeName; }"
        }
    }    

}

So we can just define a keyframe in a dictionary, and then call the function to generate the keyframes.

( or include it inline )

Then, our keyframes can be defined like this:

$keyframes = [Ordered]@{
    '0%, 20%, 50%, 80%, 100%' = [Ordered]@{
        transform = 'translateY(0)'
    }
    '40%' = [Ordered]@{
        transform = 'translateY(-30px)'
    }
    '60%' = [Ordered]@{
        transform = 'translateY(-15px)'
    }
}

For even better bonus points, we can remember how parameters work in this site generator:

If a site or a page defintes $keyframe variable that maps to the parameter, then it will be passed to this script and display a different set of keyframes.

View Source

param(
    [Alias('Keyframes')]
    [Collections.IDictionary]
    $Keyframe = [Ordered]@{
        'drop-in' = [Ordered]@{
            from = [Ordered]@{
                translate = "0 -150vh"
                scale = "1 200%"            
            }
            to = [Ordered]@{
                translate = "0 0" 
                scale = "1 100%"
            }
        }
        'drop-out' = [Ordered]@{
            from = [Ordered]@{
                translate = "0 0"
                scale = "1 100%"
            }
            to = [Ordered]@{
                translate = "0 150vh" 
                scale = "1 200%"
            }
        }
        'slide-in' = [Ordered]@{
            from = [Ordered]@{
                translate = "-150vw 0"
                scale = "200% 1"            
            }
            to = [Ordered]@{
                translate = "0 0" 
                scale = "100% 1"
            }
        }
        'slide-out' = [Ordered]@{
            from = [Ordered]@{
                translate = "0 0"
                scale = "100% 1"            
            }
            to = [Ordered]@{
                translate = "150vw 0" 
                scale = "200% 1"
            }
        }
        'invert' = [Ordered]@{
            from = [Ordered]@{
                filter = "invert(0%)"            
            }
            to = [Ordered]@{
                filter = "invert(100%)"            
            }
        }
        'blur-out' = [Ordered]@{
            from = [Ordered]@{
                filter = "blur(0px)"            
            }
            to = [Ordered]@{
                filter = "blur(5px)"            
            }
        }
        'blur-in' = [Ordered]@{
            from = [Ordered]@{
                filter = "blur(5px)"            
            }
            to = [Ordered]@{
                filter = "blur(0px)"            
            }
        }
        flip3d = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotateY(0) rotateX(0)"            
            }
            '50%' = [Ordered]@{
                transform = "rotateY(180deg) rotateX(180deg)"            
            }
        }
        rotate3d = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotateY(0) rotateX(0) rotateZ(0)"            
            }
            '25%' = [Ordered]@{
                transform = "rotateY(90deg) rotateX(90deg) rotateZ(90deg)"            
            }
            '50%' = [Ordered]@{
                transform = "rotateY(180deg) rotateX(180deg) rotateZ(180deg)"            
            }
            '75%' = [Ordered]@{
                transform = "rotateY(270deg) rotateX(270deg) rotateZ(270deg)"            
            }        
        }        
        bounce = [Ordered]@{
            '0%, 20%, 50%, 80%, 100%' = [Ordered]@{
                transform = "translateY(0)"
            }
            '40%' = [Ordered]@{
                transform = "translateY(-30px)"
            }
            '60%' = [Ordered]@{
                transform = "translateY(-15px)"
            }
        }        
        'fade-in' = [Ordered]@{
            from = [Ordered]@{
                opacity = "0"
                display = "none"
            }
            '1%' = [Ordered]@{display="block"}
            to = [Ordered]@{
                opacity = "1"
                display = "block"
            }
        }
        'fade-out' = [Ordered]@{
            from = [Ordered]@{
                opacity = "1"
                display = "block"
            }        
            to = [Ordered]@{
                opacity = "0"
                display = "none"
            }
        }    
        'rgb' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                color = "var(--red)"            
            }
            '33%' = [Ordered]@{
                color = "var(--green)"            
            }
            '66%' = [Ordered]@{
                color = "var(--blue)"            
            }
        }
        'foreground-to-background' = [Ordered]@{
            '0%' = [Ordered]@{
                color = "var(--foreground)"
            }
            '100%' = [Ordered]@{
                color = "var(--background)"            
            }
        }
        'background-to-foreground' = [Ordered]@{
            '0%' = [Ordered]@{
                color = "var(--background)"
            }
            '100%' = [Ordered]@{
                color = "var(--foreground)"            
            }
        }
        'terminal-rainbow' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                color = "var(--brightRed)"
            }
            '10%' = [Ordered]@{
                color = "var(--red)"
            }
            '20%' = [Ordered]@{
                color = "var(--brightYellow)"                
            }
            '30%' = [Ordered]@{
                color = "var(--yellow)"
            }
            '40%' = [Ordered]@{
                color = "var(--green)"
            }
            '50%' = [Ordered]@{
                color = "var(--brightGreen)"
            }
            '60%' = [Ordered]@{
                color = "var(--blue)"
            }
            '70%' = [Ordered]@{
                color = "var(--brightBlue)"
            }
            '80%' = [Ordered]@{
                color = "var(--purple)"
            }
            '90%' = [Ordered]@{
                color = "var(--brightPurple)"            
            }
        }
        'spin' = [Ordered]@{
            from = [Ordered]@{
                transform = "rotate(0deg)"            
            }
            to = [Ordered]@{
                transform = "rotate(360deg)"            
            }
        }
        'spin-reverse' = [Ordered]@{
            from = [Ordered]@{
                transform = "rotate(360deg)"            
            }
            to = [Ordered]@{
                transform = "rotate(0deg)"            
            }
        }
        'wiggle' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotate(-3deg)"            
            }
            '50%' = [Ordered]@{
                transform = "rotate(3deg)"            
            }
        }
        'wiggle-reverse' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotate(3deg)"            
            }
            '50%' = [Ordered]@{
                transform = "rotate(-3deg)"            
            }
        }
        'wiggle3d' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotateX(-3deg) rotateY(-3deg) rotateZ(-3deg)"            
            }
            '50%' = [Ordered]@{
                transform = "rotateX(3deg) rotateY(3deg) rotateZ(3deg)"
            }
        }
        'glow' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                'text-shadow' = "0 0 0em var(--foreground)"
            }
            '50%' = [Ordered]@{
                'text-shadow' = "0 0 1em var(--foreground)"
            }
        }
        'glow-box' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                'box-shadow' = "0 0 0em var(--foreground)"
            }
            '50%' = [Ordered]@{
                'box-shadow' = "0 0 1em var(--foreground)"
            }
        }
        'ebb' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                opacity = 1
            }
            '50%' = [Ordered]@{
                opacity = .75
            }
        }
        'strobe' = [Ordered]@{
            '0%,20%,40%,60%,80%,100%' = [Ordered]@{
                opacity = 1
            }
            '10%,30%, 50%, 70%, 90%' = [Ordered]@{
                opacity = .5
            }
        }
        'color-to-gray' = [Ordered]@{
            from = @{filter='grayscale(0%)'}
            to = @{filter='grayscale(100%)'}
        }
        'gray-to-color' = [Ordered]@{
            from = @{filter='grayscale(100%)'}
            to = @{filter='grayscale(0%)'}
        }
        'blink' = [Ordered]@{
            '0%,50%,100%' = [Ordered]@{
                opacity = 1
            }
            '25%,75%' = [Ordered]@{
                opacity = 0
            }
        } 
        'pulse' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "scale(1)"            
            }
            '50%' = [Ordered]@{
                transform = "scale(1.1)"            
            }
        }
        'pulse-reverse' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "scale(1.1)"            
            }
            '50%' = [Ordered]@{
                transform = "scale(1)"            
            }
        }
        'shake' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "translateX(0)"            
            }
            '25%' = [Ordered]@{
                transform = "translateX(-5px)"            
            }
            '75%' = [Ordered]@{
                transform = "translateX(5px)"            
            }
        }
        'shake-reverse' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "translateX(0)"            
            }
            '25%' = [Ordered]@{
                transform = "translateX(5px)"            
            }
            '75%' = [Ordered]@{
                transform = "translateX(-5px)"            
            }
        }    
        'skew' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "skewX(0)"            
            }
            '25%' = [Ordered]@{
                transform = "skewX(-10deg)"            
            }
            '75%' = [Ordered]@{
                transform = "skewX(10deg)"            
            }
        }    
        'skew3d' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotateX(0) rotateY(0)"            
            }
            '25%' = [Ordered]@{
                transform = "rotateX(10deg) rotateY(10deg)"            
            }
            '75%' = [Ordered]@{
                transform = "rotateX(-10deg) rotateY(-10deg)"            
            }
        }
        'skew-reverse' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "skewX(0)"            
            }
            '25%' = [Ordered]@{
                transform = "skewX(10deg)"            
            }
            '75%' = [Ordered]@{
                transform = "skewX(-10deg)"            
            }
        }
        'flip' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotateY(0)"            
            }
            '50%' = [Ordered]@{
                transform = "rotateY(180deg)"
            }
        }
        'flip-reverse' = [Ordered]@{
            '0%,100%' = [Ordered]@{
                transform = "rotateY(180deg)"
            }
            '50%' = [Ordered]@{
                transform = "rotateY(0)"
            }
        }
        'zoom-bounce' = [Ordered]@{
            '0%' = [Ordered]@{
                transform = "scale(1)"
            }
            '25%,75%' = [Ordered]@{
                transform = "scale(1.75)"
            }
            '50%' = [Ordered]@{
                transform = "scale(1.5)"
            }
            '100%' = [Ordered]@{
                transform = "scale(1)"
            }
        }
        'zoom-in' = [Ordered]@{
            from = [Ordered]@{
                transform = "scale(0.1)"
            }
            to = [Ordered]@{
                transform = "scale(1)"
            }
        }
        'zoom-out' = [Ordered]@{
            from = [Ordered]@{
                transform = "scale(1)"
            }
            to = [Ordered]@{
                transform = "scale(0.1)"
            }
        }
        'swing-in' = [Ordered]@{
            from = [Ordered]@{
                # 'transform-origin' = "50% 50%"
                transform = "rotate(-90deg)"
            }
            to = [Ordered]@{
                # 'transform-origin' = "50% 50%"
                transform = "rotate(0deg)"
            }
        }
        'spring-up' = [Ordered]@{
            '0%' = [Ordered]@{
                transform = "scale(1) translateY(0)"
            }
            '20%' = [Ordered]@{
                transform = "scale(1.25,0.75) translateY(0)"
            }
            '40%' = [Ordered]@{
                transform = "scale(0.75,1.25) translateY(-2em)"
            }
            '60%' = [Ordered]@{
                transform = "scale(1.15,0.85) translateY(0)"
            }
            '80%' = [Ordered]@{
                transform = "scale(0.95,1.05) translateY(-1em)"
            }
            '100%' = [Ordered]@{
                transform = "scale(1) translateY(0)"
            }        
        }
        'spring-down' = [Ordered]@{
            '0%' = [Ordered]@{
                transform = "scale(1) translateY(0)"
            }
            '20%' = [Ordered]@{
                transform = "scale(1.25,0.75) translateY(0)"
            }
            '40%' = [Ordered]@{
                transform = "scale(0.75,1.25) translateY(2em)"
            }
            '60%' = [Ordered]@{
                transform = "scale(1.15,0.85) translateY(0)"
            }
            '80%' = [Ordered]@{
                transform = "scale(0.95,1.05) translateY(1em)"
            }
            '100%' = [Ordered]@{
                transform = "scale(1) translateY(0)"
            }        
        }
        'spring-left' = [Ordered]@{
            '0%' = [Ordered]@{
                transform = "scale(1) translateX(0)"
            }
            '20%' = [Ordered]@{
                transform = "scale(0.75,1.25) translateX(0)"
            }
            '40%' = [Ordered]@{
                transform = "scale(1.25,0.75) translateX(-2em)"
            }
            '60%' = [Ordered]@{
                transform = "scale(0.85,1.15) translateX(0)"
            }
            '80%' = [Ordered]@{
                transform = "scale(1.05,0.95) translateX(-1em)"
            }
            '100%' = [Ordered]@{
                transform = "scale(1) translateX(0)"
            }        
        }
        'spring-right' = [Ordered]@{
            '0%' = [Ordered]@{
                transform = "scale(1) translateX(0)"
            }
            '20%' = [Ordered]@{
                transform = "scale(0.75,1.25) translateX(0)"
            }
            '40%' = [Ordered]@{
                transform = "scale(1.25,0.75) translateX(2em)"
            }
            '60%' = [Ordered]@{
                transform = "scale(0.85,1.15) translateX(0)"
            }
            '80%' = [Ordered]@{
                transform = "scale(1.05,0.95) translateX(1em)"
            }
            '100%' = [Ordered]@{
                transform = "scale(1) translateX(0)"
            }        
        }
    }
)

$Title = "CSS Keyframes"
$Description = "CSS Keyframes are cool (and easy to write in PowerShell)"

function dictionaryToKeyframes {
    param(
        [Collections.IDictionary]$keyframe
    )
    process {
        foreach ($keyframeName in $keyframe.Keys) {
            $keyframeKeyframes = $keyframe[$keyframeName]
            "@keyframes $keyframeName {"
            foreach ($percent in $keyframeKeyframes.Keys) {
                "  $percent {"
                $props = $keyframeKeyframes[$percent]
                foreach ($prop in $props.Keys) {
                    $value = $props.$prop
                    "    ${prop}: $value;"
                }
                "  }"
            }
            "}"
            ".$keyframeName { animation-name: $keyframeName; }"
        }
    }    
}

"<style>"
dictionaryToKeyframes $Keyframe
foreach ($n in 1..10) {
    ".for$n { animation-duration: ${n}s; }"
}
".infinite { animation-iteration-count: infinite; }"
".forwards { animation-fill-mode: forwards; }"

".example {
    display: inline-block;
    text-align: center;
    font-size: 2em;
    padding: 1em;
    animation-duration: 2s;
}"

"</style>"

ConvertFrom-Markdown -InputObject (@'
## CSS Keyframes

<a href='#Examples'><button>Skip to Examples</button></a>

[CSS Keyframes](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes) are a surprisingly simple way to write animations.

They take the form of:

~~~css
`@keyframes name { 
    keyframe { key: value; }
    keyframe2 { key: value; }
}
~~~

For example, to create a simple animation that moves an element from left to right, you could write:

~~~css
@keyframes left-to-right {
    from { left: 0; }
    to { left: 100%; }
}
~~~

Keyframes can also specify multiple steps in the animation, using percentages:

~~~css
@keyframes bounce {
    0%, 20%, 50%, 80%, 100% {
        transform: translateY(0);
    }
    40% {
        transform: translateY(-30px);
    }
    60% {
        transform: translateY(-15px);
    }
}
~~~


Keyframes are quite cool, and _most_ of this page is a demonstration of the keyframes defined in the `$keyframe` variable above.

Before we get to that, let's talk about how we can generate the keyframes in PowerShell.
'@ + @"

Eagle eyed readers will notice that keyframes, like all of CSS, are basically hashtables.

Making things even easier, keyframes are limited in depth, so we don't even have to use recursion.

Here's a simple function that will convert a dictionary to keyframes:

~~~PowerShell
function dictionaryToKeyframes {
$(Get-Command dictionaryToKeyframes | Select-Object -ExpandProperty Definition)
}
~~~

So we can just define a keyframe in a dictionary, and then call the function to generate the keyframes.

( or include it inline )

Then, our keyframes can be defined like this:
~~~PowerShell
`$keyframes = [Ordered]@{
    '0%, 20%, 50%, 80%, 100%' = [Ordered]@{
        transform = 'translateY(0)'
    }
    '40%' = [Ordered]@{
        transform = 'translateY(-30px)'
    }
    '60%' = [Ordered]@{
        transform = 'translateY(-15px)'
    }
}
~~~

For even better bonus points, we can remember how parameters work in this site generator:

If a site or a page defintes `$keyframe` variable that maps to the parameter, then it will be passed to this script and display a different set of keyframes.
"@) | 
    Select -ExpandProperty Html


"<details>"
"<summary>"
"View Source"
"</summary>"
"<pre><code class='language-powershell'>"
[Web.HttpUtility]::HtmlEncode($MyInvocation.MyCommand.ScriptBlock)
"</code></pre>"
"</details>"
'<div>'
"<h2 id='Examples'>Examples</h2>"
@(
foreach ($key in $keyframe.Keys) {
    "<p class='$key example infinite for5'>$key</p>"
}
) -join '</div>
<div>'
'</div>'

Examples

drop-in

drop-out

slide-in

slide-out

invert

blur-out

blur-in

flip3d

rotate3d

bounce

fade-in

fade-out

rgb

foreground-to-background

background-to-foreground

terminal-rainbow

spin

spin-reverse

wiggle

wiggle-reverse

wiggle3d

glow

glow-box

ebb

strobe

color-to-gray

gray-to-color

pulse

pulse-reverse

shake

shake-reverse

skew

skew3d

skew-reverse

flip

flip-reverse

zoom-bounce

zoom-in

zoom-out

swing-in

spring-up

spring-down

spring-left

spring-right