vue移动端实现悬浮可拖动且滑动页面时收缩至内侧按钮

效果图

按钮我用的是一个返回图片,这里你可以自定义按钮文字和宽高,父页面传参就可以了。
我是在vue移动端实现可拖动悬浮按钮上进行的修改,加了一个滑动收缩
在这里插入图片描述

组件代码

<template>
  <div class="float_button">
    <div
        @click="onBtnClicked"
        ref="floatButton"
        class="float_info"
        :style="{'width': itemWidth + 'px', 'height': itemHeight + 'px', 'left': left + 'px', 'top': top + 'px'}"
    >
      <img src="../assets/imgs/return-up-page.png" @click="XXX()">
<!--      <span class="text">{{ text }}</span>-->
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      clientWidth: 0,
      clientHeight: 0,
      timer: null,
      currentTop: 0,
      left: 0,
      top: 0,

      oldScrollTop: 0,  //记录上一次滚动结束后的滚动距离
      scrollTop: 0,     // 记录当前的滚动距离
    }
  },
  props: {
    text: {                 // 按钮文本内容
      type: String,
      default: "首页"
    },
    itemWidth: {            // 悬浮按钮宽度
      type: Number,
      default: 55
    },
    itemHeight: {           // 悬浮按钮高度
      type: Number,
      default: 55
    },
    gapWidth: {             // 距离左右两边距离
      type: Number,
      default: 15
    },
    coefficientHeight: {    // 从上到下距离比例
      type: Number,
      default: 0.55
    }
  },
  created() {
    this.clientWidth = document.documentElement.clientWidth
    this.clientHeight = document.documentElement.clientHeight
    this.left = this.clientWidth - this.itemWidth - this.gapWidth
    this.top = this.clientHeight * this.coefficientHeight
  },
  mounted() {
    this.$nextTick(() => {
      const floatButton = this.$refs.floatButton
      floatButton.addEventListener("touchstart", () => {
        floatButton.style.transition = 'none'
      })

      // 在拖拽的过程中,组件应该跟随手指的移动而移动。
      floatButton.addEventListener("touchmove", (e) => {
        if (e.targetTouches.length === 1) {         // 一根手指
          document.body.addEventListener('touchmove', this.bodyScroll, { passive: false });  //禁止页面滑动
          let touch = e.targetTouches[0]
          this.left = touch.clientX - 20
          this.top = touch.clientY - 25
        }
      })

      // 拖拽结束以后,重新调整组件的位置并重新设置过度动画。
      floatButton.addEventListener("touchend", () => {
        document.body.removeEventListener('touchmove', this.bodyScroll, { passive: false });  //解除页面禁止滑动
        floatButton.style.transition = 'all 0.3s'
        if(this.left > document.documentElement.clientWidth / 2) {
          this.left = document.documentElement.clientWidth - 70;
        }else{
          this.left = 15
        }
      })
    })
    this.handleScroll();
  },
  methods: {
    //拖动时禁止滑动
    bodyScroll(event){
      event.preventDefault();
    },
    onBtnClicked(){
      this.$emit("onFloatBtnClicked")
    },
    handleScroll() {
      window.addEventListener('scroll', () => {
        this.scrollTop = window.scrollY;
      })
    },
    handleScrollStart() {
      this.timer && clearTimeout(this.timer)
      this.timer = setTimeout(() => {
        this.handleScrollEnd()
      }, 300)
      this.currentTop = document.documentElement.scrollTop || document.body.scrollTop;
      if(this.left > this.clientWidth / 2) {
        this.left = this.clientWidth - this.itemWidth / 2
      } else {
        this.left = -this.itemWidth / 2
      }
    },
    handleScrollEnd(){
      let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
      if(scrollTop === this.currentTop) {
        if(this.left > this.clientWidth/2) {
          this.left = this.clientWidth - this.itemWidth - this.gapWidth
        } else {
          this.left = this.gapWidth
        }
        clearTimeout(this.timer)
      }
    }
  },
  beforeDestroy(){
    // 添加监听页面滚动  销魂滚动监听
    window.removeEventListener('scroll', this.handleScrollStart);
  },
  watch: {
    scrollTop(newValue, oldValue) {
      setTimeout(() => {
        if(newValue == window.scrollY) { //延时执行后当newValue等于window.scrollY,代表滚动结束
          //console.log('滚动结束');
          if (this.left === 15){  //按钮在页面左侧
            this.left = this.left + 40
          }else if (this.left < 15){
            this.left = 15
          }else {
            this.left = this.left - 40
          }
          this.oldScrollTop = newValue; //每次滚动结束后都要给oldScrollTop赋值
        };
      }, 20); //必须使用延时器,否则每次newValue和window.scrollY都相等,无法判断,20ms刚好大于watch的侦听周期,故延时20ms
      if(this.oldScrollTop == oldValue) { //每次滚动开始时oldScrollTop与oldValue相等
       //console.log('滚动开始');
        if (this.left === 15){
          this.left = this.left - 40
        }else {
          this.left = this.left + 40
        }
      }
    }
  },
  destroyed() {}
}
</script>
<style lang="less">
.float_button {
  .float_info{
    box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.1);
    color: #666666;
    transition: all 0.3s;
    position: fixed;
    bottom: 50px;
    right: 30px;
    width: 55px;
    height: 55px;
    display: flex;
    flex-flow: column;
    justify-content: center;
    align-items: center;
    z-index: 999;
    background: #1E69EB;
    border-radius: 50%;
    cursor: pointer;
    .text{
      font-size: 24px;
      color: #fff;
    }
    img{
      width: 35px;
      height: 35px;
    }
  }
}
</style>

不想滑动的时候收缩把watch里的方法清除掉就行。

父页面注册引用

<template>
  <div>
    <div v-for="item in 100">
      <div class="test">{{item}}</div>
    </div>
    <fdButton></fdButton>
  </div>
</template>

<script>
import fdButton from "@/components/fdButton";
export default {
  name: "ys",
  components:{
    fdButton
  }
}
</script>

<style scoped>
  .test{
    text-align: center;
    height: 45px;
    line-height: 45px;
    border-bottom: 1px solid #EBEEF5;
  }
</style>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值