A,

In the project, it is often encountered that the scrollbar will appear in the part beyond the area. The scrollbar can be controlled up and down by the mouse wheel on the PC side, and the page can be dragged by the mouse to scroll on the mobile side. These two scenarios are in line with user habits, but such scrollbar is generally vertical. If the horizontal scroll bar appears on the PC, you can only drag the scrollerbar button with the mouse to show the scroll area without doing any processing, and for the sake of aesthetics, generally the scroll bar will be styled or hidden, so the horizontal area cannot be rolled by default.

Second, description,

In order to solve the problem, the scrolling area on THE PC side can be directly rolled by dragging the scrolling area with the mouse, as shown in the figure

Rolling examples use the following points:

  • Vue-cli3 + IScroll. js combination is adopted.
  • Using vUE custom instructions to achieve iscroll instantiation and parameter configuration;
  • Realize the linkage between horizontal and vertical scrolling areas;
  • Implement the difference between a horizontal scrollbar centered display and using the scrollIntoView() method

3. Custom instruction V-iscroll

1. Create a command file

Here, use the vue custom command to initialize iscroll instance, create viscroll. js under vuE-cli3 project directory, the file code is as follows:

const IScroll = require('iscroll')
const VIScroll = {
  install: function (Vue, options) {
    Vue.directive('iscroll', {
      inserted: function (el, binding, vnode) {
        let callBack
        letiscrollOptions = options <! -- Option, instance--> const option = binding.value && binding.value. Option const func = binding.value && Binding. Value. instance // Determine the input parameter const optionType = option? [].toString.call(option) : undefined const funcType = func ? [].toString.call(func) : undefined // Compatible Google browser drag el.addeventListener ('touchmove'.function(e) {e.preventDefault()}) // Configure the parameters to New IScroll(EL, iscrollOptions)if (optionType === '[object Object]') {
          iscrollOptions = option
        }
        if (funcType === '[object Function]'Const myScroll = new iscroll (const myScroll = new iscroll) {callBack = func} const myScroll = new iscroll (const myScroll = new iscroll (const myScroll = new iscroll);'#wrapper'Vnode. scroll = new IScroll(EL, iscrollOptions) // If the directive passes the function in, pass the IScroll instanceif (callBack) callBack(vnode.scroll)
      },
      componentUpdated: function(el, binding, vnode, oldVnode) {// Bind scroll to new vnode, Avoid binding vnode.scroll = oldvNode. scroll // use setTimeout to make refresh skip to the end of the event stream to ensure that data has been updated during refreshsetTimeout(() => {
          vnode.scroll.refresh()
        }, 0)
      },
      unbind: function(el, binding, vnode, Scroll = oldvNode.scroll vnode.scroll. Destroy () vnode.scroll = null}})}} module.exports = VIScrollCopy the code

Here attach iscroll.js 5 official document address, iscroll NPM package address, related properties and methods to view themselves.

2. Load the reference directive

First load the directive in main.js:

import Vue from 'vue'
import App from './App.vue'
import "./assets/reset.css"// Load the scroll directive import VIscroll from'./directive/vIscroll'
Vue.use(VIscroll)
Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

Copy the code

The following code is extracted from the tablist. vue component:

<template>
  <div class="tab-container">
    <div
      class="scroll-container"
      v-iscroll="{ option: iscrollConf, instance: getIscroll }"
      ref="scrollContainer"
    >
      <ul
        class="tab-li-container"
        ref="tabLiContainer"
      >
        <li
          class="tab-li-item"
          v-for="(item, index) in list"
          :key="item.id"
          :id="item.id"
          ref="tabItem"
          @click="tabEvent(item, index)"
        >
          <div
            class="item"
            :class="{ 'item-active': currentId == item.id }"
          >{{item.num}}</div>
        </li>
      </ul>
    </div>
    <div
      class="tab-left"
      @click="tabBtnEvent('left')">&lt; </div> <div class="tab-right"
      @click="tabBtnEvent('right')">&gt; </div> </div> </template> <script>export default {
  props: ['list'].data () {
    return {
      iscrollConf: {
        bounce: true,
        mouseWheel: true,
        click: true,
        scrollX: true,
        scrollY: false
      },
      currentId: null,
      currentIndex: 0,
      myScroll: null
    }
  },
  mounted () {
    this.$refs.tabLiContainer.style.width = this.$refs.tabItem[0].offsetWidth * this.list.length + 'px'
    this.$nextTick(() => { this.myScroll.refresh() }) }, methods: { tabEvent (item, currentIndex) { <! }, tabBtnEvent (direction) {<! }, getIscroll (iscroll) {this.myScroll = iscroll}, watch: {list: { handler (l) { this.currentId = l[0].id }, immediate:true,
      deep: true</script> <style scoped>Copy the code

The v-iscroll directive in the above code passes two field parameters:

  • Option: Sets the iscroll parameter. Note that scrollX and scrollY represent horizontal or vertical scrolling.
  • Instance: invocation of the callback method, which is executed in viscroll.js, to get the iscroll instance from the component method getIscroll().

3. Scrolling up and down area linkage

The above code can solve the problem in the opening scene. Now the upper and lower regions are linked. Click a button in the horizontal scroll bar to make it selected, and then the item corresponding to the vertical scroll bar jumps to the first place, as shown in the following figure:

3-1. Linkage implementation method

Click button method:

tabEvent (item, currentIndex) { this.currentId = item.id this.currentIndex = currentIndex <! The button is always in the center of the display. <! Pass to the vertical scroll component --> this.$emit("switchTab", this.currentId, this.currentIndex)
},
Copy the code

The code part of the vertical scroll area component [app.vue] is as follows, and the switchTab() method is commented in detail:

<template>
  <div id="app">
    <TabList
      :list="list"
      @switchTab="switchTab"></TabList> <! -- v-iscroll="defalutOption" -->
    <div
      v-iscroll="{ option: defalutOption, instance: getIscroll }"
      class="tab-content-container"
      ref="detailItemContainer"
    >
      <ul class="tab-list-container">
        <li
          v-for="item in list"
          :key="item.id"
          class="list-item"
          ref="detailItem"
        >
          <div>{{item.value}}</div>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import TabList from './components/tabList.vue'

export default {
  name: 'App',
  components: {
    TabList,
  },
  data () {
    return {
      list: [
        { id: 1, value: 'This is problem number one.', num: 1 }, <! -... {id: 16, value:'This is problem 16.', num: 16 }
      ],
      defalutOption: {
        bounce: true,
        mouseWheel: true,
        click: true,
        scrollX: false,
        scrollY: true}, myScroll: null } }, methods: { switchTab (currentId, currentIndex) { <! Const offsetTop = this; const offsetTop = this;$refs.detailItem[currentIndex].offsetTop <! MaxScrollY --> const y = offsetTop >= math.abs (this.myScrolly)? MaxScrollY --> const y = offsetTop >= math.abs (this.myscrolly) this.myScroll.maxScrollY : -offsetTop <! --> this.myscrollto (0, y)}, <! GetIscroll (iscroll) {this.myScroll = iscroll}}} </script> <style scoped> <! -- Style -->... </style>Copy the code

This is the use of iscroll plugin’s own properties and methods to determine the scrolling boundary and scrolling, more convenient than using JavaScript methods, and the use of IScroll as a scrolling container, has disabled the relevant browser default events in viscroll.js.

3-2, center display

Here JavaScript has a scrollIntoView() method, the official document link, that scrolls the current element into the viewable area of the browser window. The key drawback is that if you use this method for both horizontal and vertical scrolling, only one scrolling area will work and the other will not. The scrollIntoView() method is configured as follows:

this.$refs.tabItem[this.currentIndex].scrollIntoView({
    behavior: "smooth",
    inline: "center",
    block: 'nearest'
})
Copy the code

Here, a pair of left and right buttons are added in the horizontal scrolling area to realize the switching function, as shown in the figure:

The switch button event method realizes the switch function by changing the subscript of the previous button and the next button and calling the click button method. The switch event method logic is as follows:

tabBtnEvent (direction) {
  const max = this.$refs.tabItem.length
  if (direction === 'left' && this.currentIndex > 0) {
    this.currentIndex--
  }
  if (direction === 'right'&& this.currentIndex < max - 1) { this.currentIndex++ } <! Call button click event --> this.tabEvent(this).$refs.tabItem[this.currentIndex], this.currentIndex)
},
Copy the code

Add centering logic for button click event below, detailed code and parsing diagram are shown below, you can check by comparison:

TabEvent (item, currentIndex) {this.currentid = item.id this.currentIndex = currentIndex That's the middle point const scrollContainerHalfWidth = this.$refs. ScrollContainer. OffsetWidth / 2 / / for a single item of half the length of the const tabItemHalfWidth = this.$refs.tabItem[currentIndex].offsetwidth / 2 Const halfDistance = scrollContainerHalfWidth - tabItemHalfWidth // The offset from the total length of the current item, const currentItemOffsetLeft = this.$refs.tabItem[currentIndex].offsetLeft // Scroll to the middle value const x = halfDistance-currentitemoffsetLeft this.myScroll.scrollTo(x, 0) this.$emit("switchTab", this.currentId, this.currentIndex)
},
Copy the code

4, summarize

1, the whole instance is using iscroll plug-in related attributes to achieve scrolling, to avoid the use of JavaScript methods caused by the code confusion; 2. The method of using custom instructions effectively avoids the code redundancy brought by traditional instantiation of IScroll, making it convenient and concise; 3. In this example, the scrolling options are all strings. In case of images, the iscroll.refresh() method is reasonably used to recalculate the scrolling area at the correct time to avoid the scrolling boundary limitation. 4, attach the source code address vue using iscroll.js to solve the PC side rolling problem