tlaternet-webserver/templates/src/lib/scss/_typed.scss

137 lines
3.8 KiB
SCSS
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

@use "sass:math";
@use "sass:list";
/// Animate a blinking cursor.
@mixin cursor($duration) {
$name: cursor-09d03260130069771b6ddc1cb415f39fdd27ddfab7b01ba91273398c2d245ae4;
// The number of times we need to blink is = the number of full
// seconds (500ms * 2) that fit in the total duration, rounded up,
// and doubled.
$iterations: math.ceil(math.div($duration, 1s)) * 2;
animation: $name ease-in-out 500ms $iterations alternate;
content: " ";
@keyframes #{$name} {
from {
content: " ";
}
to {
content: "";
}
}
}
/// Animate a piece of text as if it was being typed by a human.
@mixin typed($text, $duration) {
word-break: break-all;
// We don't want a linearly typed set of text, which makes this
// singificantly more complex.
//
// CSS animations normally do not permit per-frame changes in
// duration (since the total animation time is fixed). This means we
// need to create multiple animations, and delay them so that they
// happen in the time sequence we want.
//
// We generate the raw values with _generate-animations, and then
// split up the result into the animation API.
$frames: str-length($text);
$animations: _generate-animations($frames, 1.2s);
animation-name: _unzip($animations, 1);
animation-delay: _unzip($animations, 3);
animation-fill-mode: forwards;
content: "";
// We need to type each character in separate animations, see above
// comment.
@each $name, $character in $animations {
@keyframes #{$name} {
from {
content: str-slice($text, 0, $character);
}
to {
content: str-slice($text, 0, $character + 1);
}
}
}
}
/// Unzip a nested set of lists, taking the nth value of each sublist.
@function _unzip($lists, $i) {
$out: ();
$sep: comma;
@each $sublist in $lists {
$out: list.append($out, list.nth($sublist, $i), $sep);
}
@return $out;
}
/// Compute the sum of all numbers in a list.
@function _sum($list) {
$out: 0;
@each $val in $list {
$out: $out + $val;
}
@return $out;
}
/// Produce a list from a shorter list by repeating it up until size
/// $length.
@function _round-robin($base, $length) {
$out: ();
$sep: list.separator($out);
@for $i from 0 through $length {
$out: list.append($out, list.nth($base, $i % list.length($base) + 1));
}
@return $out;
}
/// Generate the actual animation values.
///
/// This generates a nested list as:
///
/// (keyframe-name, index, start time)
///
/// The duration of each frame is taken from the internal $delays in a
/// round robin fashion, to give some amount of human-like variance to
/// the duration of each frame.
///
/// Start time is set to the time at which the frame should start to
/// achieve the desired frame-by-frame duration.
@function _generate-animations($number, $total_duration) {
$id: d66fa0449c0b4d4ca287f8c96428af928b2987b4d88b72b7d60152d9a55d9f29;
$out: ();
$sep: list.separator($out);
// A set of "human-like" delays for each typed character. In
// practice, my typing seems to be about 20-70ms, but it looks a bit
// nicer to increase all typing by 20ms to make the effect more
// noticeable.
//
// Numbers generated once with a random number generator, rather
// than using `math.random()`, since they end up in CSS verbatim,
// and the build would be non-reproducible if we didn't do it this
// way. Using `math.random() wouldn't change this dynamically each
// time the page loads anyway, so we don't really lose anything by
// pre-generating these numbers.
$delays: 69ms, 83ms, 49ms, 48ms, 52ms, 59ms, 40ms, 71ms, 80ms, 67ms;
@for $animation from 0 through $number {
$out: list.append(
$out,
(
type-#{$id}-#{$animation},
$animation,
_sum(_round_robin($delays, $animation))
),
$sep
);
}
@return $out;
}