h5 page route switching animation - left and right sideslip effect (v2)

Posted by mattsinclair on Fri, 12 Nov 2021 08:40:05 +0100

The previous article described in detail the implementation idea and code of v1 version: h5 page route switching animation - left and right sideslip effect (v1)

Later, many problems in v1 were found in the process of practice and have been reconstructed. Therefore, let's share a new implementation method, which is called v2 version. The core idea is the same.

1, Review and reflection

The v1 version stores the page history and page direction through vuex, obtains the route information to jump through the beforeEach global route navigation guard, and compares and judges the page direction.

Then I thought:

  • The reason why we use vuex storage instead of ordinary js module storage is that the change of page direction data in vuex can trigger the response, so that the switching animation can change immediately according to the page direction;
  • The data in vuex is only used by the page switching module. Can it be moved into the encapsulated page switching component PageSlide;
    If the handlePageDirection method in the component is moved in, it cannot be called in the route navigation guard beforeEach, and the purpose of the call is to pass the route information of the next page;
  • Is there any other way to get the route of the next page? Yes, just watch and listen to $route, because the route exit is to be wrapped in this component.

Then, after the transformation, a component implements all the code logic.

2, Other optimization points

1. Reduced routing stack data

In v1, the routeStack data of the routing stack stores the complete data of all routing routes. In fact, the current function only uses the data of path, so only path is stored, and the deep cloning operation of JSON.parse(JSON.stringify(route)) is subtracted.

pushRouteStack (route) {
  const { path } = route
  this.routeStack.push({ path })
},

2. Support page refresh

vuex stored in v1 will be initialized and emptied after the page is refreshed, so the routeStack data will be synchronously stored in sessionStorage this time, and read from sessionStorage after the page is refreshed.

created () {
  this.routeStack = this.getSessionRouteStack()
},

 pushRouteStack (route) {
  const { path } = route
  this.routeStack.push({ path })
  this.setSessionRouteStack()
},
popRouteStack () {
  this.routeStack.pop()
  this.setSessionRouteStack()
},

3. Compatible with vue3.0

At a glance, the only incompatible part of vue3.0 should be the transition class name:
Modify class enter to class enter from;
Modify class leave to class leave from.

Then write it all:

.slide-left-enter,
.slide-left-enter-from,
.slide-right-leave-active {
  opacity: 0;
  transform: translate(@distanceX, 0);

}
.slide-left-leave-active,
.slide-right-enter,
.slide-right-enter-from {
  opacity: 0;
  transform: translate(-@distanceX, 0);
}

For more vue3.0 upgrades, please refer to: Project upgrade vue3.0 experience summary

4. Reconstruct page direction judgment logic

This version reconstructs the judgment logic of page forward and backward, considers the processing of more detailed scenarios, and stores the home page route by default at the beginning of the route stack:

handleRouteChange (to) {
  const len = this.routeStack.length
  const currentRoute = this.routeStack[len - 1]
  // Determine whether to refresh the route
  if (currentRoute.path !== to.path) {
    if (len === 1) {
      // Open the non home page route for the first time and move forward
      this.setPageForward(to)
    } else {
      // len > 1
      const lastRoute = this.routeStack[len - 2]
      if (lastRoute.path === to.path) {
        // Open the previous route and go back
        this.setPageBack(currentRoute)
      } else {
        if (to.path === this.indexPagePath) {
          // Open the home page halfway, go back and reset the routing stack
          this.setDirectionName('slide-right')
          this.resetRouteStack()
        } else {
          // Routine, forward
          this.setPageForward(to)
        }
      }
    }
  }
},

3, Complete code encapsulation

1. Encapsulated component PageSlide

<template>
  <transition :name="directionName">
    <slot></slot>
  </transition>
</template>

