gpt4 book ai didi

design-patterns - 什么是复杂动画的好的抽象?

转载 作者:行者123 更新时间:2023-12-03 10:15:04 27 4
gpt4 key购买 nike

您如何设计和实现复杂的 UI 交互动画?

(我不是在谈论像 jQuery 或 UIKit 这样的特定语言和库,除非它们迫使你以特定的方式思考管理相互依赖的动画,我很感兴趣。)

考虑一个看似“简单”的任务,比如设计和编程 iOS 主屏幕。

iOS home screen

然而,隐藏复杂性的程度令人震惊 .
关于界面,我注意到了一些事情:

  • 当您几乎不触摸图标时,它的不透明度会发生变化,但大小的变化会延迟。
  • 如果您在两个其他应用程序之间拖动应用程序,则在所有应用程序重新排列以移动可用空间之前会有明显的延迟。因此,如果您只是不断地在屏幕上移动应用程序,那么在您安定下来之前什么也不会发生。
  • 重新排列逐行进行,首先是您悬停在上面的行,然后触发链中的下一行,即之前可用空间所在的行。
  • 如果你放下一个应用程序,它会放在现在空闲的空间,而不仅仅是你放下它的地方。
  • 如果您将一个应用悬停在另一个应用上,则会出现径向光,闪烁两次,然后才会创建一个组。
  • 如果该组是在可用空间的右侧创建的,然后被丢弃,则它会在丢弃时向左移动以占用可用空间。

  • 我确定有 更复杂的是我没有注意到 .

    连续动画与离散 Action

    粗略概括,对于每对 (animation, user_action)在相同的界面上下文中,您需要决定 如果user_action怎么办发生时 animation已经在运行 .

    在大多数情况下,您可以
  • 取消动画;
  • 随时随地更改动画;
  • 忽略 Action ;
  • 将 Action 排队到动画完成时。

  • 但是在动画过程中可能会有多个 Action ,您必须决定放弃哪些 Action ,将哪些 Action 排队,以及在动画结束时是执行所有排队的 Action 还是只执行最后一个 Action 。

    如果动画完成时某些内容已排队,并且动画已更改,则您需要确定排队的操作是否仍然有意义,还是需要删除。

    如果这听起来太理论化, 考虑一个真实世界的例子 :你如何处理用户向下拖动应用程序,等待重新排列开始,然后立即向上拖动应用程序并释放它?您如何确保动画在所有可能的情况下都流畅且可信?

    适合工作的工具

    我发现自己甚至无法记住一半的可能情况。随着 UI 表现力的增加, 可能状态的数量开始严重违反 7±2 规则 .

    因此,我的问题如下:

    How do you tame the complexity in designing and implementing animations?



    我对找到思考问题的有效方法以及解决问题的方法都很感兴趣。

    例如, 事实证明,事件和观察者是大多数 UI 的非常有效的抽象 .
    但是你能设计和实现一个以事件为主要抽象的类似 iOS 的拖放屏幕吗?

    代码必须有多复杂才能准确表示 UI 的所有可能状态?当某个 bool 变量对于将其设置为 false 的函数为 true 时,它​​会是一个事件处理程序添加另一个事件处理程序,除非在它之前运行了另一个事件处理程序?

    “你没听说过课吗?”你可能想知道。为什么,我有,但是这些类想要共享的状态太多了。

    综上所述,我在找 语言不可知 (虽然可能受语言或框架启发) 管理复杂的相互依赖、可取消的动画的技术 依次或同时发生,以及 描述他们如何对用户操作使用react .

    (所有这些都考虑到我不必自己编写动画——也就是说,我确实可以访问像 jQuery 或 Core Animation 这样的框架,它们可以 animate(styles, callback) 对我来说是这样,我可以 cancel。)

    如果数据结构、设计模式、DSL 能够帮助解决问题,那么它们都很好。

    最佳答案

    在所描述的问题中,存在系统状态的隐含概念。动画是有状态的,因此它们的任何组合都是有状态的,甚至可以说更是如此。

    考虑状态和 Action 的一种方式是 Finite-state machines .

    阅读 this article解释如何应用 FSM 在 JavaScript 中实现自定义工具提示:

    enter image description here

    这个例子可能看起来有点复杂,但确实说明了这一点:有限状态机迫使你思考 什么状态是可能的,它们之间的哪些转换是有效的,它们应该何时被触发以及一旦它们被触发应该执行什么代码 .

    因为 IBM 的文章禁止使用其 code sample , 我鼓励你也阅读 an article by Ben Nadel使用 FSM 实现下拉菜单小部件。

    本写道:

    I've seen the way that Finite State Machines can take a large, complex tasks and break it down into smaller, much more manageable states. I've even tried to apply this kind of mentality to binding and unbinding event handlers in JavaScript. But, now that I am getting more comfortable with state machines and, in particular, state transitions, I wanted to try and apply this mindset to an cohesive user interface (UI) widget.



    这是他的代码的一个稍微精简的版本:

    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");
    }
    };

    请注意 事件处理程序在进入状态时绑定(bind),离开此状态时未绑定(bind)。

    FSM 解决这个问题的最大优势是它们 使状态显式 .

    虽然每个动画都可能对包含系统的状态有所贡献,但您的系统永远不可能同时处于两种状态或根本没有状态,调试变得几乎微不足道,因为您总是可以看到系统(或每个子系统)处于什么状态,鉴于您的状态设计是有道理的。

    此外,通过强制您明确设计状态,使用 FSM 排除了您不考虑状态/操作的特定组合的可能性。没有“未定义行为”,因为每个转换都是明确的,并且是 FSM 设计的一部分。

    如果您已经读到这里,您可能会对 Additive Animations 感兴趣( another intro )。它们现在是 iOS 8 中的默认设置,并已被 Kevin Doughty 提倡。几年了。

    这是一种不同的方法,在保持系统状态的同时,您允许多个(甚至相反的)动画同时处于事件状态。这可以给你 crazy results但这是一个有趣的概念。

    主要思想是避免将动画定义为从绝对值 A 到绝对值 B 的东西,而是将动画定义为相对于它们的最终值(每个动画从 -Delta 到 0)。这允许您通过将每个时间点的相对值相加来无缝组合多个动画,并避免由逆转或取消引起的尖峰:

    additive animations
    (来源: ronnqvi.st)

    有关添加动画的准系统框架不可知示例,请查看 alexkuz's additive-animation模块 ( demo )。

    如果你读到这里,你一定对动画很感兴趣!目前,我对 react-state-stream 很感兴趣方法。它建议将动画表示为惰性状态序列。这开辟了许多可能性,例如表达无限动画,逐渐添加和删除动画的变换等。

    如果你想阅读一篇关于动画的文章,我建议你是 Thoughts on Animation 程楼。

    关于design-patterns - 什么是复杂动画的好的抽象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10853211/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com