# Custom Routing
Mini Program uses more WebView
Architecture, the form of jump between pages is very single, only from right to left for animation. And native App
The forms of animations are varied, such as bouncing from the bottom, sinking the page, half-screen and so on.
Skyline
Under the rendering engine, the page has two rendering modes: WebView
and Skyline
, which are configured by the page in the Renderer
Field to make a distinction. inContinuous Skyline
pageWhen jumping between, can realize custom route effect.
# Effect display
Below is the half-screen page effect,Click to see more Skyline Example。
Scan the code to open the Mini Program example, interactive animation - Basic components - Custom Routing Can experience.
# Methods of Use
It is recommended to read first worklet animation and Gesture system Two chapters, which are the basics of custom routing.
# Interface definition
Custom Route Dependent Interfaces
- Page Jump [wx.navigateTo ]((wx.navigateTo ))
- Route context object wx.router.getRouteContext
- Sign up for custom routing wx.router.addRouteBuilder
type AddRouteBuilder = (routeType: string, routeBuilder: CustomRouteBuilder) => void
type CustomRouteBuilder = (routeContext: CustomRouteContext, routeOptions: Record<string, any>) => CustomRouteConfig
Interface SharedValue<T> {
value: T
}
Interface CustomRouteContext {
// Animation controller that affects the entry and exit transition effect of the pushed page
primaryAnimation: SharedValue<number>
// Animation Controller State
primaryAnimationStatus: SharedValue<number>
// Animation controller that affects the rollout of the top page of the stack
secondaryAnimation: SharedValue<number>
// Animation Controller State
secondaryAnimationStatus: SharedValue<number>
// Current routing progress is controlled by gesture
userGestureInProgress: SharedValue<number>
// Gesture Start Control Route
startUserGesture: () => void
// Gestures no longer control routing
stopUserGesture: () => void
// Return to the previous level with the same effect wx.navigateBack
didPop: () => void
}
Interface CustomRouteConfig {
// After the next page is pushed, the previous page is not displayed
opaque?: boolean
// Whether to maintain the previous page state
maintainState?: boolean
// Page Push Animation Length, Unit ms
transitionDuration?: number
// Page Launch Animation Length, Unit ms
reverseTransitionDuration?: number
// Mask layer background color, support rgba() and #RRGGBBAA Writing method
barrierColor?: string
// Click on the mask layer to return to the previous page
barrierDismissible?: boolean
// Accessibility semantics
barrierLabel?: string
// Whether to link with the next page to determine the current page secondaryAnimation Entry into force
canTransitionTo?: boolean
// Whether it is linked to the previous page, determines the previous secondaryAnimation Entry into force
canTransitionFrom?: boolean
// Processing the entry of the current page/Exit Animation, Return StyleObject
handlePrimaryAnimation?: RouteAnimationHandler
// Handles pressing of the current page/Press out the animation, return StyleObject
handleSecondaryAnimation?: RouteAnimationHandler
// Handle the pressing of a page at a previous level/Press out the animation, return StyleObject Base library <3.0.0> Support from
handlePreviousPageAnimation?: RouteAnimationHandler
// Whether the page is entered by snapshot Mode Optimizes Animation Performance Base library <3.2.0> Support from
allowEnterRouteSnapshotting?: boolean
// When the page exits snapshot Mode Optimizes Animation Performance Base library <3.2.0> Support from
allowExitRouteSnapshotting?: boolean
// When the right slide back, whether the draggable range fills the screen, the base library <3.2.0> Support, often used for half-screen pop-up windows
Fullscreen Drag: boolean
// Return to Gesture Direction Base library <3.4.0> Support from
popGestureDirection?: 'horizontal' | 'vertical' | 'multi'
}
type RouteAnimationHandler = () => { [key: string] : any}
# Default route configuration
const defaultCustomRouteConfig = {
opaque: true,
maintainState: true,
transitionDuration: 300,
reverseTransitionDuration: 300,
barrierColor: '',
barrierDismissible: false,
barrierLabel: '',
canTransitionTo: true,
canTransitionFrom: true,
allowEnterRouteSnapshotting: false,
allowExitRouteSnapshotting: false,
Fullscreen Drag: false,
popGestureDirection: 'horizontal'
}
# Sample Templates
The following is a sample template for registering a custom route (without adding gesture processing), see the sample code for the full implementation of the half-screen routing effect.
const customRouteBuiler = (routeContext: CustomRouteContext) : CustomRouteConfig => {
const {
primaryAnimation,
secondaryAnimation,
userGestureInProgress
} = routeContext
const handlePrimaryAnimation: RouteAnimationHandler = () => {
'worklet'
let t = primaryAnimation.value
if (!userGestureInProgress.value) {
// select another curve, t = xxx
}
// StyleObject
return {}
}
const handleSecondaryAnimation: RouteAnimationHandler = () => {
'worklet'
let t = secondaryAnimation.value
if (!userGestureInProgress.value) {
// select another curve, t = xxx
}
// StyleObject
return {}
}
return {
opaque: true,
handlePrimaryAnimation,
handleSecondaryAnimation
}
}
// Define it before the page jump routeBuilder
wx.router.addRouteBuilder('customRoute', customRouteBuiler)
// When you jump to a new page, specify the corresponding routeType
wx.navigateTo ({
url: 'xxxx',
routeType: 'customRoute'
})
# Working principle
Take the half-screen effect as an example, the pages before and after the route are recorded as A
Page,B
Pages, in the lifetime of a route, go through the following stages:
push
stage : Callwx.navigateTo
,B
The page pops up from the bottom up,A
Page Sink Shrink- Gesture drag: in the
B
The route animation changes as the page slides up and down pop
stage : Callwx.navigateBack
,B
Page down close,A
Back to the original
Subdivided into each page, in the above stage will be the following animation
- Get into/Exit animation
- Press/Squeeze animation
- Gesture drag
- in
push
Stage,B
Page is performed by entering the animation,A
The page is pressed into the animation - in
pop
Stage,B
Page is performed by exit animation,A
The page is pressed out of the animation
You can see in the routing process, before and after the two page animation linkage. In the custom routing mode, we can customize the length, curve, effect and linkage of each stage of the animation to achieve flexible and changeable page special effects.
# Route controller
When a new page is opened, the framework creates two SharedValue
Type of animation controller primaryAnimation
and secondaryAnimation
, respectively control the entry into the/Exit Animation and Pressing/Press out the animation.
The entry and exit of the page can be specified for different durations, but the progress change is always in the 01
Between. Still taking the half-screen effect as an example, the page before and after the route is recorded as A
Page,B
Pages.
# push
stage
B
Page-correspondingprimaryAnimation
from0 -> 1
Changes, do enter the animationA
Page-correspondingsecondaryAnimation
from0 -> 1
Change, do the press-in animation
# pop
stage
B
Page-correspondingprimaryAnimation
from1 -> 0
Change, do exit animationA
Page-correspondingsecondaryAnimation
from1 -> 0
Change, do the press-out animation
Among them,A
page secondaryAnimation
The value of the B
page primaryAnimation
The value changes synchronously.
Usually the entry and exit of the page may use different animation curves, which can be used by the corresponding state variables. primaryAnimationStatus
and secondaryAnimationStatus
To tell which stage you're in,ts
Defined as follows
enum AnimationStatus {
// Animation stops at the beginning
dismissed = 0,
// Animation from start to finish
forward = 1,
// Animation from the end to the beginning
reverse = 2,
// Animation stops at the end
completed = 3,
}
with primaryAnimationStatus
For example, the page entry and exit process changes as follows
push
Stages:dismissed
->forward
->completed
pop
Stages:completed
->reverse
->dismissed
# Routing gesture
After the page is pushed, in addition to calling the wx.navigateBack
Interface to return the previous level, you can also be handled by gestures, such as iOS
On the common right slide back. In custom routing mode, developers can choose the required exit mode according to different page transition effects, such as half-screen effect can be used to slide back. For the content of gesture monitoring, refer to the Gesture system The chapter on routing gestures is only based on it, supplemented by several route-related interfaces.
startUserGesture
and stopUserGesture
Two functions are always called in pairs,startUserGesture
After the call userGestureInProgress
The value will be added 1
。
When developers modify themselves primaryAnimation
To control the routing progress, you need to call these two interfacesBecause different animation curves are often used during gesture dragging, you can use the userGestureInProgress
Value to judge.
When the gesture processing determines that it is necessary to return to the previous level page, call the didPop
Interface, the role of the equivalent wx.navigateBack
。
# Routing linkage
Routing animation process, the default before and after the two pages are linked together, can be turned off through the configuration item.
canTransitionTo
: whether to link with the next page, the top of the stack This property is set tofalse
, the top page of the stack remains untouched when the next page is pushedcanTransitionFrom
: whether to link with the previous page, the new push page This property is set tofalse
, the top page of the stack remains unmoved
# Route context object
As you can see from the example template, the animation of the custom route is based on the CustomRouteContext
Context object on the route controller, write the appropriate animation update function to implement.
CustomRouteContext
The context object is also available on the page/In a custom component by wx.router.getRouteContext(this)
Read, and in turn accessed during gesture processing, through the primaryAnimation
Value to achieve page gesture return.
Tip: Available at CustomRouteContext
Object to read from the page/Modification.
# Multiple Route Jump
Consider such a scenario, from the page A
May jump to B
Pages and C
Page, but with a different route animation
A
->B
When you want to achieve the half-screen effect,A
Need to sink and contractA
->C
When it is desirable to employ ordinary routing,A
Need to move to the left
Animation when you jump to the next level of the page by handleSecondaryAnimation
Control, so that it is necessary to define the A
of CustomRouteBuilder
When considering all routing types, the implementation is more cumbersome.
Base library 3.0.0
Version, Custom Routing added handlePreviousPageAnimation
Interface for controlling the compression of the page at the previous level/Press out the animation.
const customRouteBuiler = (routeContext: CustomRouteContext) : CustomRouteConfig => {
const { primaryAnimation } = routeContext
const handlePrimaryAnimation: RouteAnimationHandler = () => {
'worklet'
let t = primaryAnimation.value
// Control the entry and exit of the current page
}
const handlePreviousPageAnimation: RouteAnimationHandler = () => {
'worklet'
let t = primaryAnimation.value
// Control the push in and out of the previous page
}
return {
handlePrimaryAnimation,
handlePreviousPageAnimation
}
}
A
Jump to B
When, A
page secondaryAnimation
The value of the B
page primaryAnimation
The value changes synchronously.
We can define B
of CustomRouteBulder
When, through primaryAnimation
Knowing the current routing progress,handlePreviousPageAnimation
Returned StyleObject
It will work on the next page.
And there's no need to declare A
For custom routes, and before that A
Jump B
When you want to achieve a half-screen effect,A
Must also be defined as a custom route.
The complete example can be found in the following code, with the help of handlePreviousPageAnimation
Can remove the right secondaryAnimation
To simplify the code logic.
Preview the effect in developer tools
# Practical cases
The following half-screen effect as an example, to explain the specific implementation of custom routing process, the complete code see sample code.
The pages before and after the route are recorded as A
Pages and B
Page for which you need to register custom routes, respectively.Newly opened pages when no custom routing effects are registered B
Will immediately override the display in the A
On the page.
# Step-1 Page Entry Animation
Let's start with simple implementation. home page -> A
page -> B
Page into the animation, and then step by step to improve.
for A
Page, enter from right to left, through the transform
Translation implementation.
function ScaleTransitionRouteBuilder(customRouteContext) {
const {
primaryAnimation
} = customRouteContext
const handlePrimaryAnimation = () => {
'worklet'
let t = primaryAnimation.value
const transX = windowWidth * (1 - t)
return {
transform: `translateX(${transX}px )`,
}
}
return {
handlePrimaryAnimation
}
}
for B
Page, the entry is bottom-up, but also through the transform
Translation, but need to change the page size, rounded corners.
const HalfScreenDialogRouteBuilder = (customRouteContext) => {
const {
primaryAnimation,
} = customRouteContext
const handlePrimaryAnimation = () => {
'worklet'
let t = primaryAnimation.value
// Distance Top Margin Factor
const topDistance = 0.12
// Distance Top Margin
const marginTop = topDistance * screenHeight
// Half Screen Page Size
const pageHeight = (1 - topDistance) * screenHeight
// Bottom-up display page
const transY = pageHeight * (1 - t)
return {
overflow: 'hidden',
borderRadius: '10px',
marginTop: `${marginTop}px`,
height: `${pageHeight}px`,
transform: `translateY(${transY}px )`,
}
}
return {
handlePrimaryAnimation,
}
}
The page jump effect is as follows, and you can see that due to the use of linear curves (not t
Do any transformation), the animation is somewhat rigid, while not differentiating into/Exit animation. in B
After the page is fully entered,A
The page becomes invisible.
# Step-2 Custom Animation Curves
with B
Page, for example, according to the AnimationStatus
Values, employing different animation curves, while setting the opaque
for false
So that the route animation is still displayed when it is finished A
Page.
const { Easing, derived } = wx.workelt
const Curves = {
linearToEaseOut: Easing.cubicBezier(0.35, 0.91, 0.33, 0.97),
easeInToLinear: Easing.cubicBezier(0.67, 0.03, 0.65, 0.09),
fastOutSlowIn: Easing.cubicBezier(0.4, 0.0, 0.2, 1.0),
fastLinearToSlowEaseIn: Easing.cubicBezier(0.18, 1.0, 0.04, 1.0),
}
function CurveAnimation({ animation, animationStatus, curve,reverseCurve }) {
return derived(() => {
'worklet'
const useForwardCurve = !reverseCurve || animationStatus.value !== AnimationStatus.reverse
const activeCurve = useForwardCurve ? curve : reverseCurve
const t = animation.value
if (!activeCurve) return t
if (t === 0 || t === 1) return t
return activeCurve(t)
})
}
const HalfScreenDialogRouteBuilder = (customRouteContext) => {
const {
primaryAnimation,
primaryAnimationStatus,
} = customRouteContext
// 1. When the page is entered, use the Curves.linearToEaseOut curve
// 2. When the page exits, take the Curves.easeInToLinear curve
const _curvePrimaryAnimation = CurveAnimation({
animation: primaryAnimation,
animationStatus: primaryAnimationStatus,
curve: Curves.linearToEaseOut,
reverseCurve: Curves.easeInToLinear,
})
const handlePrimaryAnimation = () => {
'worklet'
let t = _curvePrimaryAnimation.value
... // The rest of the content, etc. The above code is consistent
}
return {
opaque: false,
handlePrimaryAnimation,
}
}
The difference here is only that the current progress is no longer read directly primaryAnimation
The value of. Encapsulated CurveAnimation
The function will be based on the AnimationStatus
Determines whether it is in or out state to select a different animation curve. Frame provides a variety of curve types for further reference worklet.EasingThe improved page transitions are as follows
# Step-3 Page linkage effect
B
When the page enters,A
The page is pressed into the animation by secondaryAnimation
Control. Next, we add a sinking effect to it, implement and B
Linkage of the page.
function ScaleTransitionRouteBuilder(customRouteContext) {
const {
primaryAnimation
} = customRouteContext
const handlePrimaryAnimation = () => {
'worklet'
...
}
const _curveSecondaryAnimation = CurveAnimation({
animation: secondaryAnimation,
animationStatus: secondaryAnimationStatus,
curve: Curves.fastOutSlowIn,
})
const handleSecondaryAnimation = () => {
'worklet'
let t = _curveSecondaryAnimation.value
// Page Zoom Size
const scale = 0.08
// Distance Top Margin Factor
const topDistance = 0.1
// Estimated offset
const transY = screenHeight * (topDistance - 0.5 * scale) * t
return {
overflow: 'hidden',
borderRadius: `${ 12 * t }px`,
transform: `translateY(${transY}px ) scale(${ 1 - scale * t })`,
}
}
return {
handlePrimaryAnimation,
handleSecondaryAnimation
}
}
By means of A
Page Work scale
and translate
Transform to achieve the sinking effect.A
page secondaryAnimation
The value of the B
page primaryAnimation
The values of the.
The page can also be linked by canTransitionTo
and canTransitionFrom
Two properties are configured to modify the experience on the developer tools.
# Step-4 Gesture Return
At present, the animation effect has been basically achieved, and the final step, gesture return, is still needed. For the half-screen effect, we create A
Page to add the right slide back gesture,B
Page to add slide back gesture.
To the most common right slide back as an example, here only intercepts the hand gesture after the hand handling part of the code, the drag process is relatively simple to achieve, can refer to the sample code.
Page({
handleDragEnd (velocity) {
'worklet'
const {
primaryAnimation,
stopUserGesture,
didPop
} = this.customRouteContext
let animateForward = false
if (Math.abs (velocity) >= 1.0) {
animateForward = velocity <= 0
} else {
animateForward = primaryAnimation.value > 0.5
}
const t = primaryAnimation.value
const animationCurve = Curves.fastLinearToSlowEaseIn
if (animateForward) {
const duration = Math.min(
Math.floor(lerp(300, 0, t)),
300,
)
primaryAnimation.value = Timing(
1.0, {
duration,
easing: animationCurve,
},
() => {
'worklet'
stopUserGesture()
},
)
} else {
const duration = Math.floor(lerp(0, 300, t))
primaryAnimation.value = Timing(
0.0, {
duration,
easing: animationCurve,
},
() => {
'worklet'
stopUserGesture()
didPop()
},
)
}
},
})
First, depending on the speed and position of the release, decide whether to actually return to the previous level.
- Slide to the right and the speed is greater than
1
- Or the speed is small, has been dragged more than the screen
1/2
When the above conditions are met, the return is determined. adopt Timing
Interface, for primaryAnimation
Add the transition animation so that it changes to the 0
, Last Call didPop
Otherwise make it change to 1
, return to the state before the drag.
It is important to note here that when there is a need for primaryAnimation
The value is manually modified and is free to control its transition mode startUserGesture
and stopUserGesture
Interface.
The right slide gesture has been encapsulated in the sample code as swipe-back
Components that developers can use directly. The return logic of glide gestures is basically the same, with only slight differences in some values.
The final implementation effect is shown in the figure
# Setting Page Transparency
Some custom routing effects, you need to achieve a transparent background page, here on the Skyline
and webview
The hierarchical relationship of background color is described.
# Page Background Color Under Custom Route
Skyline
Mode to use a custom route jump page, the page background color has the following layers
- Page Background Color: Available by
Page
Selector in thewxss
Defined in, the default is white - Page Container Background Color: Available on Page
json
In the document throughbackgroundColorContent
Property Definition, Support#RRGGBBAA
Writing, default white - Custom route container background color, as returned in the route configuration entry
StyleObject
Control, Transparent by Default - Controls whether the previous page is displayed by the
opaque
Field control, does not show by default
When you need to set the next page to gradually enter, you can simply set
- Page background color transparent:
Page { background-color: transparent }
- Page Container Background Color Transparent:
backgroundColorContent: "#ffffff00"
View Custom Route Page Fade Sample
# webview The page background color under
By comparison,webview
Page background color in mode
- Page Background Color: Available by
Page
Selector in thewxss
Defined in, the default is transparent - Page Container Background Color: Available on Page
json
In the document throughbackgroundColorContent
Property Definition, Support#RRGGBB
Writing, default white - Window Background Color: Available by wx.setBackgroundColor Interface or page configuration modification, defaults to white