问题 Meteor - 使用订阅数据自动更新画布?


我可能会遗漏一些东西,但似乎Meteor的“魔力”围绕着将数据绑定到DOM元素,以及通过把手更新文本和HTML片段: http://docs.meteor.com/#reactivity

这很棒, 然而,当试图编写一个在<canvas>元素中显示实时数据的流星应用程序时,我无法找出在实时数据更改时更新我的​​画布的“流星方式”,因为画布是通过JS代码填充的,如:

var g = canvas.getContext('2d')
g.fillRect(x, y, w, h)

而不是HTML模板中的数据支持文本。

我试图使用来自Meteor.Collection的数据在画布上绘图。

我唯一的想法是在由车把变量填充的脚本标签中将画布绘制JS代码嵌入到HTML模板中,但这似乎是错误的,因为meteor的事件和数据绑定代码已经是客户端JS。

有没有办法监听实时数据更改,这会触发通过JS而不是HTML元素/文本在画布上绘图?

如果我能以某种方式澄清问题,请告诉我

更新: Tom的回答让我注意到了Meteor.deps,它看起来允许在被动上下文中执行任意代码: http://docs.meteor.com/#on_invalidate

如果有效,我会尝试这个并在这里更新。


1877
2018-05-29 06:53


起源

我不确定这是否是“嵌入式...在脚本标签中”的含义。目前我正在做一些事情,比如将{{drawCanvas}}放在包含我的画布的模板中,然后在名为“drawCanvas”的Template.canvas.helper的函数内部执行所有绘图,此函数不返回任何内容,因此不会渲染任何内容,但它被执行所以它将更新画布。这是你已经尝试过的吗? Template.name.helper会自动被动,因此只要它依赖于ReactiveDict或某些模型,它会随着它的变化而相应地更新 - Evan Pu
我评论的意思是动态生成JS代码作为模板中的字符串(这显然很奇怪)。我的答案似乎是Meteor的“正确”方式 - 7zark7
这确实荒谬!但我的方法看起来不错吗?这个想法只是使用一个不起作用的不可见的{{drawCanvas}},每当一些被动数据发生变化时重新运行(从而重新绘制画布) - Evan Pu
这听起来像是一个聪明的解决方案!可能希望比较方法之间的性能并报告。 - 7zark7


答案:


也许您的问题的答案是使用 Collection.observe (http://docs.meteor.com/#observe)并在各种回调中触发相关的重绘代码。

例如,类似于:

Rectangles.observe({
  added: function(rect) {
    var g = canvas.getContext('2d');
    g.fillRect(rect.x, rect.y, rect.w, rect.h);
  },
  // etc
})

7
2018-05-29 07:12



谢谢队友,没有看到。在我的特定情况下的一个问题是我需要重绘整个画布(例如删除一个矩形需要重新绘制它下面/上面的矩形),这意味着我需要整个查询结果,而不仅仅是添加/删除/更改。 - 7zark7
当然。我想你拥有的是正确的方法。我希望Meteor有一种更简单的方法来设置上下文+处理失效的方式。 - Tom Coleman


这有效:

var Shapes = new Meteor.Collection('shapes')

if (Meteor.is_client) {
  // Function that redraws the entire canvas from shapes in Meteor.Collection
  function drawShapes() {
    var shapes = Shapes.find({})
    shapes.forEach(function(shape) {
      // draw each on canvas
    })
  }

  var startUpdateListener = function() {
    // Function called each time 'Shapes' is updated.
    var redrawCanvas = function() {
      var context = new Meteor.deps.Context()
      context.on_invalidate(redrawCanvas) // Ensures this is recalled for each update
      context.run(function() {
        drawShapes()
      })
    }
    redrawCanvas()
  }

  Meteor.startup(function() {
    startUpdateListener()
  })
}

5
2018-05-29 17:10



这确实有效 - 谢谢!虽然我个人想要的只是在启动时进行完全重绘 - 然后只在更新/添加/删除时进行增量重绘。你是否正确的方式只在启动/渲染时做这种事情,而不是在每次更新后?如果你在template.rendered中放置查询它似乎不起作用。 - semateos
我完全重绘的原因是Canvas不支持删除/更新单个形状。 AFAIK,如果形状重叠等,你必须重绘整个画布。 - 7zark7
@semateos您可以通过使用像Pixi.js这样模仿Flash显示列表的库来实现这种行为。 - Wilson Silva


我在更新画布时遇到了一些麻烦,所以我创建了这个简单的游戏演示: https://github.com/randompast/Meteor-SimpleGame


1
2017-11-30 16:00