Permalink
Browse files

Merge pull request #565 from koenbok/feature/scroll-tap-fixes

Feature/scroll tap fixes
  • Loading branch information...
eelco committed Feb 20, 2018
2 parents c110c3b + 761efca commit 8616043438ad4be48280b2a4669ed4edf1ec5379
@@ -97,10 +97,10 @@ class exports.RangeSliderComponent extends Layer
knob.on("change:frame", @_knobDidMove)
knob.on("change:frame", @_updateFrame)

@sliderOverlay.on(Events.TapStart, @_touchStart)
@sliderOverlay.on(Events.TapEnd, @_touchEnd)
@sliderOverlay.on(Events.TapStart, @_tapStart)
@sliderOverlay.on(Events.TapEnd, @_tapEnd)

_touchStart: (event) =>
_tapStart: (event) =>
event.preventDefault()

if @width > @height
@@ -110,12 +110,12 @@ class exports.RangeSliderComponent extends Layer

if clickedValue > @maxValue
@maxValue = clickedValue
@maxKnob.draggable._touchStart(event)
@maxKnob.draggable._panStart(event)
@emit(Events.SliderMaxValueChange, @maxValue)

if clickedValue < @minValue
@minValue = clickedValue
@minKnob.draggable._touchStart(event)
@minKnob.draggable._panStart(event)
@emit(Events.SliderMinValueChange, @minValue)

else
@@ -125,17 +125,17 @@ class exports.RangeSliderComponent extends Layer

if clickedValue > @maxValue
@maxValue = clickedValue
@maxKnob.draggable._touchStart(event)
@maxKnob.draggable._panStart(event)
@emit(Events.SliderMaxValueChange, @maxValue)

if clickedValue < @minValue
@minValue = clickedValue
@minKnob.draggable._touchStart(event)
@minKnob.draggable._panStart(event)
@emit(Events.SliderMinValueChange, @minValue)

@_updateValue()

_touchEnd: (event) =>
_tapEnd: (event) =>
@_updateValue()

_styleKnob: (knob) =>
@@ -87,10 +87,10 @@ class exports.SliderComponent extends Layer
@knob.on("change:frame", @_updateFill)
@knob.on("change:frame", @_knobDidMove)

@sliderOverlay.on(Events.TapStart, @_touchStart)
@sliderOverlay.on(Events.TapEnd, @_touchEnd)
@sliderOverlay.on(Events.TapStart, @_tapStart)
@sliderOverlay.on(Events.TapEnd, @_tapEnd)

_touchStart: (event) =>
_tapStart: (event) =>
event.preventDefault()

if @width > @height
@@ -102,10 +102,10 @@ class exports.SliderComponent extends Layer
scaleY = @canvasScaleY()
@value = @valueForPoint(touchY / scaleY - @screenFrame.y)

@knob.draggable._touchStart(event)
@knob.draggable._panStart(event)
@_updateValue()

_touchEnd: (event) =>
_tapEnd: (event) =>
@_updateValue()

_updateFill: =>
@@ -54,6 +54,7 @@ class exports.GestureInputRecognizer
started: {}
events: []
eventCount: 0
cancelTap: false

event = @_getGestureEvent(event)

@@ -71,7 +72,6 @@ class exports.GestureInputRecognizer
@_process(@_getGestureEvent(event))

touchend: (event) =>

# Only fire if there are no fingers left on the screen

if event.touches?
@@ -88,24 +88,12 @@ class exports.GestureInputRecognizer

event = @_getGestureEvent(event)

# TODO: base cancel click on how iOS handles it:
# - session no longer than 0.75 seconds
# - no more movement more than 45 points
fireTapEvent = true
for eventName, value of @session.started
if value
# Do not fire a tap event if we are ending another events session
fireTapEvent = false
@["#{eventName}end"](event)

if fireTapEvent
# We only want to fire a tap event if the original target is the same
# as the release target, so buttons work the way you expect if you
# release the mouse outside.
if not @session?.startEvent
@tap(event)
else if @session.startEvent.target is event.target
@tap(event)
if @shouldFireTapEvent(event)
@tap(event)

@tapend(event)
@cancel()
@@ -116,6 +104,19 @@ class exports.GestureInputRecognizer

# Tap

shouldFireTapEvent: (event) ->
startEvent = @session?.startEvent
if startEvent?
# We only want to fire a tap event if the original target is the same
# as the release target, so buttons work the way you expect if you
# release the mouse outside.
isSameTarget = startEvent.target is event.target
isShortSession = event.time - @session.startTime < 750
isShortDistance = Utils.pointDistance(startEvent.touchCenter, event.touchCenter) < 45
return isSameTarget and isShortSession and isShortDistance and not @session.cancelTap
else
return true

tap: (event) => @_dispatchEvent("tap", event)
tapstart: (event) => @_dispatchEvent("tapstart", event)
tapend: (event) => @_dispatchEvent("tapend", event)
@@ -107,36 +107,48 @@ class exports.LayerDraggable extends BaseClass
@attach()

attach: ->
@layer.on(Gestures.PanStart, @touchStart)
@layer.on(Gestures.TapStart, @tapStart)
@layer.on(Gestures.PanStart, @panStart)
@layer.on("change:x", @_updateLayerPosition)
@layer.on("change:y", @_updateLayerPosition)

remove: ->
@layer.off(Gestures.PanStart, @touchStart)
@layer.off(Gestures.Pan, @_touchMove)
@layer.off(Gestures.PanEnd, @_touchEnd)
@layer.off(Gestures.TapStart, @tapStart)
@layer.off(Gestures.PanStart, @panStart)
@layer.off(Gestures.Pan, @_panMove)
@layer.off(Gestures.TapEnd, @_tapEnd)

updatePosition: (point) ->
# Override this to add your own behaviour to the update position
return point

panStart: (event) =>
# For backwards compatibility, we still call touchStart here
@touchStart(event)

touchStart: (event) =>
# We expose this publicly so you can start the dragging from an external event
# this is for example needed with the slider.
@_touchStart(event)
@_panStart(event)

# Stop the simulation if we touch the layer again
tapStart: (event) =>
if @_isAnimating
Framer.GestureInputRecognizer?.session?.cancelTap = true
@_panStart(event)

_updateLayerPosition: =>
# This updates the layer position if it's extrenally changed while
# a drag is going on at the same time.
return if @_ignoreUpdateLayerPosition is true
@_point = @layer.point

_touchStart: (event) =>

_panStart: (event) =>
LayerDraggable._globalDidDrag = false

Events.wrap(document).addEventListener(Gestures.Pan, @_touchMove)
Events.wrap(document).addEventListener(Gestures.PanEnd, @_touchEnd)
Events.wrap(document).addEventListener(Gestures.Pan, @_panMove)
# One would assume to use Gestures.PanEnd here, but we also want to animate back when we touched, but didn't pan
Events.wrap(document).addEventListener(Gestures.TapEnd, @_tapEnd)

# Only reset isMoving if this was not animating when we were clicking
# so we can use it to detect a click versus a drag.
@@ -188,7 +200,7 @@ class exports.LayerDraggable extends BaseClass

@emit(Events.DragSessionStart, event)

_touchMove: (event) =>
_panMove: (event) =>
return unless @enabled

# If we started dragging from another event we need to capture some initial values
@@ -258,12 +270,14 @@ class exports.LayerDraggable extends BaseClass

@emit(Events.DragSessionMove, event)

_touchEnd: (event) =>
_tapEnd: (event) =>
@_panEnd(event)

_panEnd: (event) =>
LayerDraggable._globalDidDrag = false

Events.wrap(document).removeEventListener(Gestures.Pan, @_touchMove)
Events.wrap(document).removeEventListener(Gestures.TapEnd, @_touchEnd)
Events.wrap(document).removeEventListener(Gestures.Pan, @_panMove)
Events.wrap(document).removeEventListener(Gestures.TapEnd, @_tapEnd)
event.stopPropagation()

event.stopPropagation() if @propagateEvents is false
@@ -35,9 +35,9 @@ describe "Layer", ->
move.touches = [
{clientX: i, clientY: 0}
]
layerA.draggable._touchMove(move)
layerA.draggable._panMove(move)
Utils.delay i*time, ->
layerA.draggable._touchEnd(document.createEvent("MouseEvent"))
layerA.draggable._panEnd(document.createEvent("MouseEvent"))
layerA.onDragEnd ->
simulation = layerA.draggable._simulation.x
a = @animate
@@ -119,13 +119,13 @@ describe "ScrollComponent", ->
done("expected up+, down+, null+, but got: #{moves}")

# TODO instead of actually taking time, trick time
draggable._touchStart({clientX: 100, clientY: 100, preventDefault: (->), stopPropagation: (->)})
draggable._panStart({clientX: 100, clientY: 100, preventDefault: (->), stopPropagation: (->)})
Utils.delay 0.01, ->
draggable._touchMove({clientX: 100, clientY: 111, preventDefault: (->), stopPropagation: (->), delta: {x: 0, y: 11}})
draggable._panMove({clientX: 100, clientY: 111, preventDefault: (->), stopPropagation: (->), delta: {x: 0, y: 11}})
Utils.delay 0.20, -> # enough time to set velocity to zero
draggable._touchMove({clientX: 100, clientY: 112, preventDefault: (->), stopPropagation: (->), delta: {x: 0, y: 1}})
draggable._panMove({clientX: 100, clientY: 112, preventDefault: (->), stopPropagation: (->), delta: {x: 0, y: 1}})
Utils.delay 0.21, ->
draggable._touchEnd({clientX: 100, clientY: 112, preventDefault: (->), stopPropagation: (->), delta: {x: 0, y: 0}})
draggable._panEnd({clientX: 100, clientY: 112, preventDefault: (->), stopPropagation: (->), delta: {x: 0, y: 0}})

describe "PageComponent", ->
it "should fire scroll events", (done) ->

0 comments on commit 8616043

Please sign in to comment.