【问题标题】:How do I get Elm animator to animate SVG elements using CSS animations?如何让 Elm animator 使用 CSS 动画为 SVG 元素设置动画?
【发布时间】:2021-06-28 19:51:34
【问题描述】:

我正在尝试在 Elm 中制作 CSS 动画,但我无法让它工作!

Elm 有 几个 动画包。我尝试使用的是mdgriffith/elm-animator。可悲的是,像许多 Elm 包一样,它的文档很少。它似乎提供了两种渲染方式:一种是通过运行 Elm 事件循环来更新每一帧的 DOM,另一种是使用 CSS 动画 [我什至没有意识到它存在]。我正在尝试做后者。

我已尝试将代码缩减为一个最小示例:

module Main exposing (main)

import Time
import Platform.Cmd
import Browser
import Html
import Html.Attributes
import Html.Events
import Color
import Svg
import Svg.Attributes
import Animator
import Animator.Css

main =
  Browser.document
  {
    init          = \        () -> ({fill = Animator.init <| Color.rgb 1 0 0}, Platform.Cmd.none),
    subscriptions = \     state -> Animator.toSubscription Tick state animator,
    view          = \     state -> {title = "Animation test", body = view state},
    update        = \ msg state -> (update msg state, Platform.Cmd.none)
  }

type alias State = { fill : Animator.Timeline Color.Color }

animator : Animator.Animator State
animator =
  Animator.animator
    |> Animator.Css.watching (\ state -> state.fill) (\ c state -> { state | fill = c })

type Msg = Tick Time.Posix | DoSomething

update : Msg -> State -> State
update msg state0 =
  case msg of
    Tick      t -> Animator.update t animator state0
    DoSomething -> { state0 | fill = Animator.go Animator.slowly (Color.rgb 1 1 0) state0.fill }

view : State -> List (Html.Html Msg)
view state0 =
  [
    Html.button [Html.Events.onClick DoSomething] [Html.text "Do something"],
    Svg.svg
      [
        Svg.Attributes.width  "100px",
        Svg.Attributes.height "100px"
      ]
      [
        Animator.Css.node "circle"
          state0.fill
          [
            Animator.Css.color
              "fill"
              (\ x -> x)
          ]
          [
            Svg.Attributes.cx "50",
            Svg.Attributes.cy "50",
            Svg.Attributes.r  "50"
          ]
          []
      ]
  ]

我很欣赏它仍然很大,但我不知道如何在不混淆它的作用的情况下轻松使它更短。

当我运行它时,SVG 无法呈现。当我点击按钮时,什么也没有发生。

这是 非常奇怪 部分:如果我打开 Firefox Inspector 窗口,我可以看到 SVG &lt;circle&gt; 元素。如果我编辑它的各种属性,什么都不会发生。但是,如果我右键单击&lt;circle&gt; 并选择“编辑为 HTML...”,然后在元素文本中添加一个空格并单击它,突然出现圆圈!此外,如果我再次右键单击该元素,现在它显示为“编辑为 SVG...”,这很可疑。

更有趣的是:如果我加载页面,单击按钮,然后然后执行上述技巧,彩色动画就会运行!

请注意,如果我编辑 HTML 并且不做任何更改,它就不起作用。我必须对文本进行一些微不足道的更改(否则我猜浏览器认为它不需要重新处理?)

所以确信这最终会成为一个奇怪的 Firefox 错误...但后来我在 Chrome 和 Edge 中尝试了它,它完全一样!

有没有人有模糊的线索为什么这不起作用?我对 Elm 代码做错了吗? (我真的非常想弄清楚这个库是如何工作的;我基本上只是在猜测这些类型是如何组合在一起的。所以也许我做了一些愚蠢的事情。)

【问题讨论】:

    标签: css svg elm


    【解决方案1】:

    这是由于namespaces 发生了一件奇怪的事情。在 HTML 文档中(即在任何网页上),默认命名空间是 HTML 命名空间,如果您想以其他格式呈现嵌入文档,则需要确保在正确的命名空间中创建节点。

    现在,当您将 HTML 编写为字符串并且浏览器从文本中解析它时,它会自动为您执行此操作:

    <div> <!-- HTML Namespace -->
      <svg> 
        <circle /> <!-- SVG Namespace -->
      </svg>
      <math>
        <mrow></mrow> <!-- MathML namespace 
                      (not really relevant, just pointing out different NS) -->
      </math>
    </div>
    

    但是,当您动态构建节点时(即从 JS 或 Elm),您需要显式请求命名空间。也就是说,在 JS 中,您将使用 Document.createElementNS() 而不仅仅是 Document.createElement()。如果你look at the source of the elm/svg package 你会看到它是这样做的:

    node : String -> List (Attribute msg) -> List (Svg msg) -> Svg msg
    node =
      VirtualDom.nodeNS "http://www.w3.org/2000/svg"
    

    如果您不这样做,浏览器就会知道您要创建一个名为circle 的新HTML 元素,而浏览器并不知道该元素。如果浏览器不知道某个元素,它基本上只会将其视为&lt;div&gt;

    现在如果您查看the source of the elm-animator package,您会注意到它要求Html.node,所以没有命名空间!


    至于如何使它与 elm-animator 一起工作,我怀疑你不能。但也许其他人可以提供一些解决方案......


    最后,您可能要考虑 SMIL 风格的动画,它往往可以在不需要任何外部包的情况下完成工作。

    【讨论】:

    • 感谢您花时间解决这个问题。如果 Inspector 真的会显示命名空间,我早就可以自己解决这个问题。哦,好吧!
    • 看看github.com/mdgriffith/elm-animator/blob/1.1.1/notes/roadmap.md,有一个括号评论是关于“你已经可以通过 Animator.move 为 Float 值设置动画,然后将其格式化为适当的属性,从而使用 Animator 为 Svg 设置动画。”我想知道这应该是什么意思? (遗憾的是,没有示例。Animator.move 没有记录。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    • 2013-01-09
    • 2015-03-31
    • 2022-01-05
    • 2014-10-13
    • 2015-03-27
    相关资源
    最近更新 更多