Подтвердить что ты не робот

Каковы хорошие абстракции для сложных анимаций?

Как вы относитесь к разработке и реализации сложных анимаций взаимодействия с пользовательским интерфейсом?

(Я не говорю о конкретных языках и библиотеках, таких как jQuery или UIKit, если только они не заставят вас задуматься об управлении взаимозависимыми анимациями, которые меня интересуют.)

Рассмотрим обманчиво "простую" задачу, такую ​​как проектирование и программирование главного экрана iOS.

iOS home screen

Однако величина скрытой сложности поражает.
Несколько вещей, которые я заметил об интерфейсе:

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

Я уверен, что здесь еще сложнее, я не заметил.

Непрерывная анимация против дискретных действий

В грубом обобщении для каждой пары (animation, user_action) в том же контексте интерфейса вам нужно решить что делать, если user_action происходит, когда animation уже запущен.

В большинстве случаев вы можете

  • Отменить анимацию;
  • Измените анимацию на ходу;
  • Игнорировать действие;
  • Остановите действие до завершения анимации.

Но тогда во время анимации может быть несколько действий, и вам нужно решить, какие действия следует отбрасывать, что делать в очереди, и выполнять ли все действия в очереди или только последние, когда анимация завершена.

Если что-то ставится в очередь, когда анимация заканчивается, а анимация изменена, вам нужно решить, все ли в порядке очереди, или нужно удалить.

Если это звучит слишком теоретически, рассмотрим пример в реальном мире: как вы работаете с пользователем, перетаскивая приложение вниз, ожидая начала перегруппировки, а затем сразу же перетаскиваете приложение вверх и освобождаете Это? Как вы гарантируете, что анимация будет гладкой и правдоподобной во всех возможных случаях?

Правильные инструменты для работы

Я не могу удержать даже половину возможных сценариев в голове. По мере увеличения выразительности UI число возможных состояний начинает насильственно нарушать правило 7 ± 2.

Поэтому мой вопрос таков:

Как укротить сложность при проектировании и внедрении анимаций?

Мне интересно как найти эффективные способы мышления о проблеме, так и способы ее решения.

В качестве примера события и наблюдатели оказались очень эффективной абстракцией для большинства пользовательских интерфейсов.
Но можете ли вы спроектировать и реализовать iOS-подобный экран drag-n-drop, полагающийся на события как главную абстракцию?

Насколько запутанный код должен быть для точного представления всех возможных состояний пользовательского интерфейса? Будет ли обработчик события добавлением другого обработчика события, когда некоторая логическая переменная верна функции, которая устанавливает его в значение false, если только еще один обработчик события не запустился раньше?

"Вы никогда не слышали о занятиях?" вы можете удивиться. Почему, у меня есть, но есть слишком много состояний, которые эти классы захотят разделить.

Подводя итог, я ищу язык-агностик (хотя, вероятно, язык или фреймворк) методы управления сложными взаимозависимыми, отменяемыми, анимациями последовательно или сразу, и , описывая, как они реагируют на действия пользователя.

(Все это касается того, что мне не нужно сама программировать анимацию, т.е. у меня есть доступ к фреймворку вроде jQuery или Core Animation, который может animate(styles, callback) для меня, и я могу cancel его. )

Структуры данных, шаблоны проектирования, DSL файлы хороши, если они помогают в решении проблемы.

4b9b3361

Ответ 1

В описанной проблеме есть неявное понятие состояния системы. Анимации с состоянием, и поэтому любая их композиция является такой же с состоянием, и, возможно, даже больше.

Один из способов думать о состояниях и действиях - это конечные автоматы.

Прочтите эту статью, объясняющую, как применить FSM для реализации настраиваемой подсказки в JavaScript:

enter image description here

Этот пример может показаться немного запутанным, но он иллюстрирует мысль: конечный автомат заставляет вас думать , какие состояния возможны, какие переходы между ними действительны, когда они должны запускаться и какой код должен выполняться, когда они являются.

Поскольку статья IBM запрещает использование ее примера кода, я рекомендую вам также прочитать статью Бена Наделя, в которой для реализации виджета раскрывающегося меню используется FSM.

Бен пишет:

