需求:场景类似驾校刷题,手指从左往右大幅度滑动切换至上一题,手指从右往左大幅度滑动切换至下一题。已答题目直接显示正确与否和答案状态,所选答案不存在后端,后端只保存该题是否答过、答对还是答错的状态。获取题目时避免多次前后端交互,故做成分页形式:交互一次获取10道题目。

思考:直接做切换页面虽然效果正确,但是相邻题作答状态无法保留。原本想过使用uView轮播图swiper或者类型组件来做,但是实现后出现两个问题:1.轮播一般是针对图片,且切换时可在两个轮播对象间停留,切换时中间状态可见,即会出现两道题都显示一部分的状态,用户体验感不佳;2.轮播必须事先存在页面元素,即至少页面上要写3个相同的轮播元素,再根据切换更换数据,页面效率不佳。所以,在既想完成滑页效果且不能看见中间状态,又不想浪费代码篇幅给重复的滑块元素而是直接做数据的切换,才最终选择了利用动画。

做法:给中间题目view添加动画实例和touch事件,当符合滑动条件则开启滑动动画,并在动画过程中切换题目数据。

动画官网API:uni-app官网

尤其要注意“.step()”的使用,如果两个动画写在一起表示动画同时发生,如“....translateX(0).opacity(1)”即同时进行平移和透明度设为1,如果在动画间只用了step(),则表示动画间的停顿,如“...translateX('100%').step().opacity(0)”即先平移,平移完成后再透明设为0.

<view :animation="animationData" 
  class="p-body" 
  @touchstart="touchstart" 
  @touchend="touchend"
>
  <!-- 题型&来源 -->
  <view class="p-subhead">
    <text class="p-type">
      {{ queType === 1 ? '多选' : queType === 10 ? '判断' : '单选' }}题
    </text>
    <view>
      <u-icon v-if="pageType === 'wrong'" name="remove" custom-prefix="custom-icon" 
        color="#202020" size="38" class="mr-40" @click="showDeleteModal = true" />
      <u-icon :name="ifCollect === 1 ? 'star-fill' : 'star'" 
        :color="ifCollect === 1 ? '#F7B500' : '#202020'" 
        size="38" @click="handleCollection" />
    </view>
  </view>
  <!-- 题干&附加文字 -->
  <view class="practice-question">
    <p>{{ queTitle }}</p>
    <p v-if="queSubtitle !== ''" class="practice-addtext">{{ queSubtitle }}</p>
  </view>
</view>

<script>
export default {
  name: 'Practice',
  data() {
    return {
      animationData: {} // 动画
    }
  },
  onLoad(option) {
    // 创建动画实例
    this.animation = uni.createAnimation({ timingFunction: 'ease', duration: 120 })
  },
  methods: {
    // 触摸滑动开始
    touchstart(e) {
      this.startX = e.changedTouches[0].clientX // 触摸起点
    },
    // 触摸滑动结束
    touchend(e) {
      this.endX = e.changedTouches[0].clientX // 触摸终点
      const subX = this.endX - this.startX // 触摸滑动距离
      
      if (subX > 100) { // 手指大幅度从左向右滑动---上一题
        // 非第一题有动画
        if (this.queNum !== 1) {

          // 动画:右出左进
          this.animation.translateX('100%').step()      先横向向右移至100%,即整块移没
            .opacity(0).step({ duration: 10 })          再使题目部分透明
            .translateX('-100%').step({ duration: 10 }) 然后趁透明横向向左移至-100%
            .translateX(0).opacity(1).step()         接着横向向右移动至初始位置并恢复透明度

          this.animationData = this.animation.export()  动态动画

          // 为避免uniapp动画只有第一次生效,必须延迟删除动画实例数据
          setTimeout(() => { this.animationData = {} }, 250)
        }

        // 上一题
        this.lastQue()

      } else if (subX < -100) { // 手指大幅度从右向左滑动---下一题
        // 非最后一题有动画
        if (this.queNum !== this.queSum) {

          // 动画:左出右进
          this.animation.translateX('-100%').step()
            .opacity(0).step({ duration: 10 })
            .translateX('100%').step({ duration: 10 })
            .translateX(0).opacity(1).step()

          this.animationData = this.animation.export()

          setTimeout(() => { this.animationData = {} }, 250)

        }

        // 下一题
        this.passQue()

      }
    },
    ...
  }

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