<script>
export default {
  name: 'PageSlide',
  props: {
    // Home routing path
    indexPagePath: {
      type: String,
      default: '/',
    },
  },
  data () {
    return {
      directionName: '', // Page switching direction: slide left slide forward, slide right slide backward,
      routeStack: [], // Store routing stack
    }
  },
  watch: {
    '$route' (val) {
      this.handleRouteChange(val)
    }
  },
  created () {
    this.routeStack = this.getSessionRouteStack() || [{ path: this.indexPagePath }]
  },
  methods: {
    handleRouteChange (to) {
      const len = this.routeStack.length
      const currentRoute = this.routeStack[len - 1]
      // Determine whether to refresh the route
      if (currentRoute.path !== to.path) {
        if (len === 1) {
          // Open the non home page route for the first time and move forward
          this.setPageForward(to)
        } else {
          // len > 1
          const lastRoute = this.routeStack[len - 2]
          if (lastRoute.path === to.path) {
            // Open the previous route and go back
            this.setPageBack(currentRoute)
          } else {
            if (to.path === this.indexPagePath) {
              // Open the home page halfway, go back and reset the routing stack
              this.setDirectionName('slide-right')
              this.resetRouteStack()
            } else {
              // Routine, forward
              this.setPageForward(to)
            }
          }
        }
      }
    },
    // forward
    setPageForward (route) {
      this.setDirectionName('slide-left')
      this.pushRouteStack(route)
    },
    // back off
    setPageBack () {
      this.setDirectionName('slide-right')
      this.popRouteStack()
    },
    // Set page orientation
    setDirectionName (name) {
      this.directionName = name
    },
    // Reset
    resetRouteStack () {
      this.routeStack = [{ path: this.indexPagePath }]
      this.setSessionRouteStack()
    },
    pushRouteStack (route) {
      const { path } = route
      this.routeStack.push({ path })
      this.setSessionRouteStack()
    },
    popRouteStack () {
      this.routeStack.pop()
      this.setSessionRouteStack()
    },
    setSessionRouteStack () {
      sessionStorage.setItem('routeStack', JSON.stringify(this.routeStack))
    },
    getSessionRouteStack () {
      const str = sessionStorage.getItem('routeStack')
      if (!str) {
        return null
      }
      let val = []
      try {
        val = JSON.parse(str)
      } catch (error) {
        console.warn('parse routeStack wrong')
      }
      return val
    },
  }
}
</script>

<style lang="less" scoped>
@distanceX: 100%;

.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
  position: absolute;
  width: 100%;
  transition: all 0.3s cubic-bezier(0.55, 0, 0.1, 1);
}

.slide-left-enter,
.slide-right-leave-active {
  opacity: 0;
  transform: translate(@distanceX, 0);

}
.slide-left-leave-active,
.slide-right-enter {
  opacity: 0;
  transform: translate(-@distanceX, 0);
}
</style>

2. App.vue reference

<template>
  <div id="app">
    <PageSlide indexPagePath="/home">
      <router-view />
    </PageSlide>
  </div>
</template>

<script>
import PageSlidefrom '@/components/PageSlide'

export default {
  name: 'App',
  components: {
    PageSlide,
  },
}
</script>
  • The indexPagePath parameter is the path of the project home page route. Replace it with the path of your own project.

4, react implementation guidelines

No matter v1 or v2, vue or react, the core idea remains unchanged, that is:
Store the route access history data, obtain the route data of the next page, compare and judge the page direction.

The implementation of react mainly needs to solve the following problems:

  • 1. Transition Animation: you can react-transition-group realization.
  • 2. Get the routing data of the next page: you can use the withRouter To achieve.
  • 3. Listen for route changes:
    • With the above withRouter, when the props.location changes, the componentWillReceiveProps hook will be executed and can be used as a listening function;
    import { withRouter } from 'react-router-dom'
    class Main extends Component {
      constructor (props) {
        super(props)
        this.state = {}
      }
      componentWillReceiveProps (nextProps) {
      	console.log(nextProps.location)
      }
      render() {
        return (
            <div>demo</div>
        )
      }
    }
    export default withRouter(Main)
    
    • hooks syntax is simpler. useEffect can directly respond to routing changes.
    const Demo = function (props) {
      useEffect(() => {
        console.log(props.location)
      }, [props.location])
    }
    

The rest of the logic is almost the same as vue. Implement it yourself.

Topics: Javascript Front-end React Vue.js