vue h5 上下拉拖拽功能的实现

<template>

  <div class="cloze-filling-drag-popup" ref="popup">

    <div

      class="title"

      @touchstart.stop.prevent="touchStart"

      @touchmove.stop.prevent="touchmove"

      @touchend.stop.prevent="touchend"

      ref="titleBar"

    ></div>    <

div class="popup-content" ref="popupContent">    

  <!-- <slot name="popupContent"></slot> -->

      <div class="options-title" ref="refOptionsTitle">

        <span>填空{{activefillIndex}}</span>

        <span style="float:right;">

          <span style="color:#0096f0">{{activefillIndex}}</span>

          /{{fills.length}}

        </span>

        <div

          style="width:100%;height:1px;box-sizing: border-box;border-bottom:1px solid #e0e0e0;position:absolute;bottom:0px;"

        ></div>

      </div>

      <van-swipe

        :loop="false"

        :show-indicators="false"

        indicator-color="white"

        ref="swiper2"

        :initial-swipe="initialSwipe"

        @change="swiperChange"

      >     

   <template v-for="(fill,index) in fills">

          <van-swipe-item :key="index">

            <div class="options">

              <template v-for="(item,idx) in fill">

                <div :key="idx" class="option" @click="itemClick(idx,index,item,fill)">

                  <div class="tag">

                    <span class="normal-option" :class="[ item.checked ?'active-option':'']">

                      <!--:class="[ item.checked ?'active-option':'']" -->

                      {{idx == 0 ? 'A'

                      : idx == 1 ? 'B'

                      :idx == 2 ? 'C'

                      : 'D'

                      }}

                    </span>

                  </div>

                  <div class="content" v-html="item.content"></div>

                </div>

              </template>

            </div>

          </van-swipe-item>

        </template>

      </van-swipe>

    </div>

  </div>

</template>

<script>

import { mapActions, mapGetters, mapState } from "vuex";

import { Popup, Swipe, SwipeItem } from "vant";

