榆树 + Masonry.js

Elm + Masonry.js

我正在尝试通过端口获取与 Masonry.js 集成的 Elm 应用程序,但我在尝试弄清楚如何获取信号 Html 以触发告诉 Html 的端口时遇到了麻烦=20=] 重绘视图。

我正在使用 StartApp,但不确定如何从更新调用中获取视图已完成重新呈现的信号。

可以更好地与 Elm 一起工作的替代库或完全 Elm 解决方案也将受到赞赏。

关于我试图解决的整体问题的更多细节: 我有一系列图像,我想以砖石格式 (http://masonry.desandro.com/). They're represented by a list of objects in Elm which are converted to a list of divs in the view (with background-image set appropriately), but the images are of different sizes, hence the desire to tile them nicely. I'm using StartApp (http://package.elm-lang.org/packages/evancz/start-app/2.0.2/) 平铺以抽象 html 的实际渲染。

您可以在 Elm 中使用 ports 与 javascript 通信,以便双向发布和订阅事件。让我们构建一个示例,其中图像列表显示为砖石布局,单击图像将删除它并触发砖石布局剩余图像。

由于 Elm 应用程序将向 javascript 发送多种类型的事件,我们可以创建一个发送字符串的端口,然后 javascript 可以对其进行操作。这些字符串将是我们可以在 javascript 中解释的命令,以便告诉 masonry 做某些事情,比如 "initialize""imageRemoved"。此端口还需要一个邮箱,我们可以从 Elm 内部向其发送消息。

masonryMailbox : Signal.Mailbox String
masonryMailbox =
  Signal.mailbox ""

port masonryCommands : Signal String
port masonryCommands =
  masonryMailbox.signal

由于您使用的是 StartApp,您可以在 update 函数中 return Effects,因此让我们创建一个函数,将邮件从StartApp 初始值设定项和 update 函数内部。

sendMasonryCommand : String -> Effects.Effects Action
sendMasonryCommand cmd =
  let
    task =
      Signal.send masonryMailbox.address cmd
        `Task.andThen` \_ -> Task.succeed NoOp
  in
    Effects.task task

您可以在 StartApp 初始化函数期间发送 "initialize" 命令,如下所示:

init =
  (initialModel, sendMasonryCommand "initialize")

update函数中,如果我们有一个RemoveImage String动作,我们可以向javascript发送一个"imageRemove"命令:

update action model =
  case action of
    NoOp ->
      (model, Effects.none)
    RemoveImage url ->
      let model' =
        { model
        | images = List.filter ((/=) url) model.images
        , message = "Removing image " ++ url
        }
      in (model', sendMasonryCommand "imageRemoved")

现在我们需要连接 javascript 端来监听这些事件。如果javascript得到命令"initialize",那么我们就可以接线砌筑了。如果我们得到 "imageRemoved" 命令,我们可以告诉 masonry 再次触发布局命令。

var app = Elm.fullscreen(Elm.Main);
app.ports.masonryCommands.subscribe(function(cmd) {
  var $grid = $('.grid')

  if (cmd === "initialize") {
    $grid.masonry({
      itemSelector: '.grid-item',
      percentPosition: true,
      columnWidth: '.grid-sizer'
    });

    $grid.imagesLoaded().progress( function() {
      $grid.masonry();
    });  
  } else if (cmd === "imageRemoved") {
    $grid.masonry();
  }
});

我们还可以连接端口以将事件发送回 Elm。让我们在每次砌体完成渲染时向 Elm 发送一条消息来添加示例。首先,我们将创建一个名为 setMessage.

的端口
port setMessage : Signal String

因为这是 javascript 将发布 的端口,我们只在 Elm 中定义函数签名,而不是函数本身。相反,当我们调用 Elm.fullscreen() 时,我们必须从 javascript 端给信号一个初始值。 javascript 更改为:

var app = Elm.fullscreen(Elm.Main, { setMessage: "" });

现在,在处理 "initialize" 命令的 javascript 块中,您可以连接 masonry 的 layoutComplete 函数以将消息发送到这个新端口。

$grid.on("layoutComplete", function() {
  app.ports.setMessage.send("Masonry layout complete!");
});

为了在 Elm 中使用来自 setMessage 端口的这些消息,您需要一个 SetMessage String 操作,并且您需要将信号映射到 StartApp inputs列表。您的 StartApp 初始化代码如下所示:

app =
  StartApp.start
    { init = init
    , view = view
    , update = update
    , inputs = [ Signal.map SetMessage setMessage ]
    }

我已经在一些要点中提供了所有这些的完整工作示例。这是一个gist containing the Elm code and here is a gist containing the html and javascript.