アニメーション
キーフレームも Rust の値です。要素がキーフレームを使うと、そのキーフレームのルールも要素と一緒にページに書かれます。
キーフレーム
Keyframes::new(name) でアニメーションを作り、at(percent, style) で段階を 1 つ追加します。Style::keyframes は要素の animation-name を設定し、@keyframes のルールもスタイルに含めます。そのため、要素を含むページやパッチには、そのルールも含まれます。キーフレームにはセレクタもメディアクエリも書けないので、キーフレームに書かれるのはスタイルのうち条件の付かない宣言だけです。
use hypersig::html::span;
use hypersig::style::value::{AnimationIterationCount, AnimationTimingFunction, ms, raw};
use hypersig::style::{Keyframes, Media, Style};
use hypersig::{Page, Signals};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
struct Upload {
busy: bool,
}
let spin = Keyframes::new("spin")
.at(0, Style::new().transform(raw("rotate(0deg)")))
.at(100, Style::new().transform(raw("rotate(360deg)")));
let spinner = span::<Upload>()
.attr("aria-hidden", "true")
.css(
Style::new()
.keyframes(&spin)
.animation_duration(ms(800))
.animation_timing_function(AnimationTimingFunction::Linear)
.animation_iteration_count(AnimationIterationCount::Infinite)
// Slower for a visitor who asks for reduced motion.
.on(Media::ReducedMotion, Style::new().animation_duration(ms(4000))),
)
.show(Upload::busy())
.text("◌");
let html = Page::new("Upload", Upload { busy: true }).child(spinner).render();
assert!(html.contains("@keyframes spin"));実行結果
spinner.rs
use hypersig::html::{div, span};
use hypersig::style::pattern::hstack;
use hypersig::style::value::{
AnimationIterationCount, AnimationTimingFunction, BorderStyle, ms, px, raw, rem,
};
use hypersig::style::{Keyframes, Media, Style};
use hypersig::{Page, Signals, if_else};
use hypersig_ui::{ButtonSize, ButtonVariant, button, token};
use serde::{Deserialize, Serialize};
#[derive(Signals, Serialize, Deserialize)]
pub struct Upload {
busy: bool,
}
pub fn page() -> Page<Upload> {
let busy = Upload::busy();
let spin = Keyframes::new("spin")
.at(0, Style::new().transform(raw("rotate(0deg)")))
.at(100, Style::new().transform(raw("rotate(360deg)")));
let ring = Style::new()
.width(rem(1.5))
.height(rem(1.5))
.border_width(px(3))
.border_style(BorderStyle::Solid)
.border_color(token::muted().var())
.border_top_color(token::primary().var())
.border_radius(px(999))
.keyframes(&spin)
.animation_duration(ms(800))
.animation_timing_function(AnimationTimingFunction::Linear)
.animation_iteration_count(AnimationIterationCount::Infinite)
.on(
Media::ReducedMotion,
Style::new().animation_duration(ms(4000)),
);
Page::new(
"アップロード",
Upload { busy: true },
)
.child(
div()
.css(hstack(rem(1)))
.child(span().attr("aria-hidden", "true").css(ring).show(busy))
.child(span().attr("role", "status").bind_text(if_else(
busy,
"アップロード中…",
"完了",
)))
.child(
button(ButtonVariant::Outline, ButtonSize::Sm)
.on_click(busy.toggle())
.bind_text(if_else(
busy,
"停止",
"開始",
)),
),
)
}動きを減らす設定
Media::ReducedMotion は、動きを減らすよう求める訪問者にだけスタイルを適用します。hypersig_ui のコンポーネントはその場合に色をトランジションなしで切り替えます。
同じ名前の 2 つの
Keyframes は、ブラウザにとっては 1 つのルールとして扱われ、スタイルシートで後にある方が使われます。名前が重複していないかは検査されません。