vue-router 手势滑动触发返回

   vue-router的路由变换只存在“变换前”和“变换后”,不存在“切换中”的状态,所以做不到大多数app(微信那样的)在滑动过程中让界面跟随手指移动。但滑动事件还是可以监听的,我们可以在滑动之后再触发路由回退事件。

  微博的滑动返回基本上就是这样的原理:先滑动、再触发返回事件,但用起来很是怪异,有严重的滞后感。夸克浏览器做的就比较好:一是滑动时界面虽然不动,但是界面上有小图标提示,能让用户接受到反馈;二是返回过程很快,没有多余的过渡动画。

  app.vue文件如下:

  1 <template>
  2     <div id="app" v-on:touchstart="bodyTouchStart" v-on:touchmove="bodyTouchMove" v-on:touchend="bodyTouchEnd">
  3         <transition :name="direction">
  4             <keep-alive include="home">
  5                 <router-view class="appView"></router-view>
  6             </keep-alive>
  7         </transition>
  8     </div>
  9 </template>
 10 
 11 <script>
 12 var swidth = document.documentElement.clientWidth;
 13 
 14 export default {
 15     name: 'app',
 16     data: () => ({
 17         // direction 页面切换的过渡动画,配合transition组件使用
 18         direction: "slide-left",
 19         // touchLeft 划动起点界限,起点在靠近屏幕左侧时才有效
 20         touchLeft: swidth*2/5,
 21         // touchStartPoint 记录起始点X坐标
 22         touchStartPoint: 0,
 23         // distance 记录划动的距离
 24         distance: 0,
 25         // 回退按钮的dom,根据页面上是否存在此dom来判断该路由是否可回退
 26         backBtn: null
 27     }),
 28 
 29     watch: {
 30         // 监听路有变化,决定页面过渡动画
 31         $route(to, from) {
 32             if (from.name == "login" || from.path.indexOf("home") > -1) {
 33                 this.direction = "slide-left";
 34             } else if (to.path.indexOf("home") > -1) {
 35                 this.direction = "slide-right";
 36             } else {
 37                 const toDepth = to.path.split("/").length;
 38                 const fromDepth = from.path.split("/").length;
 39                 this.direction = toDepth < fromDepth ? "slide-right" : "slide-left";
 40             }
 41         }
 42     },
 43 
 44     methods: {
 45         bodyTouchStart: function(event) {
 46             this.backBtn = document.getElementById("navback");
 47             if (this.backBtn) {
 48                 // 获得起点X坐标,初始化distance为0
 49                 this.touchStartPoint = event.targetTouches[0].pageX;
 50                 this.distance = 0;
 51             }
 52         },
 53         bodyTouchMove: function(event) {
 54             if (this.backBtn && this.touchStartPoint < this.touchLeft) {
 55                 // 只监听单指划动,多指划动不作响应
 56                 if (event.targetTouches.length > 1) {
 57                     return;
 58                 }
 59                 // 实时计算distance
 60                 this.distance = event.targetTouches[0].pageX - this.touchStartPoint;
 61                 // 根据distance在页面上做出反馈。这里演示通过返回按钮的背景变化作出反馈
 62                 if (this.distance > 0 && this.distance < 100) {
 63                     this.backBtn.style.backgroundPosition = ((this.distance - 100) / 100) * 50 + "px 0";
 64                 } else if (this.distance >= 100) {
 65                     this.backBtn.style.backgroundPosition = "0 0";
 66                 } else {
 67                     this.backBtn.style.backgroundPosition = "-50px 0";
 68                 }
 69             }
 70         },
 71         bodyTouchEnd: function(event) {
 72             if (this.backBtn && this.touchStartPoint < this.touchLeft) {
 73                 // 划动结束,重置数据
 74                 this.touchStartPoint = 0;
 75                 this.backBtn.style.backgroundPosition = "-50px 0";
 76                 // 当划动距离超过100px时,触发返回事件
 77                 if (this.distance > 100) {
 78                     // 返回前修改样式,让过渡动画看起来更快
 79                     document.getElementById("app").classList.add("quickback");
 80                     this.$router.back();
 81                     setTimeout(function(){
 82                         document.getElementById("app").classList.remove("quickback");
 83                     },250)
 84                 }
 85             }
 86         }
 87     }
 88 }
 89 </script>
 90 
 91 <style>
 92 #app {
 93     -webkit-font-smoothing: antialiased;
 94     -moz-osx-font-smoothing: grayscale;
 95     width: 100%;
 96     overflow-x: hidden;
 97 }
 98 .appView {
 99     position: absolute;
100     width: 100%;
101     background: #fff;
102     min-height: 100vh;
103     transition: transform 0.24s ease-out;
104 }
105 #app.quickback .appView{
106     transition-duration: 0.1s;
107 }
108 .slide-left-enter {
109     transform: translate(100%, 0);
110 }
111 .slide-left-leave-active {
112     transform: translate(-50%, 0);
113 }
114 .slide-right-enter {
115     transform: translate(-50%, 0);
116 }
117 .slide-right-leave-active {
118     transform: translate(100%, 0);
119 }
120 </style>

 

 代码案例见 https://github.com/yource/VueSPA

posted @ 2018-08-22 11:51  前端大兵  阅读(5353)  评论(0编辑  收藏  举报