Vue实现内部组件轮播切换效果的示例代码(5)
这个问题还比较好处理,另外一个不太好处理的问题是:动画的时间是0.5s,如果用户点下一题的速度很快在0.5s之内,上面的代码执行就会有问题,会导致数据错乱。如果每次切到下一题之后按钮初始化都是disabled,因为当前题还没答,只有答了才能变成可点状态,可以保证0.5s的时间是够的,那么就可以不用考虑这种情况。但是如果需要处理这种情况呢?
3. 解决点击过快的问题
我想到两个方法,第一个方法是用一个sliding的变量标志当前是否是在进行切换的动画,如果是的话,点击按钮的时候就直接更新数据,同时把setTimeout 0.5s的计时器清掉。这个方法可以解决数据错乱的问题,但是切换的效果没有了,或者是切换到一半的时候突然就没了,这样体验不是很好。
第二个方法是延后切换,即如果用户点击过快的时候,把这些操作排队,等着一个个做切换的动画。
我们用一个数组表示队列,如果当前已经在做滑动的动画,则入队暂不执行动画,如下代码所示:
methods: { nextQuestion() { this.currentIndex = (this.currentIndex + 1) % this.questions.length; // 把currentIndex插到队首 this.slideQueue.unshift(this.currentIndex); // 如果当前没有滑动,则执行滑动 !this.sliding && this._slideToNext(); }, }
每次点击按钮都把待处理的currentIndex插到队列里面,如果当前已经在滑动了,则不立刻执行,否则执行滑动_slideToNext函数:
_slideToNext() { // 取出下一个要处理的元素 let currentIndex = this.slideQueue.pop(); // 下一个slide的类型 let nextType = currentIndex % 2 ? "odd" : "even"; let $nextSlide = this.$refs[`${nextType}Task`].$el; $nextSlide.style.display = "block"; setTimeout(() => { $nextSlide.style.transform = "translateX(0)"; this.sliding = true; setTimeout(() => { this._updateData(currentIndex); // 如果当前还有未处理的元素, // 则继续处理即继续滑动 if (this.slideQueue.length) { // 要等到两个component的DOM更新了 this.$nextTick(this._slideToNext); } else { this.sliding = false; } }, 500); }, 0); },
这个函数每次都先取出当前要处理的currentIndex,然后接下来的操作和第2点提到的一样,只是在0.5s动画结束后的异步回调里面需要判断一下,当前队列是否还有未处理的元素,如果有的话,需要继续执行_slideToNext,直到队列空了。这个执行需要挂在nextTick里面,因为需要等到两个component的DOM更新了才能操作。
这样理论上就没问题了,但实际上还是有问题,感受如下: