# Gesture System
In business development, we often need to listen to the nodetouchevent, and handle the drag-and-zoom logic.BecauseSkylineuses a dual-threaded architecture, there is a large asynchronous latency when performing such interactive animations, which can be referenced to wxs responding to events .
Skyline``wxsCode runs in theJSthread, and events are generated in theUIThreads, sowxs animationperformance is reduced. To improve the effect of the Weixin Mini Program interactive experience, we built a number of gesture components. The advantages of using gesture components include
- Eliminating the need for developers to listen to
touchevents and compute the complex steps of gesture logic on their own - The gesture component responds directly at the
UIthread, avoiding delays caused by passing to theJSthread
# Effects display
The following diagram demonstrates the effects of dragging a ball using gestures, negotiating gestures, half-screen pop-up gestures to drag and close, and segmented half-screen. Click for more Skyline examples .

Scanning Weixin Mini Program examples to experience thebasic gesturesand thenegotiating gesturesnew features

# Gesture Component
| Component Name | Trigger timing |
|---|---|
| `` | Triggered when you click |
| `` | Triggered when double-clicking |
| `` | Multiple indicators trigger when zooming |
| `` | The iPhone device is triggered again on time |
| `` | Triggered when you drag (horizontal / vertical) |
| `` | Triggered when sliding vertically |
| `` | Triggered when swiping laterally |
| `` | Long time triggers |
# Working principle
A gesture component is a virtual component , and its immediate child nodes are the ones that actually respond to events.In the code below, we add two types of gesture listening to thecontainernode.
- When swiping laterally across the screen, the
horizontal-draggesture node's callback will be triggered; - A callback of the
vertical-draggesture node is triggered when swiping lengthwise across the screen.
<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.If you add a vertical gesture listener insidescroll-view, the gesture listeners insidescrollvieware blocked, causing the gestures to not swipe.
<scrol-view>
<vertical-drag-gesture-handler>
<view id="container"></view>
</vertical-drag-gesture-handler>
</scroll-view>
It should be noted that thepantype is better than thevertical-dragis loose, so when sliding vertically,vertical-dragwill respond, whilepanwill fail.When swiping sideways, thepantype responds.
<vertical-drag-gesture-handler>
<pan-gesture-handler>
<view id="container"></view>
</pan-gesture-handler>
</vertical-drag-gesture-handler>
# Common attributes
| attribute | type | Default values | Required to fill in | Introductions |
|---|---|---|---|---|
| tag | string | nothing | no | Declaration of component identification during gesture consultation |
| worklet:ongesture | eventhandler | nothing | no | Gesture processing callbacks |
| worklet:should-response-on-move | callback | nothing | no | Does the gesture respond when the finger moves? |
| worklet:should-accept-gesture | callback | nothing | no | Whether gestures should be recognized |
| simultaneous-handlers | Array | [] | no | Declarate gesture nodes that can be triggered at the same time |
| native-view | string | nothing | no | Native node types of the agent |
Native-viewSupported enumeration values arescroll-viewandswiper。The 'gesture component proxy internal gestures when the scroll container is scrolled lengthwise, and' when scrolling container is scrolled laterally.
Eventhandlertype is event callback, no return valueThe callbacktype is a callback function registered by the developer to the component, which is executed at the appropriate time to read the callback value- All callbacks can pass in only one worklet callback
# Event callback parameters
# worklet:should-response-on-move
The returned parameterspointerEventThe fields are as follows.When each touch move is callbacked, the corresponding gesture component cannot receive thefalseevent when themoveevent is returned.
| attribute | type | Introductions |
|---|---|---|
| identifier | number | Unique identifier for a touch object |
| type | string | Type of event |
| deltaX | number | The coordinates that moved in the x-axis relative to the last time |
| deltaY | number | The coordinates that moved in the Y direction relative to the last time |
| clientX | number | The X coordinate of the contact point relative to the left edge of the visible visual area |
| clientY | number | The Y coordinate of the contact point relative to the upper edge of the visible area |
| radiusX | number | Returns the horizontal axis (X axis) radius of the smallest ellipse capable of enclosing the contact area |
| radiusY | number | Returns the vertical axis (Y axis) radius of the smallest ellipse capable of enclosing the contact area |
| rotationAngle | number | Returns an angle value representing the angle at which the ellipse described above by radiusX and radiusY needs to be rotated clockwise in order to cover as precisely as possible the area of contact between the user and the plane |
| force | number | The user's pressure on the touch plane |
| timeStamp | number | The timestamp triggered by the event |
Page({
shouldResponseOnMove(pointerEvent) {
'worklet'
return false
}
})
# worklet:should-accept-gesture
As follows, the framework callbacks when the gesture recognition takes effect, leaving it up to the developer to decide whether the gesture will take effect.Take thePangesture as an example.
State.PossibleState,shouldAcceptGestureReturnfalseEnter afterState.CANCELLEDState,Returningtrueenters theState.Startstate to continue receiving the proceduremoveevent.
Page({
shouldAcceptGesture() {
'worklet'
return false
}
})
# worklet:ongesture
Different types of gesture components return the following parameters
# tap / double-tap
| attribute | type | Introductions |
|---|---|---|
| state | number | Gesture Status |
| absoluteX | number | The X coordinate relative to the global |
| absoluteY | number | The Y coordinate relative to the global |
# pan / vertical-drag / horizontal-drag
| attribute | type | Introductions |
|---|---|---|
| state | number | Gesture Status |
| absoluteX | number | The X coordinate relative to the global |
| absoluteY | number | The Y coordinate relative to the global |
| deltaX | number | The coordinates that moved in the x-axis relative to the last time |
| deltaY | number | The coordinates that moved in the Y direction relative to the last time |
| velocityX | number | Lateral speed of the finger away from the screen (pixel per second) |
| velocityY | number | Vertical speed of the finger away from the screen (pixel per second) |
# scale
| attribute | type | Introductions |
|---|---|---|
| state | number | Gesture Status |
| focalX | number | The X coordinate of the center point relative to the global |
| focalY | number | Y coordinate of the center point relative to the global |
| focalDeltaX | number | The coordinate of the center point moving in the X axis relative to the last time |
| focalDeltaY | number | Relative to the last time, the coordinate of the center point moving in the Y axis |
| scale | number | Proportions that are enlarged or decreased |
| horizontalScale | number | Lateral component of scale |
| verticalScale | number | Longitudinal component of scale |
| rotation | number | Angle of swing (in radians) |
| velocityX | number | Lateral speed of the finger away from the screen (pixel per second) |
| velocityY | number | Vertical speed of the finger away from the screen (pixel per second) |
| pointerCount | number | Tracking the Hand Index |
- When multi-finger swiping,
focalXandfocalYare coordinates of the center focus of multiple touch points - When a single finger slides,
pointerCount = 1, the effect is the same aspan-gesture-handler,scaleGesture is a superset ofpan.
# long-press
| attribute | type | Introductions |
|---|---|---|
| state | number | Gesture Status |
| absoluteX | number | The X coordinate relative to the global |
| absoluteY | number | The Y coordinate relative to the global |
| translationX | number | X-axis offset relative to the initial touch point |
| translationY | number | Y-axis offset relative to the initial touch point |
| velocityX | number | Lateral speed of the finger away from the screen (pixel per second) |
| velocityY | number | Vertical speed of the finger away from the screen (pixel per second) |
# force-press
| attribute | type | Introductions |
|---|---|---|
| state | number | Gesture Status |
| absoluteX | number | The X coordinate relative to the global |
| absoluteY | number | The Y coordinate relative to the global |
| pressure | number | The pressure size |
# Gesture Status
All gestureworklet: ongesturecallbacks return astatestatus field.
enum State {
// 手势未识别
POSSIBLE = 0,
// 手势已识别
BEGIN = 1,
// 连续手势活跃状态
ACTIVE = 2,
// 手势终止
END = 3,
// 手势取消
CANCELLED = 4,
}
We divide hand gestures into the following two types:
- Discrete gestures:
tapanddouble-tap, triggered only once - Continuous gestures: Other types of gesture components that are triggered multiple times with dragging a finger
The tap-gesture-handlerThe gesture component returns thestateis always 1.
Pan-gesture-handlergesture component During a complete drag, thestatechanges as follows
- When the finger touches the screen, the
state = 0 - Move a short distance,
panWhen the gesture decision takes effect,state = 1 - Continue moving,
state = 2 - Finger off screen
state = 3
Since nested gestures can create conflicts (only one final judgment of recognition takes effect),Thus, changes in continuous gesturesstatemay occur in the following scenarios, and developers need to handle some exceptions based on thestate]]value.
POSSIBLE - > BEGIN - > ACTIVE - > ENDNormal processPOSSIBLE - > BEGIN - > ACTIVE - > CANCELLEDEARLY INTERRUPTIONPOSSIBLE - > CANCELLEDGesture not recognized
Not all continuous gestures have aPOSSIBLEstate, such asscale-gesture-handlerThe gesture component, when two fingers touch and release, thestatechanges as follows:
- Two-fingered touch screen,
state = 1, pointerCount = 2 - Double finger magnification operation,
state = 2, pointerCount = 2 - Two fingers leave the screen,
state = 3, pointerCount = 1, and then callback a.state = 1, pointerCount = 1b.state = 2, pointerCount = 1c.state = 3, pointerCount = 0
# Note
- Gesture components can only be used in
Skylinerendering mode - The gesture component is a virtual component and will not be layouted. Set on the gesture component
style,classis invalid - The gesture component can contain only one direct sub-node, otherwise it does not take effect
- The parent component style of a gesture component directly affects its child nodes
- All callbacks of gesture components must be declared as
worklet - The gesture is different from the normal
touchevent and does not bubble eventhandler / callbackIs declared as aworkletfunction, and the callback is triggered in theUIthread
# How to use it
# sample code
Preview the effect in the developer tool
# ChainingAPI init function sample code
Preview the effect in the developer tool
# Example 1: Listen to drag and drop gestures
<pan-gesture-handler on-gesture-event="handlePan">
<view></view>
</pan-gesture-handler>
Page({
handlePan(evt) {
"worklet";
console.log(evt.translateX);
},
});
# Example 2: Listen 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: Internal gestures in a proxy native component
For rolling containers like 'and``, the inside is also gesture-based to handle rolling operations.Compared toweb,skyline'A lower-level access mechanism is provided, allowing for more granular and phased control when doing some complex interactions.
<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>
In the case of a vertically scrolled'', you can use the``gesture component and declarenative-view = "scroll-view" 'to proxy its internal gestures.
# Scrolling events
When scrolling the list, both the gesture component event callback and the'scroll' event callback are triggered, the difference being:
The scrollevent is triggered only when scrolling, and when the top / bottom is reached, it is not called backThe on-gesture-eventThe gesture callback is triggered when the finger slides on the screen until you let go
# Gesture Control
In the previous introduction of continuous gesture states, we knew that gestures have their own process of identification.For examplevertical-dragA gesture, which is inPOSSIBLEwhen touched by a finger, is recognized after moving a short distance asBEGIN, at which point the gesture is recognizable (]]ACCEPT)。
# 1. Gesture recognition
The should-accept-gestureattribute allows developers to register acallback,And returns a Boolean value that participates in the process of gesture recognition .Whenfalseis returned, the touch gesture is no longer valid and the associated component cannot be rolled.
# 2. Event distribution
The should-response-on-moveattribute allows developers to register acallback,And returns a Boolean value that participates in the event dispatch process.Whenfalseis returned, the eventmoveis no longer sent, and the associated `` does not continue to scroll.This callback is continuously triggered as the finger moves and can be changed at any time to control the scrolling container to continue / pause the scrolling.
Page({
// 这里返回 false,则 scroll-view 无法滚动
// should-accept-gesture 会在手势识别的一开始触发一次
// should-response-on-move 是在 move 过程中不断触发
shouldScrollViewAccept() {
'worklet'
return true
},
// 这里返回 false,则 scroll-view 无法滚动
shouldScrollViewResponse(pointerEvent) {
'worklet';
return true;
},
// 手指滑动离开滚动组件时,指定衰减速度
adjustDecelerationVelocity(velocity) {
'worklet';
return velocity;
},
// scroll-view 滚动到边界后,手指滑动,scroll 事件不再触发
handleScroll(evt) {
'worklet';
},
// scroll-view 滚动到边界后,手指滑动,手势回调仍然会触发
handleGesture(evt) {
'worklet'
},
});
# Example 4: Gesture consultation
In some scenarios, we will encounter gesture conflict .As shown in the following code, there are nested 'components, and we want theouterThegesture component handles longitudinal drag, and theinnergestures component handles list scrolling, but only theinner' gesture callback is actually triggered.
Embedding the same type of gesture component, when the gesture in the inner layer is recognized, the gesture component in the outer layer 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>
However, such scenarios are quite common, as seen in the comment lists of WeChat Channels, where the scrolling of the list and the dragging of the entire comment section seamlessly integrate.Gesture negotiation mechanism is used to solve this kind of problem, and it is also very simple to use.simultaneous-handlersattribute states that multiple gestures can be triggered simultaneously.
<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 point,outerandinnerTheon-gesture-eventcallback of the gesture component is triggered in succession, combined with the gesture control principle mentioned above, to achieve the desired effect.Full code reference sample demo .