airyland / vux

Mobile UI Components based on Vue & WeUI
https://vux.li
MIT License
17.59k stars 3.71k forks source link

iOS的webview不占满屏,当手指滑动到webview之外时,不会触发touchend事件,导致vux的scroller在pulldown时,一直处于下拉状态 #2370

Closed fubai closed 6 years ago

fubai commented 6 years ago
fubai commented 6 years ago

这个问题不是vux的bug,也不是vux scroller组件所依赖的xscroll的bug,这是iOS的bug。

把issue发在这里,是因为我是在使用vux的scroller组件时碰到的这个iOS bug,而且解决方式也是vux(vue)的方式。

解决方法如下:

1.在main.js中,通过store维护当前正在pulldown的scroller

let store = new Vuex.Store({
  state: {
    scrollerEl: null // 正在下拉的scroller pulldown的el元素
  },
  mutations: {
    scrollerEl (state, v) {
      state.scrollerEl = v;
    }
  }
});

2.在main.js中,监听touchmove事件,

if (!!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)) {//iOS
  function triggerTouchend(e) {
    if (!e || !store.state.scrollerEl) {
      return;
    }

    // 手动分发一个touchend事件
    store.state.scrollerEl.dispatchEvent(new TouchEvent('touchend', {
      touches: e.touches,
      targetTouches: e.targetTouches,
      changedTouches: e.changedTouches,
      ctrlKey: e.ctrlKey,
      shiftKey: e.shiftKey,
      altKey: e.altKey,
      metaKey: e.metaKey,
      detail: e.detail,
      view: e.view,
      sourceCapabilities: e.sourceCapabilities,
      bubbles: e.bubbles,
      cancelable: e.cancelable,
      composed: e.composed
    }));
  }

  let started = false,
    timeoutId,
    diffX = 39;//tabbar的高度

  document.addEventListener('touchstart',  function(e) { started = true;  }, false);
  document.addEventListener('touchend',    function(e) { started = false; }, false);
  document.addEventListener('touchcancel', function(e) { started = false; }, false);

  document.addEventListener('touchmove', function(e) {
    if (timeoutId) {
      clearTimeout(timeoutId);
      timeoutId = null;
    }

    if (e.changedTouches[0].pageY >= (document.body.clientHeight + diffX)) {//这种情况是,用户下拉滑动,滑过了webview和tabbar,滑倒了物理屏幕之外
      e.preventDefault();
      e.stopPropagation();// 结束事件的传播,保证scroller不再处理
      triggerTouchend(e);
    } else if (e.changedTouches[0].pageY >= document.body.clientHeight) {//这种情况是,用户下拉滑动,停留在了tabbar之上
      if (started) {//在tabbar上停止不动,超过40ms则手动结束滑动
        timeoutId = setTimeout(function() {
          triggerTouchend(e);
        }, 40);
      } else {//在tabbar上停止不动,超过40ms被手动结束滑动后,又继续滑动
        e.preventDefault();
        e.stopPropagation();// 结束事件的传播,保证scroller不再处理
      }
    }
  }, {
    capture: true, // 在捕获阶段处理事件,保证在scroller之前处理事件
    passive: false // 保证e.preventDefault()生效
  });
}
  1. 在使用了scroller的组件内,监听pulldown的状态。保证当前正在pulldown的scroller,存储在store中,反之,保证store中没有当前scroller
    data () {
    return {
      status: {
        pulldownStatus: 'default'
      }
    };
    },
    watch: {
    'status.pulldownStatus' (to, from) {
      this.$store.commit('scrollerEl', to === 'default' ? null : this.$refs.scroller._xscroll.renderTo);
    }
    }

另外,scroller代码大致如下:

<scroller v-model="status" lock-x :scrollbar-x=false :scrollbar-y=false height="100%" style="width:100%;" ref="scroller" :use-pulldown="true" @on-pulldown-loading="onPullDown" :pulldown-config="{height:60,autoRefresh:false,downContent:'↓ 下拉刷新',upContent:'↑ 松开立即刷新',loadingContent:'加载中...'}">
fubai commented 6 years ago

我知道这个issue提在这里不合适,还请原谅,只希望给遇到相同bug的童鞋一个帮助。

wg5945 commented 6 years ago

thx,现在已经不建议使用Scroller组建了,可以使用第三方组件替代,例如better-scroll