Mithril не содержит встроенного анимационного движка и не навязывает конкретный способ работы с анимациями. Архитектура фреймворка строится вокруг быстрого виртуального DOM и предсказуемых жизненных циклов компонентов, что позволяет реализовывать анимации через стандартные веб-технологии: CSS, JavaScript, Web Animations API и сторонние библиотеки. Ключевая особенность — точный контроль над моментами вставки, обновления и удаления DOM-узлов.
Основные точки интеграции анимаций:
oncreate, onupdate,
onremovekey для управления повторным использованием
DOM-узловНаиболее простой и производительный способ анимации — использование
CSS transition. Mithril не мешает этому подходу, так как не
манипулирует стилями напрямую.
const Fade = {
view() {
return m("div.fade", "Контент")
}
}
.fade {
opacity: 0;
transition: opacity 300ms ease;
}
.fade.active {
opacity: 1;
}
Активация анимации происходит через изменение класса. В Mithril это обычно делается через состояние компонента.
const Fade = {
visible: false,
view() {
return m("div.fade" + (this.visible ? ".active" : ""), "Контент")
}
}
Изменение состояния приводит к перерисовке, CSS самостоятельно запускает переход.
oncreate)Хук oncreate вызывается после добавления DOM-узла в
документ. Это идеальное место для инициации анимации появления.
const SlideIn = {
view() {
return m("div.panel", {
oncreate(vnode) {
vnode.dom.classList.add("enter")
requestAnimationFrame(() => {
vnode.dom.classList.add("enter-active")
})
}
}, "Панель")
}
}
.panel {
transform: translateX(-20px);
opacity: 0;
}
.panel.enter-active {
transform: translateX(0);
opacity: 1;
transition: transform 250ms ease, opacity 250ms ease;
}
Использование requestAnimationFrame гарантирует, что
браузер применит начальное состояние перед переходом к активному.
onupdate)Хук onupdate вызывается после обновления DOM-узла. Он
применяется для анимации переходов между состояниями.
const Resizable = {
view(vnode) {
return m("div.box", {
style: { width: vnode.attrs.width + "px" },
onupdate(vnode) {
vnode.dom.classList.add("resizing")
}
})
}
}
.box {
transition: width 300ms ease;
}
При каждом обновлении атрибута ширины браузер автоматически проигрывает анимацию.
onremove)По умолчанию Mithril мгновенно удаляет элементы из DOM. Для
анимированного удаления используется onremove, который
должен вернуть Promise.
const FadeOut = {
view() {
return m("div.item", {
onremove(vnode) {
return new Promise(resolve => {
vnode.dom.classList.add("leave")
vnode.dom.addEventListener("transitionend", resolve)
})
}
}, "Удаляемый элемент")
}
}
.item {
opacity: 1;
transition: opacity 200ms ease;
}
.item.leave {
opacity: 0;
}
Пока Promise не завершён, Mithril удерживает элемент в
DOM, позволяя корректно завершить анимацию.
keyMithril агрессивно переиспользует DOM-узлы для повышения
производительности. Для анимаций списков это может мешать, поэтому
используется атрибут key.
items.map(item =>
m("li", { key: item.id }, item.text)
)
Наличие key гарантирует:
При анимации сортировок и фильтраций key является
обязательным.
Для списков часто комбинируются oncreate и
onremove.
const ListItem = {
view(vnode) {
return m("li", {
oncreate(v) {
v.dom.classList.add("appear")
},
onremove(v) {
return new Promise(resolve => {
v.dom.classList.add("disappear")
v.dom.addEventListener("transitionend", resolve)
})
}
}, vnode.children)
}
}
li {
transition: transform 200ms ease, opacity 200ms ease;
}
li.appear {
transform: translateY(0);
opacity: 1;
}
li.disappear {
transform: translateY(-10px);
opacity: 0;
}
Такой подход позволяет реализовать плавные вставки и удаления без сторонних библиотек.
Для более сложных сценариев используется
element.animate.
const Bounce = {
view() {
return m("div.box", {
oncreate(vnode) {
vnode.dom.animate([
{ transform: "scale(0.9)" },
{ transform: "scale(1)" }
], {
duration: 300,
easing: "ease-out"
})
}
})
}
}
Преимущества:
AnimationMithril не конфликтует с GSAP, anime.js и подобными решениями. Подключение осуществляется через хуки.
oncreate(vnode) {
gsap.from(vnode.dom, { opacity: 0, y: 20, duration: 0.3 })
}
Критически важно:
key для стабильностиОсновные рекомендации:
transform и opacityheight и top при больших
DOM-деревьяхonupdate без проверки
измененийMithril выполняет перерисовки синхронно, поэтому сложные анимации должны быть изолированы от частых обновлений состояния.
В сложных приложениях анимации выносятся в:
Пример фабрики:
const Animated = animation => ({
view(vnode) {
return m(vnode.tag, {
...vnode.attrs,
oncreate(v) {
animation.enter(v.dom)
},
onremove(v) {
return animation.leave(v.dom)
}
}, vnode.children)
}
})
Такой подход сохраняет чистоту бизнес-логики и локализует анимационное поведение.
Анимации в Mithril строятся не вокруг магии, а вокруг точного знания жизненного цикла DOM. Фреймворк предоставляет минимальный, но достаточный набор хуков, позволяя использовать стандартные веб-инструменты максимально эффективно.