Обзор того, как я использовал макросы для создания анимации в CSS с юлианскими типами.

Введение

Те из вас, кто следит за моими текстами и кодом (спасибо :)), скорее всего, знают, что я работаю над фреймворком для веб-разработки для Джулии под названием Toolips.jl. Причина, по которой я создаю такую ​​вещь, заключается в том, что я верю в Джулию как в потенциальное смягчение или решение языковых проблем, существующих в веб-разработке. Вся методология, лежащая в основе модуля, заключается в том, что все можно сделать с помощью Julia, независимо от того, находится ли он в Javascript, HTML или в другом месте. Идея состоит в том, чтобы создать первый интерфейсный веб-фреймворк Джулии. На самом деле вы можете быть очень удивлены, насколько далеко продвинулся этот проект. Если вы хотите взглянуть на модуль самостоятельно, вы можете взглянуть на него здесь:



Следует отметить, что вы, вероятно, захотите взглянуть на Unstable, там ТОННЫ невыпущенных изменений, некоторые из которых очень опасны. Модуль разбит на несколько компонентов.

  • Интерфейс высокого уровня
  • Основной сервер
  • Расширения сервера
  • Servables

Сегодня мы будем работать как над Servables, так и над высокоуровневым интерфейсом, включив анимацию в компоненты стиля в Toolips. Если вы хотите просмотреть этот выпуск на Github, вот ссылка:



Кроме того, обзор всех проблем покажет, насколько мы близки к версии 1.0 Toolips.jl. Излишне говорить, что это супер захватывающе. В любом случае, давайте приступим к созданию этого компонента, так как у меня есть несколько довольно интересных идей, когда дело доходит до его анимации!

Установка в Toolips.jl

Анимацию в Toolips можно сделать одним из двух способов. Во-первых, мы можем применить анимацию к обслуживаемому стилю, чтобы дать анимацию всем компонентам с этим стилем. Второй способ очень похож, за исключением того, что вместо создания класса мы применяем анимацию непосредственно к обслуживаемому объекту, изменяя его свойства. Оба варианта, безусловно, подходят, и, честно говоря, было бы неплохо использовать оба варианта. Давайте взглянем на код, в который это нужно вписать и с которым работать, начиная с конструктора Component.

mutable struct Component <: Servable
    name::String
    f::Function
    properties::Dict

Когда компонент создается, он обычно создается либо с другими компонентами, либо с методом, который напрямую вызывает этот конструктор. Словарь свойств — это действительно единственное, что здесь следует упомянуть, это важно, потому что свойства определяют результат обслуживаемого драматическим образом. Обычно они редактируются с помощью функционального интерфейса. Далее давайте взглянем на конструктор Style, который на самом деле может принимать анимацию в качестве аргумента.

mutable struct Style <: StyleComponent
    name::String
    f::Function
    rules::Dict
    function Style(name::String; animation::Animation = nothing)
            ....
    end
end

Подобно полю свойств типа Dict, конструктор Style содержит словарь правил, влияющих на то, что делает стиль. Одно из этих свойств применит анимацию к стилю, и мы сможем сделать это с помощью методов. Однако сначала нам нужно создать конструктор анимации и придумать способ удобной передачи наших анимаций через него. Вот что я придумал, в котором хранятся ключевые кадры, а также другие данные анимации.

mutable struct Animation
    name::String
    keyframes::Dict
    f::Function
    delay::Float64
    length::Float64
    function Animation(name::String = "animation"; delay::Float64 = 0.0,
        length::Float64 = 5.2)
        f(c) = begin
  
        end
        keyframes::Dict = Dict()
        new(name, keyframes, f, delay, length)
    end
end

Единственное, чего нам сейчас серьезно не хватает, так это функции f() и какого-то способа добавления ключевых кадров. Вот синтаксис, который нам понадобится для объединения, используя мощь строк.

CSS

@keyframes slidein {
  from {
  opacity: 0%;
    transform: translateY(100%);
  }
  to {
  opacity: 100%;
    transform: translateY(0%);
  }
}

Во-первых, все начинается с чего-то похожего на макрос Julia, так что может иметь смысл его использовать. Во-вторых, за позициями следуют свойства и изменения этих свойств. Вот макрос, который я придумал, который принимает один из этих ключей ключевого кадра, за которым следует свойство и соответствующее ему значение.

macro keyframe!(anim::Symbol, keyframes::Any ...)
    anim::Animation = eval(anim)
    kf = [string(frame) for frame in keyframes]
    keyframe!(anim, kf)
end

Что я настоятельно рекомендую делать при работе с eval() в подобных сценариях, так это аннотировать тип. Таким образом, если то, что мы оцениваем, окажется любым другим типом, кроме этого, оно выдаст ошибку еще до того, как оно будет помещено в стек Джулии. Я подумал, что было бы здорово иметь возможность добавлять свойства немного проще, поэтому push! было отправлено:

import Base: push!
push!(anim::Animation, p::Pair) = push!(anim.keyframes, [p[1]] => p[2])

Наконец, я написал метод, связывающий этот ключевой кадр! вызывает в качестве возврата:

function keyframe!(anim::Animation, frames::Vector{String})
    prop = string(frames[2]) * ": " 
    value = string(frames[3]) * "; "
    if string(frames[1]) in keys(anim.keyframes)
        anim.keyframes[frames[1]] = anim.keyframes[frames[1]] * "$prop $value"
    else
        push!(anim.keyframes, frames[1] => "$prop $value")
    end
end

Все это позволяет нам сначала: создать анимацию:

anim = Animation("hello")

затем мы можем анимировать его, используя @keyframe и невероятно простой синтаксис.

@keyframe! anim from height "50px"
anim
Animation("hello", Dict{Any, Any}("from" => "height:  50px; "), var"#f#34"{String}("hello", Core.Box(Dict{Any, Any}("from" => "height:  50px; "))), 0.0, 5.2)

Мы также можем добавить дополнительные свойства к любому заданному ключевому кадру, например:

@keyframe! anim from width "50px"

Последним компонентом является функция f, которая оборачивает все это в стиль CSS.

mutable struct Animation
    name::String
    keyframes::Dict
    f::Function
    delay::Float64
    length::Float64
    function Animation(name::String = "animation"; delay::Float64 = 0.0,
        length::Float64 = 5.2)
        f(c) = begin
            s::String = "<style> @keyframes $name {"
            for anim in keys(keyframes)
                vals = keyframes[anim]
                s = s * "$anim {" * vals * "}"
            end
            s * "}</style>"
        end
        keyframes::Dict = Dict()
        new(name, keyframes, f, delay, length)
    end
end

Затем, когда это будет вызвано сервером Toolips, мы получим следующее!

anim.f("h")

"<style> hello {from {height:  50px; width:  50px; }}</style>"

Довольно круто, правда?

Воссоздание анимации

В качестве последнего трюка я создам анимацию, а затем попытаюсь использовать ее в таблице HTML. Первым шагом является вызов конструктора анимации:

fadein = Animation("fadein")

Теперь добавим в ключевые кадры от и до.

@keyframe! fadein from opacity "0%"
@keyframe! fadein to opacity "100%"

Наконец, для последнего шага мы вызываем f. Здесь я собираюсь вызвать это с помощью «», обычно с Toolips.jl все это будет автоматически и будет сделано сервером с Connection в качестве аргумента. Тем не менее, я хотел поместить это в блокнот, чтобы другие люди могли видеть отображаемую анимацию, если захотят, поэтому, если вы хотите увидеть этот блокнот, вы можете здесь:



В любом случае, давайте вызовем Animation.f():

s = fadein.f("")

Затем я сделаю заголовок, чтобы не импортировать всплывающие подсказки в этот блокнот и тому подобное, я просто напишу его как HTML и установлю стиль анимации.

htm = "<h1 style='animation: fadein 10s;'>Hello there, how are you?</h1>"

Опять же, это то, что обычно обрабатывается сервером и Servables в целом. Обычно свойство «класс» устанавливается после того, как мы применяем стиль в Toolips. Наконец, мы используем display(), чтобы увидеть наше новое творение!

display("text/html", s * htm)

Заключение

Большое спасибо за чтение. Я надеюсь, что эта статья успешно достигла нескольких целей. Во-первых, объяснить сложность и зависимость от типа к типу в этом программном обеспечении простыми словами. Другая цель состояла в том, чтобы продемонстрировать различные методы, которые можно использовать для решения некоторых подобных проблем. Наконец, я также просто хотел дать обновленную информацию о Toolips.jl. Toolips.jl находится на пороге настоящего, ломающего и очень красивого перехода на главную страницу с новыми сервисами, серверными расширениями, стилями и составными компонентами. Как только эта версия 0.1 будет готова, на самом деле появится больше вещей Toolips в виде расширений, что очень интересно! В любом случае, у меня много отличных планов, когда дело доходит до создания веб-сайтов с помощью этого фреймворка, и спасибо, что следите за моим прогрессом! Я взволнован, потому что я действительно думаю, что фреймворк невероятно крут.