Я видел, как конечные автоматы могут выполнять большие сложные задачи и разбивать их на более мелкие, гораздо более управляемые состояния. Я даже пытался применить такой подход к привязке и отмене привязки обработчиков событий в JavaScript. Но теперь, когда мне стало удобнее работать с конечными автоматами и, в частности, с переходами между состояниями, я хотел попробовать применить этот подход к виджету связного пользовательского интерфейса.

Вот немного урезанная версия его кода:

var inDefault = {
    description: "I am the state in which only the menu header appears.",
    setup: function() {
       dom.menu.mouseenter(inHover.gotoState);
    },    
    teardown: function() {
         dom.menu.unbind("mouseenter");
    }
};

var inHover = {
    description: "I am the state in which the user has moused-over the header of the menu, but the menu has now shown yet.",
    setup: function() {
        dom.menu.addClass("menuInHover");
        dom.menu.mouseleave(inDefault.gotoState);
        dom.header.click(
            function(event) {
                event.preventDefault();
                gotoState(inActive); 
            }
       );    
    },
    teardown: function() {
        dom.menu.removeClass("menuInHover");
        dom.menu.unbind("mouseleave");
        dom.header.unbind("click"); 
    }    
};

var inActive = {
     description: "I am the state in which the user has clicked on the menu and the menu items have been shown. At this point, menu items can be clicked for fun and profit.",

    setup: function() {
        dom.menu.addClass("menuInActive");
        dom.stage.mousedown(
            function(event) {
                var target = $(event.target);
                if (!target.closest("div.menu").length) {
                    gotoState(inDefault); 
                } 
            }
       );
       dom.header.click(
            function(event) {
                event.preventDefault();
                 gotoState(inHover);

            }
       );
       dom.items.delegate(
            "li.item",
            "click",
            function(event) {
                console.log(
                    "Clicked:",
                    $.trim($(this).text())
               );

            }
       );
    },    
    teardown: function() {
        dom.menu.removeClass("menuInActive"); 
        dom.stage.unbind("mousedown", inDefault.gotoState);
        dom.header.unbind("click"); 
        dom.items.undelegate("li.item", "click");
    }
};

Обратите внимание, что обработчики событий связаны при входе в состояние и не связаны при выходе из этого состояния.

Самое большое преимущество, которое дают ФСМ в решении этой проблемы, - они делают состояние явным.

Хотя каждая анимация может вносить вклад в состояние охватывающей системы, ваша система никогда не может находиться в двух состояниях одновременно или вообще не находиться в состоянии, а отладка становится почти тривиальной, потому что вы всегда видите, в каком состоянии находится система (или каждая подсистема), учитывая ваш государственный дизайн имеет смысл.

Кроме того, если вы заставляете вас проектировать состояния в явном виде, использование FSM исключает возможность того, что вы не думаете о конкретной комбинации состояний/действий. Не существует "неопределенного поведения", поскольку каждый переход является явным и является частью вашего проекта FSM.


Если вы читали это далеко, вас могут заинтересовать дополнительные анимации (другое введение). Теперь они используются по умолчанию в iOS 8 и защищаются Кевином Даути уже несколько лет.

Это другой подход, при котором, сохраняя состояние системы в состоянии, вы позволяете нескольким (даже противоположным) анимациям быть активными одновременно. Это может дать вам сумасшедшие результаты, но это интересная концепция.

Основная идея состоит в том, чтобы не определять анимацию как нечто, переходящее от абсолютного значения A к абсолютному значению B, и вместо этого определять анимации относительно их окончательного значения (каждая анимация идет от -Delta до 0). Это позволяет легко объединять несколько анимаций, суммируя их относительные значения в каждый момент времени, и избегать всплесков, вызванных сторнированием или отменой:

additive animations
(источник: ronnqvi.st)

Чтобы увидеть пример аддитивных анимаций, не зависящих от фреймворка, ознакомьтесь с модулем аддитивной анимации alexkuz (demo).


Если вы читали это далеко, вы должны быть действительно заинтересованы в анимации! В настоящее время я заинтригован подходом response-state-stream. Он предлагает выражать анимации как ленивые последовательности состояний. Это открывает множество возможностей, таких как выражение бесконечной анимации, постепенное добавление и удаление преобразований из анимации и т.д.

Если вы хотите прочитать одну статью об анимации, я предлагаю это Мысли об анимации Ченг Лу.