vue开发webApp时所遇到的问题整理

背景

因为新项目是基于 cordova 打包出来的 app,其实内部页面都是通过 webapp 实现,所以近期先将部分遇到的问题整理一下。

既然最后打包成 app,做为开发人员肯定想在动效实现上和原生相近,所以在我做的过程中发现了如下几个问题。

问题(坑)

ios swiper left or right

在 safari 中页面可以通过左划来返回上一页,右划前往之前返回的下一页,这个功能在你没有使用 transition 的时候是完全没有问题的,但是原生 app 那么多有页面切换的过渡效果,我们肯定也不能少啊,所以在我使用 transition 后我发现一个问题,原生 app 中左划右划后是不是有动画的,道理也是很明显的,因为你左划右划的时候页面都是完全直接展示的,这个如果如果有动画就意味着页面会消失然后再出现,这个体验就不是很好了。

但是如果我们使用了 transition 后会出现上述的情况,那么我们怎么解决呢?

我们可以通过监听页面中的 touch 事件来判断当前页面是否会通过左划来返回上一页。

主要的想法就是记录左划的距离,如果这个距离超过了当前页面的一半宽度那么就会返回上一页,这个时候我们要关掉上一页的 transition 效果。

那么这里就有几个坑要解决了,首先设备判断左划这个操作需要一定的条件,你不可能手放上去划一点点页面就跟着滑动了,不然页面肯定很不稳定,因为一点点的触碰都会导致左划。其次如果关闭上一个的 transition。

// 记录最开始的x坐标
let startX = -1;
// 储存vue实例,在左划结束的时候判断是否会返回上一页,同时将结果存储
// 然后在父组件中监听$route,每次路由变化的时候去获取那个值
// 从而判断是否执行transition效果
let vue = null;

export function SafariSlideLeftTwiceTransitionAddLis(vm) {
  if (!vue) vue = vm;
  document.addEventListener("touchstart", watchTouchStart);
  document.addEventListener("touchend", watchTouchEnd);
}

export function SafariSlideLeftTwiceTransitionRemoveLis() {
  document.removeEventListener("touchstart", watchTouchStart);
  document.removeEventListener("touchend", watchTouchEnd);
}

function watchTouchStart(e) {
  console.log("touchstart");
  // 首先要满足左划的第一个条件就是pageX在页面的34px以内,
  // 就是你touchstart的触电不能大于这个x,
  // 这个数据也是我测试多次得到的,可能不是最精确的。
  startX = e.pageX < 34 ? e.pageX : -1;
}

// 因为我只需要最后的点,所以不需要关心touchmove中的过程
function watchTouchEnd(e) {
  if (startX > -1 && e.pageX < 0) {
    console.log("touchend");
    const width = window.screen.width;
    // 满足左划的条件之二
    // 你只有先往左划了16px之后,浏览器才会认为你要通过左划返回上一页了
    // 同样数据可能不是最精确的。
    const dis = width + e.pageX - startX - 16;
    if (dis > width / 2) {
      vue.$store.commit("SET_TRANSITION", false);
    }
  }
}

然后你只要在相关页面导入这两个函数,并监听一下,记得在 beforeDestroy 中解除一下。然后在父组件中监听一下路由变化就可以实现这个功能了。

但是随之而来又出现了一个问题,那就是这个时候 safari 记住的是你上次离开这个页面时的状态,所以如果你是通过点击按钮来跳转下一页的话,这个时候你左划后那个页面会出现被点击的背景色,这也是个坑,但是你可以设置 a 标签-webkit-tap-highlight-color: transparent;来取消这个背景色,但是相对的你点击这个按钮的时候也没有背景色了。

评论功能

首先在开发前让我们来看看原生 app 上的评论功能是咋样的。

首先当你点击 input 输入框后,键盘弹起,背景有个灰色的遮罩,同时不能滚动页面,而且当前页面也不会滚动。看原生 app 的效果就觉得真的简单,啥都不用动就弹个键盘加层遮罩就完事了对不对。

现在让我们来看看浏览器里实现上述操作的时候会发现啥。

假设我们使用的是 fixed 固定底端的 input

首先出现的是 input 被遮挡问题,在每次切换 safari chrome 后第一次唤起会出现这个问题,之后都不会。
其次每次唤起键盘页面都会上移,并且键盘去掉的时候页面不会上移回到之前的位置。

那么我们来解决这几个问题:

首先 input 被遮挡的问题主要是第一次点击的时候浏览器获取到的 window.innerHeight 比正常的大,导致 input 被遮挡,但是其实没过多少时间浏览器就会获取到正常大值,所以我们只需要在 input 的 click 事件中添加个 settimeout 事件,里面执行 scrollIntoView 即可,scrollIntoViewIfNeeded 可执行可不执行。

而第二个问题,页面上移我发现完全避免不了的,我能做的就是在关闭键盘后讲页面返回原来的位置。

        let top = 0
        let bodyEl = document.querySelector('body');

        inputFocus(el) {
            if (this.disScroll) return;
            this.scrollY = window.scrollY;
            this.disScroll = true;

            top = window.scrollY;
            bodyEl.style.position = 'fixed';
            bodyEl.style.top = -top + 'px';

            setTimeout(() => {
                el.target.scrollIntoView(true);
                el.target.scrollIntoViewIfNeeded();
            }, 200);
        },
        inputBlur() {
            this.disScroll = false;
            bodyEl.style.position = '';
            bodyEl.style.top = '';
            window.scrollTo(0, top);
        }

这个方案也有个问题,那就是在 safari 下,input 和键盘之间会有一段空隙,这段空隙就是 safari toolbar 的高度。如果打包成 app 后应该是没有这个问题了,但是 web 调试的时候看起来很别扭。

使用 flex 布局将 input 置于底部

同样会出现 input 遮挡问题,页面会上移,但是键盘去掉后会下移回到之前的位置。
中间内容块的滚动卡顿。

下面的问题解决方法: 遮挡问题还是通过上面那个方法,滚动卡顿可以通过设置-webkit-overflow-scrolling: touch

但是如果你使用 settimeout,但是会出现键盘和 input 之间有空隙,不过你可以通过之前 fixed 固定 body 的方法来消除。

虽然在 iphone6s 会出现这些问题,但是我今天换了 iphonexs 后,这些问题都复现不了了!!

总结

最后我推荐使用第二种方法,因为我感觉兼容性比较好,而且使用 fixed 固定 body 后在 ios 下也不会出现键盘和 input 的空隙。
但是第二种方法我发现会出现滚动穿透的问题。原生的 scroll 是真的坑啊(仅在 safari 下),这个穿透简直了!!!!

最后我用 better-scroll 来代替了原生的 scroll,就没有滚动穿透了。

blog comments powered by Disqus
目 录