export default {

  name: "exam-drag-component",

  components: {

    vanPopup: Popup,

    vanSwipe: Swipe,

    vanSwipeItem: SwipeItem

  },

  props: {

    scrollTop: {

      type: Number,

      default: 0

    },

    question: {

      type: Object,

      default: () => {

        return {};

      }

    },

    footHeight: {

      type: Number,

      default: 0

    }

  },

  watch: {

    scrollTop: {

      handler(val) {

        this.obj.ele.style.top = this.bottom + "px";

        //this.moveBottomHandler();

      }

    },

    footHeight: {

      handler(val) {

        this.$nextTick(() => {

          if (this.$refs.popup) {

            this.initData();

          }

        });

      },

      immediate: true

    },

    question: {

      handler(val) {

        if (val) {

          this.data = val;

          if (val.fills && val.fills.length > 0) {

            let arr = val.fills.reduce((ls, cur) => {

              let a = [];

              if (cur.length > 0) {

                cur.forEach(val => {

                  let o = {};

                  o.content = val;

                  o.checked = false;

                  a.push(o);

                });

              }

              ls.push(a);

 

              return ls;

            }, []);

            this.fills = arr;

          } else {

            this.fills = [];

          }

        }

      },

      immediate: true,

      deep: true

    },

    currentUserAnswer: {

      handler(val) {

        this.currentAnswer = [];

        if (this.data.id == val.questionId) {

          this.userCheckedData = val.checkOption;

          this.initUserCheckedFills();

        }

      },

      immediate: true,

      deep: true

    },

    activeQuestion: {

      handler(val) {

        this.activefillIndex = 1;

        this.obj.ele.style.top = this.bottom + "px";

      }

    }

  },

  data() {

    return {

      msg: "拖拽组件",

      obj: {},

      app: this.isApp,

      android: this.isAndroid,

      bottom: 0,

      data: {},

      currentAnswer: [],

      activefillIndex: 1,

      fills: [],

      initialSwipe: 0,

      userCheckedData: [],

      titleHeight: 0

    };

  },

  mounted() {

    console.log(this.activeQuestion);

    this.initData();

    this.initUserCheckedFills();

  },

  computed: {

    ...mapGetters(["activeQuestion", "currentUserAnswer"]),

    ...mapState({

      qsNum: state => state.examPaper.qsNum

    })

  },

  methods: {

    ...mapActions(["setUserAnswers"]),

    initData() {

      this.obj.ele = this.$refs.popup;

      this.titleHeight =

        this.$refs.refOptionsTitle.offsetHeight +

        this.$refs.titleBar.offsetHeight;

      //alert(document.documentElement.clientHeight);

      let clientHeight = document.documentElement.clientHeight;

 

      this.bottom =

        document.documentElement.clientHeight -

        this.footHeight -

        this.titleHeight;

      this.obj.ele.style.top = this.bottom + "px";

    },

    swiperChange(data) {

      this.activefillIndex = data + 1;

    },

    itemClick(data, currentfill, item, fill) {

      fill.forEach(val => {

        val.checked = false;

      });

      item.checked = true;

      let that = this;

      let obj = {};

      obj.fill = currentfill;

      obj.answer = data;

      let idx = -1;

      if (this.currentAnswer.length > 0) {

        this.currentAnswer.forEach((val, index) => {

          if (val.fill == currentfill) {

            idx = index;

          }

        });

      }

      if (idx > -1) {

        this.currentAnswer.splice(idx, 1);

        this.currentAnswer.push(obj);

      } else {

        this.currentAnswer.push(obj);

      }

      this.itemClickHandler();

    },

    itemClickHandler() {

      let obj = {};

      obj.id = this.activeQuestion;

      obj.questionId = this.data.id;

      obj.fillLen = this.fills.length;

      obj.mode = this.data.mode_id;

      obj.checkOption = this.currentAnswer;

      obj.img = "";

      //this.setUserAnswers(obj);

      console.log(obj);

      //this.$emit("submitItemAnswer", obj);

      this.setUserAnswers(obj);

      //console.log(this.currentAnswer);

    },

 

    initUserCheckedFills() {

      console.log(this.userCheckedData);

      this.currentAnswer = this.userCheckedData;

      if (

        this.currentUserAnswer.questionId == this.data.id &&

        this.currentUserAnswer.id == this.activeQuestion

      ) {

        if (this.userCheckedData.length > 0 && this.fills.length > 0) {

          this.userCheckedData.forEach(val => {

            console.log(val);

            this.fills[val.fill][val.answer].checked = true;

          });

        }

      }

    },

    moveTopHandler() {

      this.animateMove(this.obj.ele, this.top);

    },

    moveBottomHandler() {

      this.animateMove(this.obj.ele, this.bottom);

    },

    animateMove(element, target) {

      clearInterval(timer);

      var left = element.offsetTop;

      //设置步长 表示一步动作的差值

      //通过比较element当前的left值和target值的大小,来确定平移方向

      var step = (target - left) / 1;

 

      var timer = setInterval(function() {

        //如果目标值大于当前的left值,step为正数,向右移动

        //如果目标值小于当前的left值,step为负数,向左移动

        left += step;

        element.style.top = left + "px";

 

        //判断停止动画

        //比较差值,取绝对值,当两者的差值小于了步进值时,停止动画,

        if (Math.abs(target - left) <= Math.abs(step)) {

          clearInterval(timer);

          element.style.top = target + "px";

        }

      }, 1);

    },

    touchStart(evt) {

      if (this.android || !this.isApp) {

        this.obj.Y = evt.changedTouches[0].clientY;

        this.obj.newTop = evt.path[1].style.top.split("px")[0];

        this.obj.minTop = this.bottom - this.$refs.popupContent.offsetHeight;

      } else {

        this.obj.Y = evt.targetTouches[0].clientY;

        this.obj.newTop = this.obj.ele.style.top.split("px")[0];

        this.obj.minTop = this.bottom - this.$refs.popupContent.offsetHeight;

        console.log(this.obj.minTop, "2222");

      }

    },

    touchmove(evt) {

      let moveY = 0;

      if (this.android || !this.isApp) {

        moveY = evt.changedTouches[0].clientY;

      } else if (!this.android && this.isApp) {

        moveY = evt.targetTouches[0].clientY;

      }

      let len = this.obj.Y - moveY;

      if (len > 0) {

        if (this.obj.newTop - len > this.obj.minTop + this.titleHeight) {

          this.obj.ele.style.top = this.obj.newTop - len + "px";

        } else {

          this.obj.ele.style.top = this.obj.minTop + this.titleHeight + "px";

        }

      } else {

        //console.log(this.bottom);

        if (Number(this.obj.newTop) + Math.abs(len) > this.bottom) {

          this.obj.ele.style.top = this.bottom + "px";

        } else {

          let top = this.obj.newTop - len;

          this.obj.ele.style.top = top + "px";

        }

      }

      this.obj.len = Math.abs(len);

    },

    touchend(evt) {

      //console.log(this.obj, "end");

    }

  }

};

</script>

<style lang="less">

.cloze-filling-drag-popup {

  position: fixed;

  width: 100%;

  z-index: 99;

  -webkit-overflow-scrolling: touch;

  .title {

    position: relative;

    width: 32vw;

    height: 10vw;

    background: #f8f8f8;

    margin-left: 35%;

    border-radius: 8px 8px 0px 0px;

    border: 1px solid #d8c9c9;

    border-bottom: none;

    background-image: url(./../assets/img/sgt.png);

    background-size: 50% 50%;

    background-repeat: no-repeat;

    background-position: center center;

    box-sizing: border-box;

    z-index: 3;

  }

  .popup-content {

    width: 100%;

    background: #ffffff;

    border-top: 1px solid #e0e0e0;

    padding-bottom: 10vw;

    .title {

      padding: 2vw 2vw 0vw 2vw;

      font-size: 5vw;

      height: 8vw;

      box-sizing: border-box;

    }

    .options-title {

      position: relative;

      padding: 2vw 2vw 0vw 2vw;

      font-size: 5vw;

      height: 10vw;

      box-sizing: border-box;

    }

    .options {

      padding: 0vw 1vw 1vw 0;

      width: 100%;

      font-size: 4vw;

      .option {

        display: flex;

        margin-top: 2vw;

        flex-wrap: nowrap;

        .tag {

          width: 10vw;

          height: 8vw;

          line-height: 5.5vw;

          text-align: center;

          .normal-option {

            display: inline-block;

            width: 5vw;

            height: 5vw;

            padding: 1vw;

            margin-top: 1vw;

            border: 1px solid #646464;

            border-radius: 100%;

          }

          .active-option {

            background: #0096f0;

            color: #ffffff;

          }

        }

        .content {

          padding-top: 1vw;

          flex: 1;

        }

      }

    }

  }

}

</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值