Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore d3.{mouse,touch,touches}; add d3.pointers. #253

Closed
wants to merge 1 commit into from

Conversation

mbostock
Copy link
Member

@mbostock mbostock commented Aug 1, 2020

Needs documentation (and tests, ideally).

@mbostock mbostock requested a review from Fil August 1, 2020 20:38
@mbostock
Copy link
Member Author

mbostock commented Aug 1, 2020

I explicitly did not bring back the implicit pulling out of event.changedTouches[0] in d3.mouse, since that still feels too surprising.

@Fil
Copy link
Member

Fil commented Aug 1, 2020

Re: changedTouches it's nice to be able to query the position on touchend / dbltap. This is useful for example for a dbltap to launch a zoom at the right place in d3-zoom, as we do in https://github.com/d3/d3-zoom/blob/523ccff340187a3e3c044eaa4d4a7391ea97272b/src/zoom.js#L350

@mbostock
Copy link
Member Author

mbostock commented Aug 1, 2020

Re: changedTouches it's nice to be able to query the position on touchend / dbltap.

Not sure what you’re saying. Are you suggesting we want to call d3.mouse here and have it pull out event.changedTouches[0]?

@Fil
Copy link
Member

Fil commented Aug 2, 2020

Yes, I'd prefer to know where an event happened with d3.mouse(event). With the current proposal, on mouseup and pointerup events, d3.mouse return [x, y], but on touchend it returns [NaN, NaN].

@mbostock
Copy link
Member Author

mbostock commented Aug 2, 2020

It’s doesn’t make sense to call d3.mouse on a touch event, though; that’s not a mouse event. If the spec authors had thought it appropriate to give a TouchEvent a “primary” pointer, they would have put event.clientX and event.clientY properties on the TouchEvent. They didn’t, so it feels inappropriate for us to magically pull out the first changed touch, and it’s almost certainly an indication of a broken touch implementation if the caller were to do so (since it ignores the other touches).

@Fil
Copy link
Member

Fil commented Aug 2, 2020

I understand this choice on principles.

My point is that in practice this makes the "one-finger touch" case more difficult than necessary when you don't need fancy multitouch (for example https://observablehq.com/@d3/delaunay-find reacts on click and mousemove, but could get the "touchmove" for free if this worked). Also, it doesn't help with multitouch.

However, a good solution might be to use pointer events instead (https://observablehq.com/d/d86d69071bef1f76):

  .on("pointermove", event => draw(delaunay.find(...d3.pointer(event))))
  .on("touchstart", event => event.preventDefault())
@mbostock
Copy link
Member Author

mbostock commented Aug 2, 2020

could get the "touchmove" for free if this worked

Well, you already get touchmove for free because of emulated mouse events, albeit the emulated mousemove doesn’t occur until after the touch gesture ends.

And if you want to handle touchmove events directly, it’s never strictly free because you still have to explicitly listen for them (i.e. replacing .on("mousemove", …) with .on("mousemove touchmove", …). And so you could just be explicit about what you want to happen:

  .on("mousemove", (event, context) => draw(context, delaunay.find(...d3.pointer(event))))
  .on("touchmove", (event, context) => draw(context, delaunay.find(...d3.pointer(event.touches[0]))))

I think the “ideal” behavior for this particular example would be to plot multiple points simultaneously if there are multiple touches. Something like:

  .on("mousemove", (event, context) => find(context, [d3.pointer(event)]))
  .on("touchmove", (event, context) => find(context, d3.pointers(event.touches)))

Obviously that’s trickier to do for the animation case, since you’d need to track concurrent animations, ideally for each touch independently. But my point is that you have a relatively easy way to just use a single touch, if that’s what you want. And I don’t think it’s a good idea for D3 to apply that logic for you automatically because it’s usually wrong.

@Fil
Copy link
Member

Fil commented Aug 2, 2020

That's a great start for a documentation!

Here's a different point of view, on d3.pointers, which might be less wrong than my previous argument on d3.mouse.

Let's say I've coded a "multitouch-first" draw function that expects (and reacts intelligently to) any number of pointers (mouse or touches). I really don't want to duplicate code, so I'm tempted to write:

selection.on("mousemove touchmove", event => draw(d3.pointers(event.touches || [event])))

or in a drag behavior:
drag.on("drag start", event => draw(d3.pointers(event.SourceEvent.touches || [event])))

Wouldn't it help if we could just call d3.pointers(event) everywhere?

@mbostock
Copy link
Member Author

mbostock commented Aug 2, 2020

Okay, I’m thinking about an alternative where d3.pointer does the sourceEvent traversal, and so does d3.pointers, but d3.pointers can also optionally take a TouchList directly, and if given a non-touch event, returns a single-element array. I think that would be reasonably straightforward and convenient. There’s the remaining question of whether we want a d3.touch method that pulls out the touch with a particular identifier, but I don’t expect that’s needed often so maybe it’s fine to remove. Also in some sense I think it’s better to not provide a non-backwards-compatible d3.mouse method because it’ll be more obvious that the code needs to be changed to upgrade.

@Fil
Copy link
Member

Fil commented Aug 3, 2020

Excellent! Yeah I don't think we need d3.touch(…identifier). At this level of detail one would work with the actual events (or with pointer events https://observablehq.com/@fil/pointer-events#trackPointer).

It will be much simpler to have to learn only d3.pointer (for single touch) and pointers (if multitouch is possible).

@mbostock mbostock mentioned this pull request Aug 3, 2020
@mbostock
Copy link
Member Author

mbostock commented Aug 3, 2020

Closing in favor of #255.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants