# Gesture system

In business development, we often need to listen to nodes touch Event that handles drag and drop and zoom related logic. Because Skyline Using a two-thread architecture, in such interactive animation, there will be a large asynchronous delay, which can be referred to wxs Responding to Events

Skyline in wxs The code runs on the JS Thread, while events are generated in the UI Thread. Therefore, wxs animation Performance has been reduced, in order to improve the effect of the interactive experience of the Mini Program, we have built a number of gesture components. The advantages of using gesture components include

  1. Avoid listening to developers touch Events, calculate the complex steps of gesture logic on your own
  2. The gesture component directly in the UI Thread response, avoiding the need to pass to the JS Delays caused by threads

# Effect display

The following figure demonstrates the use of gestures, negotiation gestures to achieve the drag ball, half-screen pop-up gesture drag off, segmented half-screen and other effects.Click to see more Skyline Example

Scan code Mini Program examples, respectively, to experience Basic gesture and Negotiation gesture New features

# Gesture component

Component name Trigger timing
<tap-gesture-handler> Triggered when clicked
<double-tap-gesture-handler> Triggered when double-click
<scale-gesture-handler> Triggered when multi-finger scaling
<force-press-gesture-handler> iPhone Equipment re-triggered on time
<pan-gesture-handler > Drag (Horizontal/Longitudinal) when triggered
<vertical-drag-gesture-handler> Triggered when longitudinal sliding
<horizontal-drag-gesture-handler > Triggered when lateral sliding
<long-press-gesture-handler> Long time trigger

# Working principle

Gesture component is virtual component, the real response to the event is its direct child node. In the code below, we give container The node adds two types of gesture listening.

  1. When swiping horizontally across the screen,horizontal drag The callback of the gesture node will be triggered
  2. When swiping lengthwise on the screen,vertical-drag The callback of the gesture node will be triggered.
<horizontal-drag-gesture-handler >
  <vertical-drag-gesture-handler>
     <view id="container"></view>
  </vertical-drag-gesture-handler>
</horizontal-drag-gesture-handler >

When you touch the screen, the rendering engine recognizes gestures from the inside out, and when one of the listeners meets the criteria, the rest of the listeners fail. As in scroll-view When you add a longitudinal gesture monitor internally, it will block scroll-view Inside the gesture listener, resulting in an inability to swipe.

<scrol-view>
  <vertical-drag-gesture-handler>
     <view id="container"></view>
  </vertical-drag-gesture-handler>
</scroll-view>

It's important to note,pan The ratio of determining conditions for type vertical-drag To be loose, so when sliding lengthwise,vertical-drag Will respond, and pan Will fail. When sliding horizontally,pan Type will respond.

<vertical-drag-gesture-handler>
  <pan-gesture-handler >
     <view id="container"></view>
  </pan-gesture-handler >
</vertical-drag-gesture-handler>

# Common attributes

attribute type Default value Required Introductions
tag string nothing no Component identity when declaring gesture negotiation
worklet:ongesture eventhandler nothing no Gesture Processing Callback
worklet:should-response-on-move callback nothing no Whether the gesture responds during finger movement
worklet:should-accept-gesture callback nothing no Whether gestures should be recognized
simultaneous-handlers Array<string> [] no Declare gesture nodes that can be triggered simultaneously
native-view string nothing no Native node type for proxy

native-view The supported enumeration values are scroll-view and swiperWhen scrolling the container lengthwise, use the <vertical-drag-gesture-handler> The gesture component represents the internal gesture, and when scrolling horizontally, the <horizontal-drag-gesture-handler >

  • eventhandler The type is an event callback, with no return value
  • callback A type is a callback function that the developer registers with the component and is executed at the appropriate time to read the return value.
  • All callbacks can only pass in one worklet Callback

# Event callback parameter

# worklet:should-response-on-move

Parameters returned pointerEvent The fields are as follows. Callback each time the touch moves, returning the false The corresponding gesture component cannot receive the move Events.

attribute type Introductions
identifier number Touch Object's unique identifier
type string Type of event
deltaX number Last time, x Axis moving coordinates
deltaY number Last time, Y Axis moving coordinates
clientX number Contact with respect to the left edge of the visible visual X coordinate
clientY number Contact with respect to the upper edge of the visible visual Y coordinate
radiusX number Returns the horizontal axis of the smallest ellipse capable of surrounding the contact area (X axis) radius
radiusY number Returns the vertical axis of the smallest ellipse capable of surrounding the contact area (Y axis) radius
RotationAngle number Returns an angle value representing the value described above by radiusX and radiusY The ellipse described needs to be rotated clockwise in order to cover the contact area between the user and the plane as accurately as possible
force number The user's pressure on the touch plane
timeStamp number The time stamp triggered by the event
Page({
  shouldResponseOnMove(pointerEvent) {
    'worklet'
    return false
  }
})

# worklet:should-accept-gesture

The usage is as follows. Frame gesture recognition takes effect when it calls back, and it is up to the developer to decide whether the gesture takes effect. with Pan Hand gestures, for example.

Enter when you touch screen State.Possible Status,shouldAcceptGesture return false After entering State.CANCELLED Status, Return true After entering State.Begin Status, can continue to receive formalities move Events.

Page({
  shouldAcceptGesture() {
    'worklet'
    return false
  }
})

# worklet:ongesture

The parameters returned by different types of gesture components are as follows

# tap / double-tap

attribute type Introductions
state number Gesture state
absoluteX number Relative to the global X coordinate
absoluteY number Relative to the global Y coordinate

# pan / vertical-drag / horizontal drag

attribute type Introductions
state number Gesture state
absoluteX number Relative to the global X coordinate
absoluteY number Relative to the global Y coordinate
deltaX number Last time, x Axis moving coordinates
deltaY number Last time, Y Axis moving coordinates
velocityX number The lateral speed of the finger as it leaves the screen (pixel per second)
velocityY number The vertical speed of the finger as it leaves the screen (pixel per second)

# scale

attribute type Introductions
state number Gesture state
focalX number Center point with respect to the global X coordinate
focalY number Center point with respect to the global Y coordinate
focalDeltaX number Relative to the last time, the center point in the X Axis moving coordinates
focalDeltaY number Relative to the last time, the center point in the Y Axis moving coordinates
scale number Magnified or reduced ratio
horizontalScale number scale The transverse component of
verticalScale number scale The longitudinal component of the
rotation number Angle of swing (in radians)
velocityX number The lateral speed of the finger as it leaves the screen (pixel per second)
velocityY number The vertical speed of the finger as it leaves the screen (pixel per second)
pointerCount number Number of fingers tracked
  • When multiple fingers slide,focalX and focalY Coordinates for a central focus of a plurality of touch points
  • When one finger slides,pointerCount = 1, the same effect at this time pan-gesture-handlerscale The gesture is pan Of the superset.

# long-press

attribute type Introductions
state number Gesture state
absoluteX number Relative to the global X coordinate
absoluteY number Relative to the global Y coordinate
translationX number Relative to the initial touch point of the X Axis offset
translationY number Relative to the initial touch point of the Y Axis offset
velocityX number The lateral speed of the finger as it leaves the screen (pixel per second)
velocityY number The vertical speed of the finger as it leaves the screen (pixel per second)

# force-press

attribute type Introductions
state number Gesture state
absoluteX number Relative to the global X coordinate
absoluteY number Relative to the global Y coordinate
pressure number Pressure size

# Gesture state

All gestures worklet:ongesture The callback will always return a state Status field.

enum State {
  // Gesture not recognized
  POSSIBLE = 0,
  // Gesture recognized
  BEGIN = 1,
  // Continuous gesture active state
  ACTIVE = 2,
  // Gesture termination
  END = 3,
  // Gesture cancellation
  CANCELLED = 4,
}

We divide gestures into two types:

  1. Discrete Gestures:tap and double-tap, Triggered Only Once
  2. Continuous gestures: Other types of gesture components, finger dragging triggers multiple times

tap-gesture-handler Returned by the gesture component state Always for 1。

pan-gesture-handler The gesture component during a complete drag,state Will change in the following manner

  1. When your finger touches the screen,state = 0
  2. Moving a short distance,pan When the gesture determination is in effect,state = 1
  3. Keep moving,state = 2
  4. Finger off the screen state = 3

Because nested gestures conflict (only one final decision is recognized), successive gestures state The changes may have the following scenarios, the developer needs according to the state Value to handle some exceptional cases.

  1. POSSIBLE -> BEGIN -> ACTIVE -> END Normal flow
  2. POSSIBLE -> BEGIN -> ACTIVE -> CANCELLED Advance interruption
  3. POSSIBLE -> CANCELLED Gesture not recognized

Not all continuous gestures have POSSIBLE Status, such as scale-gesture-handler Gesture component, when the two fingers touch and let go,state The changes are as follows:

  1. Two-fingered touch screen,state = 1, pointerCount = 2
  2. Double finger magnification operation,state = 2, pointerCount = 2
  3. Two fingers off the screen,state = 3, pointerCount = 1♪ and then we'll call it back a. state = 1, pointerCount = 1 b. state = 2, pointerCount = 1 c. state = 3, pointerCount = 0

# Note

  • The gesture component is available only in Skyline Can be used in rendering mode
  • The gesture component is a virtual component and will not be laid out. styleclass Is invalid.
  • A gesture component can contain only one direct child node, otherwise it is not valid
  • The parent component style of a gesture component directly affects its children
  • The callback function of the gesture component must be declared as worklet function
  • Gestures are different from ordinary touch Event, will not proceed to bubble
  • Of the gesture component. eventhandler / callback Must be declared as worklet Function, the callback in the UI Thread Trigger

# Methods of Use

# sample code

Preview the effect in developer tools

# Chaining API init Function sample code

Preview the effect in developer tools

# Example 1: Listen for drag gestures

<pan-gesture-handler  on-gesture-event="handlePan">
  <view></view>
