This is the 9th day of my participation in the November Gwen Challenge. See details: The Last Gwen Challenge 2021.


preface

In business requirements, the navigation bar component is a very common user interaction component, and some mainstream frameworks in the market have encapsulated it. But on smaller projects, referring to a framework in order to use a component is killing the goose, or the use of a third party framework is not allowed within the team. At this point, you have to write a component yourself. Here I would like to share the navigation bar component written by the author in the project. Although the capability and extensibility are not as much as the mature framework in the market, it is also enough for daily business scenarios

The preparatory work

Before we start implementing a component, let’s think about what capabilities we need to provide for that component. Since it is a navigation bar, it must provide corresponding data. The second is the click callback event. For now, it seems that passing these two parameters through to the component is enough. Then we can start writing components

Component design

Let’s take a look at the node structure:

<view>
    <! -- Scroll left to control the position of the scroll bar -->
    <! -- scroll with-animation -->
    <scroll-view 
        scroll-x="true" 
        class="nav" 
        scroll-left="{{navScrollLeft}}" 
        scroll-with-animation="{{true}}" 
        style="background: {{background}}">
        <block wx:for="{{navData}}" wx:for-index="idx" wx:for-item="navItem" wx:key="idx">
            <view 
                class="nav-item {{[currentTab == idx ?'active':'', 'nav-item-' + idx]}} "  
                data-current="{{idx}}" 
                bindtap="switchNav" 
                style="color: {{currentTab == idx ? color : ''}}" >
                    {{navItem.text}}
            </view>
        </block>
    </scroll-view>
</view>
Copy the code

Here we can see that the components need to use the displacement value (navScrollLeft), background (background), navigation data (navData), currentTab (currentTab), single click callback (switchNav)

From the analysis of these five parameters, it can be known from experience that except for the displacement value and the currently selected subscript, all the others are imported from the outside. Instead, the displacement value and the currently selected subscript are defined by the component itself, so let’s see what happens to that data within the component

Component({
  properties: {
    navData: Array.cur: Number.color: {
      type: String.value: '#D60935',},background: {
      type: String.value: '#f7f7f7',}},data: {
    currentTab: 0.navScrollLeft: 0
  },
  attached: function () {
    wx.getSystemInfo({
      success: (res) = > {
        this.setData({
          windowWidth: res.windowWidth
        })
      },
    })
  },
  methods: {
    switchNav(event) {  
      let that = this;
      let cur = event.currentTarget.dataset.current;
      const query = that.createSelectorQuery();
      query.select(`.nav-item-${cur}`).boundingClientRect();
      query.selectViewport().scrollOffset();
      query.exec(function(res){
        let offsetLeft = event.currentTarget.offsetLeft;
        that.setData({
          navScrollLeft: (offsetLeft - (that.data.windowWidth/2) + (res[0].width/2))}); })if (that.data.currentTab == cur) {
        return false;
      } else {
        that.setData({
          currentTab: cur
        })
      }
      // Pass the value to the parent component
      that.triggerEvent('myevent', {cur: cur})
    }
  } 
})
Copy the code

Click on the callback

As we mentioned above, the clickback event should be defined by the parent, which seems to be defined within the component. In fact, it registers a click event in the component, because the displacement calculation must be done by the component itself. When the logic inside the component is done, the parent click callback is called.

TriggerEvent (‘myevent’,{cur: cur}) sends an event notification to the parent telling the parent that the click event has occurred

Displacement calculation

Speaking of displacement calculation, here we have to thank the small program to provide the scroll view component, the component’s scroll left property, so that we can use less code to achieve smooth offset effect:

  1. The offset sliding effect is optimized within the component itself
  2. scroll-leftWhen the value exceeds the element’s maximum width, the view offset takes the element’s maximum width down

Thanks to the Scrollview component, we just need to focus on the position relationship between the element itself and the view. The core code is as follows:

let cur = event.currentTarget.dataset.current;
const query = that.createSelectorQuery();
query.select(`.nav-item-${cur}`).boundingClientRect();
query.selectViewport().scrollOffset();
query.exec(function(res){
    let offsetLeft = event.currentTarget.offsetLeft;
    that.setData({
      navScrollLeft: (offsetLeft - (that.data.windowWidth/2) + (res[0].width/2))}); })Copy the code

Currently select subscript

The processing of the current selected subscript is relatively simple. When clicking, the clicked subscript can be transparently transmitted to obtain the current selected subscript

let cur = event.currentTarget.dataset.current;
if (that.data.currentTab == cur) {
    return false;
} else {
    that.setData({
      currentTab: cur
    })
}
Copy the code

It is worth noting, however, that the currently selected subscript value is also passed through by the parent, since we may need to specify the subscript at initialization

How to use

Now that it’s all wrapped up, we just need to introduce and declare components where we need them

<tabarNav 
    id="topNav" 
    bindmyevent="onMyEvent" 
    navData="{{navData}}" 
    cur="{{cur}}">
</tabarNav>
Copy the code

What the input parameters represent is not explained here too much. Those who want to know what the input parameters represent refer to the component design section. Of course, during component design, we defined a number of parameters, only three of which are included here, and the rest can be loaded as needed