From efaa885a7b65fc68080bda0c84fbbad534eedb1b Mon Sep 17 00:00:00 2001 From: Geoff Schmidt Date: Fri, 10 Aug 2012 20:17:32 -0700 Subject: [PATCH] use click events rather than hit testing --- .../landmark-demo/client/landmark-demo.js | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/examples/landmark-demo/client/landmark-demo.js b/examples/landmark-demo/client/landmark-demo.js index f04d33cfb5..0b9618b724 100644 --- a/examples/landmark-demo/client/landmark-demo.js +++ b/examples/landmark-demo/client/landmark-demo.js @@ -148,24 +148,12 @@ Template.d3Demo.right = function () { return { group: "right" }; }; -var hitTest = function (x, y, selector) { - var circles = Circles.find(selector).fetch(); - for (var i = 0; i < circles.length; i++) { - var c = circles[i]; - var dist2 = (x - c.x) * (x - c.x) + (y - c.y) * (y - c.y); - if (dist2 < c.r*c.r) - return c; - } - return null; -}; - Template.circles.events = { - 'click svg': function (evt, template) { - var circle = hitTest(evt.offsetX / 200, evt.offsetY / 200, - {group: this.group}); - console.log("click " + (circle ? circle._id : "null")); + 'click circle': function (evt, template) { // XXX actually want to create a ReactiveVar on the template! - Session.set("selectedCircle:" + this.group, circle); + // (but how will it be preserved across migration?) + // (maybe template.get, template.set?? rather than form??) + Session.set("selectedCircle:" + this.group, evt.currentTarget.id); }, 'click .add': function () { Circles.insert({x: Meteor.random(), y: Meteor.random(), @@ -226,11 +214,53 @@ Template.circles.render = function () { d3.select(self.node).append("rect"); self.handle = autorun(function () { + // XXX In an ideal world, we'd pass a cursor to .data(), and as + // long as we were within an autorun, the "right thing" would + // happen, meaning that d3 would process only the changed + // elements. + // + // You could model this as a getChanges() method on a cursor, + // which reactively returns the changes since you last called it + // (as an object with added, removed, moved, changed sections.) + // Except you should be able to have N per cursor. + // + // Actually, you could make a function Meteor.getChanges(cursor) + // that returns a changes function that has the above + // properties. + // + // Then, we'd need to reach inside d3's matching logic to make + // it detect a Meteor cursor and call getChanges ... + // + // XXX no, you need one getchanges per cursor per autorun. hmm. + // maybe a factory that memoizes them somehow? but, per autorun? + // + // Maybe: + // var stream = ChangeStream(cursor); + // autorun(function () { d3.select(...).stream(stream) ... } + // Streams are the factory described above. returns ChangeSets + // + // XXX what this doesn't answer is, what if you depend on other + // reactive values, eg Session.get("color")? + // + // XXX why can't we just do this stuff declaratively with + // Handlebars and an element? what does that imply about + // missing animation support in Spark? + // + // XXX make Session be a ReactiveDict (ReactiveMap?) and put the + // ReactiveDiff impl in packages/deps/tools.js. Keep the + // low-level deps machinery as it is (maybe add invalidation + // sequencing.) Rename Meteor.deps.Context => + // InvalidationContext. + // + // XXX allow query selectors, sorts, to be lambdas? var circle = d3.select(self.node).selectAll("circle") .data(Circles.find({group: data.group}).fetch(), function (d) { return d._id; }); circle.enter().append("circle") + .attr("id", function (d) { + return d._id; + }) .attr("cx", function (d) { return d.x * 200; }) @@ -265,6 +295,7 @@ Template.circles.render = function () { .attr("r", 0) .remove(); + // XXX this doesn't animate as I'd hoped when you press Scram var selectionId = Session.get("selectedCircle:" + data.group); var s = selectionId && Circles.findOne(selectionId); var rect = d3.select(self.node).select("rect");