</pan-gesture-handler >
Page({
  handlePan (possibly ) {
    "worklet"
    console.log(evt.translateX)
  },
})

# Example 2: Listening for nested gestures

<horizontal-drag-gesture-handler  on-gesture-event="handleHorizontalDrag">
  <vertical-drag-gesture-handler on-gesture-event="handleVerticalDrag">
    <view class="circle">one-way drag</view>
  </vertical-drag-gesture-handler>
</horizontal-drag-gesture-handler >

# Example 3: Proxy native component internal gestures

for <scroll-view> and <swiper> Such a scrolling container internally handles scrolling operations based on gestures. Compared to webskyline Provides a lower level access mechanism, so that in some complex interactions, you can do more granular, phased control.

<vertical-drag-gesture-handler
  native-view="scroll-view"
  should-response-on-move="shouldScrollViewResponse"
  should-accept-gesture="shouldScrollViewAccept"
  on-gesture-event="handleGesture"
>
  <scroll-view
    scroll-y
    type="list"
    adjust-deceleration-velocity="adjustDecelerationVelocity"
    bindscroll="handleScroll" 
  >
    <view class="item" wx:for="{{list}}">
      <view class="avatar" />
      <view class="comment" />
    </view>
  </scroll-view>
</vertical-drag-gesture-handler>

Rolling lengthwise <scroll-view> For example, you can use <vertical-drag-gesture-handler> Gesture components, and declare that native-view="scroll-view" To represent their internal gestures.

# Rolling events

When the list is scrolled, the event callback of the gesture component and the <scroll-view> of scroll Event callbacks are triggered, and they differ in that:

  1. scroll Events are triggered only when rolling, when the top/After the bottom, no more callbacks
  2. on-gesture-event Gesture callbacks trigger when the finger is swiped across the screen until the hand is released

# Gesture control

In the previous introduction to continuous gesture states, we know that gestures have their own recognition process. for example vertical-drag Gesture, when the finger touches for POSSIBLE State, moves a short distance before being recognized as BEGIN State, at which time the gesture is said to be recognized (theACCEPT)。

# 1. Gesture recognition

should-accept-gesture Property allows the developer to register a callback, and returns a boolean value that participates in theGesture recognitionOf the process. When returning false When the touch gesture is no longer in effect, the associated <scroll-view> Components cannot be scrolled either.

# 2. Event distribution

should-response-on-move Property allows the developer to register a callback, and returns a boolean value that participates in theEvent distributionOf the process. When returning false When, when move Of the events are no longer distributed, the associated <scroll-view> No, keep rolling. This callback will continue to trigger as the finger moves and can be changed at any time to control the scroll container to continue/Pause scrolling.

Page({
  // Here to return False, then scroll-view Unable to scroll
  // should-accept-gesture Will trigger once at the beginning of gesture recognition.
  // should-response-on-move Was in the move Constantly triggered in the process
  shouldScrollViewAccept() {
    'worklet'
    return true
  },

  // Here to return False, then scroll-view Unable to scroll
  shouldScrollViewResponse(pointerEvent) {
    'worklet'
    return true
  },

  // Specifies the decay rate when the finger slides away from the rolling assembly
  adjustDecelerationVelocity(velocity) {
    'worklet'
   return velocity
  },

  // scroll-view After scrolling to the border, slide your finger, scroll Events are no longer triggered
  handleScroll (possibly ) {
    'worklet'
  },

  // scroll-view After scrolling to the border, the finger slides and the gesture callback still triggers
  handleGesture (possibly ) {
    'worklet'
  },
})

# Example 4: Gesture Negotiation

In some cases, we will encounterGesture conflictAs the code below shows, there are nested <vertical-drag-gesture-handler> Components. We hope that Outer The gesture component to handle the longitudinal drag,Inner The gesture component handles the scrolling of the list, but actually only Inner The gesture callback is triggered.

Nested gesture components of the same type, when the inner gesture recognition, the outer gesture components will not be recognized.

<vertical-drag-gesture-handler tag="outer">
  <vertical-drag-gesture-handler tag="inner" native-view="scroll-view">
    <scroll-view scroll-y></scroll-view>
  </vertical-drag-gesture-handler>
</vertical-drag-gesture-handler>

But the above scenario is very common, such as WeChat Channels comment list, the list of scrolling and the entire comment section of the link is very smooth. Gesture negotiation mechanism is used to solve this problem, and it is also very simple to use.simultaneous-handlers Property declares that multiple gestures can be triggered at the same time.

<vertical-drag-gesture-handler tag="outer" simultaneous-handlers= ["inner"]}}">
  <vertical-drag-gesture-handler tag="inner" simultaneous-handlers= ["outer"]}}" native-view="scroll-view">
    <scroll-view scroll-y></scroll-view>
  </vertical-drag-gesture-handler>
</vertical-drag-gesture-handler>

At this time,Outer and Inner Of the gesture component. on-gesture-event Callbacks will be triggered in turn, in combination with the aboveGesture controlPrinciple, can achieve the desired effect. Full Code ReferenceExample Demo