The blogger came back to 😂 after two months. Since joining Alibaba, he has participated in three projects successively:

  1. Taobaowulin Assembly answer panel
  2. Taobao live base binding and check-in H5 page
  3. Favorites stub weeX page

Now taobao’s favorites folder project is also in charge of me. The focus is also to talk about Taobao favorites weeX animation optimization technology – BindingX. It’s mostly for new people

Weex and BindingX literacy

What is Weex?

Weex is dedicated to enabling developers to build Android, iOS and Web applications based on common cross-platform Web development languages and development experience. In short, with the WeexSDK integrated, you can use the JavaScript language and front-end development experience to develop mobile applications.

Generally speaking, JS can communicate with native through WEEX, so you can write JS code in front to develop part of the functions and pages of mobile app. Generally speaking, weeX layer in Taobao app is mainly some static pages that do not rely on API calls of mobile phone native functions, and most of them show some static data. Weex for feed stream list display is still very friendly, for example, THIS TIME I took over weex Taobao favorites project, about favorites baby content list page, baby search page, baby list page and a series of static pages. You can also look at the shortcut function I made this time:

What is a BindingX

BindingX is a solution to the problem of rich interaction between WEEx and React Native. It provides a mechanism called “Expression Binding” that allows complex interactions such as gestures to run smoothly at 60fps on weex and React Native without causing stutter, resulting in a better user experience. — — from BindingX website: alibaba. Making. IO/BindingX/gu…

The core idea of BindingX is to describe “interactive behavior” in the form of expression and preset to Native in advance, so as to avoid frequent communication between JS and Native when the behavior is triggered. — — from BindingX website: alibaba. Making. IO/BindingX/gu…

How to use WEEX in RAX

Here, I will not talk about too much weeX foundation, most of the application related, combined with the use of WEEX in RAX to explain to you one by one.

  1. Rax init [your project name] : NPX init [your project name]
  2. Select the App.
  3. Select Web and WEEx.
  4. For example use we chose SPA (single page application).
  5. Enter your own name.
  6. Choose the language you want to use, in this case JavaScript.
  7. I’m just going to enter. (Note: Do not introduce react, that is, do not check the third option: Compatibility with react)
  8. Here you are prompted to automatically install dependencies, and we chose yes.

So far our project has been created, the next is our actual combat link!

Bookmark small cases

In order to let you experience the weeX BindingX technology charm I will use a familiar favorite clip small function as a demonstration: favorites list left slide function, I believe we all know the specific operation, is left slide three options, to the store, share, delete.

First of all, let’s not use BindingX to get real. OK, let’s get down to business!

How to implement this without BindingX

We write our code directly in the index.jsx file in the SRC /Home directory. First of all, let’s think about it. In fact, it is not difficult to implement this function. The layout is very simple, an outer container with a child container and a left slide container. . When we touchStart, record the x coordinate of the starting point, and then calculate the value of the end point to the starting point when we touchEnd to know the sliding distance, and judge the sliding direction by positive and negative. Finally, we can move the box to the corresponding position.

With that in mind, we can start writing code:

  1. Let’s start with the dependencies we need:
import { createElement, useEffect, useRef } from 'rax';
// The default div container in rax
import View from 'rax-view';
// Text labels in rax
import Text from 'rax-text';
// findDOMNode can be used to get the real DOM element to manipulate the DOM node when the component is already mounted to the DOM.
import findDOMNode from 'rax-find-dom-node';
import transition from 'universal-transition';
// Use rax to monitor gestures.
// onHorizontalPan listens for horizontal swipe gestures
// onVerticalPan listens for vertical swipe gestures
import GestureView from 'rax-gesture-view';
import './index.css';
Copy the code
  1. Second, start layout, here is directly on the code:
  // Store the X coordinates of the starting point
  let startX = useRef(0);
  // Store the x-coordinate difference between the end point and the start point
  let moveX = useRef(0);
  // Store the x-coordinate of the destination
  let endX = useRef(0);
  // Whether to enable animation
  let animation = useRef(true);
  // bind the target move container's ref
  const panBoxRef = useRef(null);

 return (
  <View className="container">
    <View ref={deleteBoxRef} className="deleteBox">
      <Text>Delete</Text>
    </View>
    <GestureView ref={panBoxRef} class="box">
      <View className="head">
        <View className="avatar"></View>
      </View>
      <View className="content">
        <Text className="desc">this is container</Text>
      </View>
    </GestureView>
  </View>
Copy the code

The CSS code:

.container {
  width: 750;
  height: 360;
  flex: 1;
  background-color: #eeeeee;
}
.box {
  width: 750;
  height: 240;
  background-color: #651fff;
}
.head {
  background-color: #651fff;
  width: 750;
  height: 120;
  flex-direction: row;
  align-items: center;
}
.content {
  width: 750;
  height: 240;
  background-color: #651fff;
  padding-left: 24;
  padding-top: 24;
  padding-right: 24;
  box-sizing: border-box;
}

.avatar {
  width: 96;
  height: 96;
  border-radius: 48;
  background-color: #cddc39;
  margin-left: 36;
  margin-right: 48;
}

.deleteBox {
  width: 200;
  height: 360;
  position: absolute;
  right: 0;
  top: 0;
  background-color: red;
}
Copy the code

Next you can see the layout as follows:

<GestureView></GestureView>
<View></View>

<GestureView ref={panBoxRef} onHorizontalPan={handleHorizontalPan} class="box">
  ...
</GestureView>
Copy the code

Next write our handleHorizontalPan and the transitionShowDelete method that controls the animation:

const handleHorizontalPan = (e) => { // console.log(e); if (e.state === 'start') { startX.current = e.changedTouches[0].pageX; } else if (e.state === 'end') { endX.current = e.changedTouches[0].pageX; moveX.current = endX.current - startX.current; if (moveX.current < 0) { transitionShowDelete(true); } else if (moveX.current >= 0) { transitionShowDelete(false); }}}; / * * * the transition of the specific method of use can view the official documentation * https://rax.js.org/docs/api/transition * / const transitionShowDelete = (shouldShowDelete) => {transition(findDOMNode(panboxref.current), {// You can use a percentage or a number transform with RPX units: shouldShowDelete ? `translateX(-${(20 / 75) * 100}%)` : 'translateX(0%)', }, { timingFunction: 'ease-in-out', delay: 0, duration: 300}, () = > {/ / animation execution after the success callback function}); };Copy the code

So here we can print the event parameter e of this method and see what that is?

You’ll notice that E.state stores the states of touchStart, touchMove, and touchEnd: “Start” indicates that the swipe begins, and “end” indicates that the swipe ends. Now we’re done! Here’s how it looks:

However, if you know how WEEx works, you may decide that this is not the best practice. Let’s take a look at the communication process between WEEX and Native.

Previously we said that we can use some native API functions by writing JS code, so how to achieve this? And I’m going to draw a picture to give you a simple answer.

JS layer is responsible for visual control and event processing; Bridge layer is the communication Bridge between JS layer and Native layer, responsible for instruction translation and transmission to Native layer. The Native layer is responsible for visual rendering and event gathering.

From the above figure, we can see that every time we respond to events, perform interactive operations and change UI, we need frequent communication between JS layer and native layer.

For example, if we change the UI during the touch event response, and want the Native layer to do some complex interactive operations, the JS layer will have to constantly deal with the events collected from the Native layer and make timely visual response. If the response is not timely, visual lag will occur. We all know that the average phone’s screen refresh rate is 60fps, which is about as smooth as the human eye can handle, meaning that a refresh needs to be done in 1/60 of a second without feeling stuck. Therefore, if the JS layer completes the drawing of a new view from the event receiving, processing and feedback to Native over 16.67ms, there will be visual lag. Moreover, if each visual update requires a large amount of communication, it will also increase CPU consumption and increase the risk of crash.

So you can imagine that our method above is a case like this: Every touch event is triggered, we need to communicate with native once and then update the view. UI drawing is still not executed in onTouchMove. If it is executed in onTouchMove, the performance will definitely be greatly affected, and the visual drawing will obviously feel stuck. If you don’t believe me, we can try:

If we want to place the animation action in the TouchMove event, we can directly modify the handleHorizontalPan method by using e.state === ‘move’.

const handleHorizontalPan = (e) = > {
  // console.log(e);
  if (e.state === 'start') {
    startX.current = e.changedTouches[0].pageX;
  } else if (e.state === 'move') {
    endX.current = e.changedTouches[0].pageX;
    moveX.current = endX.current - startX.current;
    if (moveX.current < 0) {
      transitionShowDelete(true);
    } else if (moveX.current >= 0) {
      transitionShowDelete(false); }}};Copy the code

Here’s how it looks:

What do you think? You feel a noticeable crunch! So what if I had to update my view every time I touchMove? And that’s where our BindingX comes in!

How to do this with BindingX

We believe that you have a preliminary understanding of what BindingX is. If you still don’t understand the significance of the introduction of BindingX, it doesn’t matter, you can read my explanation next:

As can be seen from the above figure, if we did not introduce BindingX, we would need frequent JS and native communication to render the view during touchMove, and each touchMove would be a performance consumption of communication. If we introduced BindingX, we could inject an expression into the Native layer via the onTouchStart event, which would be parsed at the Native layer and then executed.

For example, if we inject an expression that moves on the X axis, then when the touchStart injection is successful, the Native layer will automatically update the view for us every time we touchMove, and then the view update will return a successful callback to the JS layer.

If you don’t understand, don’t worry, let’s do it now:

NPM I weex-bindingx –save NPM I weex-bindingx –save

import { createElement, useEffect, useRef } from 'rax';
// The default div container in rax
import View from 'rax-view';
// Text labels in rax
import Text from 'rax-text';
// findDOMNode can be used to get the real DOM element to manipulate the DOM node when the component is already mounted to the DOM.
import findDOMNode from 'rax-find-dom-node';
import transition from 'universal-transition';
import Bindingx from 'weex-bindingx';
// Determine the current environment
import { isWeex } from 'universal-env';
import './index.css';
Copy the code

The layout also needs a change:

return (
  <View className="container">
    <View ref={deleteBoxRef} className="deleteBox">
      <Text>Delete</Text>
    </View>
    <View ref={panBoxRef} onTouchStart={handleTouchStart} class="box">
      <View className="head">
        <View className="avatar"></View>
      </View>
      <View className="content">
        <Text className="desc">this is container</Text>
      </View>
    </View>
  </View>
);
Copy the code

The next big thing is our handleTouchStart method:

useEffect(
  (a)= >= > {{}, ()// BindingX unbinds during component destruction
    Bindingx.unbind({
      token: gesToken.current,
      eventType: 'pan'}); } []);// Determine the current environment: Weex and the incoming ref on the Web are different
const getEl = (panBoxRef) = > {
  return isWeex ? findDOMNode(panBoxRef).ref : panBoxRef;
};

const handleTouchStart = (e) = > {
  console.log(e);
  if(! animation.current) {return;
  }
  startX.current = e.changedTouches[0].pageX;
  let ref = getEl(panBoxRef.current);
  // Inject an expression into native. The returned value has a token attribute for BindingX unbinding
  let gesTokenObj = Bindingx.bind(
    {
      // The anchor point of the element to bind
      anchor: ref,
      // Trigger animation event type: 'pan' for touch trigger animation
      eventType: 'pan'.props: [{element: ref, // The anchor point of the element to animate
          property: 'transform.translateX'.// Animation properties
          expression: 'x+0'.// represents moving in the x axis
        },
      ],
    },
    (e) => {
      // The state of e.state is different depending on the state of the injected expression. Here we injected translateX, so e.state === 'end' represents the state after the move, which is when the touchEnd is reached
      console.log(e);
      if (e.state === 'end') {
        moveX.current = e.deltaX;
        animation.current = true;
        if (moveX.current <= - 100.) {
          anchorToCurrent(- 200.);
        } else if (moveX.current > - 100.) {
          anchorToCurrent(0); }}}); gesToken.current = gesTokenObj.token; };// During touchEnd, trigger a rebound effect depending on the distance moved
const anchorToCurrent = (currentWidth) = > {
  let result = Bindingx.bind(
    {
      // Event type: timing executes animation based on time
      eventType: 'timing'.exitExpression: 't>600'.// When the animation ends
      props: [
        {
          element: getEl(panBoxRef.current), // The anchor point of the element to animate
          property: 'transform.translateX'.// Animation properties
          expression: `easeOutElastic(t, ${moveX.current}.${ currentWidth - moveX.current }`, 600),
        },
      ],
    },
    (e) => {
      animation.current = false; }); };Copy the code

So far our optimization is complete, if there are students don’t know where can view BindingX official document, the website also provides a lot of animation difference, can be used for us: alibaba. Making. IO/BindingX/gu…

Here’s how it works:

How, did not feel like dove silky smooth and supple! This is the charm of BindingX to make your WEEX interactive animation silky smooth!

BindingX can be used in many ways, and you should consider using it for any of the following interaction scenarios:

  • Listening to thepanGestures, update the UI.
  • Listen for the onScroll event of a scroll container (such as a List) to update the UI.
  • Monitor device sensor direction changes and update THE UI.
  • The animation. (that is, listen for the screen refresh callback event of every frame on the device to update the UI).

BindingX can also achieve a lot of animation, interactive, website have examples you can also see: alibaba. Making. IO/BindingX /

Weex supplement

Do this aspect of the supplement is mainly to give the novice some coding suggestions, here is a simple list of some attention, want to more detailed can go to the official view of the document. For us programmers, official documentation is the best learning aid

Weex crater

For WEEX we first have to understand weeX some pits, that is, and we usually write code some differences. This is probably a CSS pit for most of you. Here I put together a list of CSS precautions for reference, here is the main WEEX in raX writing method:

  1. Border and background cannot be written in combination and must be edited separately. The background-image attribute and label do not support only the pixel value, but not the relative value. 750 pixels are used as the standard
  2. Weex support position location: relative | absolute | fixed | sticky, the default value is relative
  3. Weex does not support the z-index hierarchy, but the lower element has a higher hierarchy
  4. If the positioned element exceeds the container boundary, the overflow portion is not visible under Android because on the Android side the element overflow defaults to hidden and overflow has no other value.
  5. Background-image supports only linear gradient: Linea-gradient, while radial gradient: radius-gradient is not supported
  6. Weex box-shadow supports only ios
  7. In ios, the image component cannot define one or more border-radius corners. Instead, you can use border-radius to define rounded corners
  8. In Weex, flexbox is the default and only layout model, and each element has the display: Flex attribute by default
  9. Box-sizing of Weex box models is border-box by default
  10. Child elements do not inherit their styles from parent elements, such as color and font size
  11. Do not nest more than 14 levels of containers; otherwise, crashes may occur

Specific can view the official documentation: weex.apache.org/zh/docs/sty…

Weex environment variables

There is an independent Weex variable in the JS context of each Weex page, which can be used like a global variable, but is isolated and read-only across different pages. Environment variables in weeX can be used by WXEnvironment, which is a built-in global variable of WEEX. The main fields are as follows: DeviceWidth deviceHeight deviceHeight deviceWidth deviceHeight deviceHeight

Specific to view the official document: weex.apache.org/zh/docs/api…

At the same time, WEEx has its own built-in document model object. Weex. document is the document model object of the current page, which can be used to create and manipulate elements in the DOM tree. It is part of the Weex DOM API specification, but it is different from the DOCUMENT object in the W3C DOM specification. Let dom = __weex_require__(‘@weex-module/dom’); Introduction. Dom apis include:

  1. ScrollToElement: Scrolls a child node of the list to the current viewport
  2. GetComponentRect: Obtains the bounding Rect layout information for a component
  3. AddRule: Adds font-face rule
  4. GetLayoutDirection: Get the layout direction of a component (RTL, LRT, inherit)

Specific to view the official document: weex.apache.org/zh/docs/mod…