类抖音评论区弹窗效果,手拖弹窗

屁话少说上效果!

具体就是根据 touch的 start、move、end 来实现 然后就是代码!

<template>
  <transition name="van-slide-up">
    <div
      class="w-list-container"
      v-show="!wrapIsHideAll"
      :style="`pointer-events: ${wrapIsWait ? 'none' : 'auto'}`"
    >
      <!-- 背景块 点了也能关闭 -->
      <div v-show="wrapIsShowAll" class="bg" @click="handleClickBg"></div>

      <div class="weather-wrap" ref="listWrapperRef">
        <div class="list-wrap">
          <!-- 手拖块 -->
          <div
            class="touch-bar-box"
            :style="`height: ${touchBarHeight}`"
            @click.stop="() => {}"
            @touchstart="handleTouchStart"
            @touchmove="handleTouchMove"
            @touchend="handleTouchEnd"
          >
            <!-- 自定义块 -->
            <slot name="diy-touch"></slot>
          </div>
          <slot></slot>
        </div>
      </div>
    </div>
  </transition>
</template>

<script>
// vh
const TRIGGER_VALUE = 16; // 滑动的距离
export const SHOW_Y_VALUE = 17; // 显示
export const HIDE_Y_VALUE = 100; // 隐藏
export const WAIT_Y_VALUE = 85; // 等待[露出一部分

let initialed = false;

let endDistance = HIDE_Y_VALUE; // 当前 endDistance状态

const clientHeight = window.screen.height;

// 只可能 等待 或  显示 的时候能触发这个方法
function getNextStatusValue(v, isToUp) {
  if (v === WAIT_Y_VALUE) {
    return isToUp ? SHOW_Y_VALUE : HIDE_Y_VALUE;
  }
  // 只可能往下
  return WAIT_Y_VALUE;
}

// 获得滑动时/后 的相关参数
function getParams(e, startY) {
  let distanceVH = ((e.pageY - startY) / clientHeight) * 100;
  const isToUp = distanceVH < 0; // 是否向上滑
  let distanceAbsVH = Math.abs(distanceVH); // 移动的距离
  const needSetEndDistance = distanceAbsVH >= TRIGGER_VALUE; // 是否要设置 endDistance,就是这次滑动是否算有效滑动
  return {
    distanceAbsVH,
    needSetEndDistance,
    isToUp
  };
}
export default {
  props: {
    // 触摸条的高
    touchBarHeight: {
      type: String,
      default: "118px"
    }
  },
  data() {
    name: 'WList',
    return {
      startY: 0,
      wrapIsShowAll: false, // 展示了全部
      wrapIsHideAll: false, // 隐藏了全部
      wrapIsWait: true // 等待中
    };
  },
  mounted() {
    this.setTransform(endDistance);
  },
  methods: {
    async setTransform(y, unit = "vh", cb) {
      const isString = typeof y === "string";
      this.$refs.listWrapperRef.style.transform = `translate3d(0, ${
        isString ? y : y + unit
      }, 0)`;
      if (!isString) {
        endDistance = y;
      } else {
        console.log("y是个string, 注意可能会有bug");
      }
      this.updateWrapStatus();
      if (typeof cb === "function") {
        await this.$nextTick();
        cb();
      }
    },
    // 给外部调用 ,其实就是把状态改成 等待中
    show(v = WAIT_Y_VALUE) {
      this.setTransform(v);
    },
    hide() {
      this.setTransform(HIDE_Y_VALUE);
    },
    // 手动更新状态【因为自动的话,或者有依赖关系 感觉会卡!】
    updateWrapStatus() {
      this.wrapIsShowAll = endDistance === SHOW_Y_VALUE;
      this.wrapIsHideAll = endDistance === HIDE_Y_VALUE;
      this.wrapIsWait = endDistance === WAIT_Y_VALUE;

      // 通知外层  隐藏了就true  另外两个显示都是 false
      this.$emit("toggle", this.wrapIsHideAll);

      if (this.wrapIsShowAll) {
        this.$emit("show", this);
      }
      if (this.wrapIsWait) {
        this.$emit("wait", this);
      }
    },
    // 点了背景时设置为 等待中
    handleClickBg() {
      this.setTransform(WAIT_Y_VALUE);
    },
    setInitialed(v) {
      initialed = v;
    },
    setStartY(v) {
      this.startY = v;
    },
    handleTouchStart(e) {
      // console.log('开始了新的点击!')
      // if (!canTouch || initialed) return;
      if (initialed) {
        console.log("还没准备好");
        return;
      }
      // 记录开始位置
      this.setStartY(e.touches[0].pageY);
      // 防止 handleTouchStart 重复触发
      this.setInitialed(true);
    },

    handleTouchMove(e) {
      // console.log('handleTouchMove', e)
      // if (!canTouch || !initialed) return;
      if (!initialed) {
        console.log("还没准备好");
        return;
      }
      // 实时计算滑动距离
      let { distanceAbsVH, isToUp } = getParams(e.touches[0], this.startY);

      if (endDistance === WAIT_Y_VALUE) {
        // 等待的情况
        if (isToUp) {
          // 往上滑
          //  console.log('等待的情况 还向上滑', distanceAbsVH)
          if (WAIT_Y_VALUE - distanceAbsVH < SHOW_Y_VALUE) {
            // console.log('过了!过最小值了!', WAIT_Y_VALUE - distanceAbsVH, SHOW_Y_VALUE)
            distanceAbsVH = SHOW_Y_VALUE;
          } else {
            // console.log('未过最大值', WAIT_Y_VALUE - distanceAbsVH, SHOW_Y_VALUE)
            distanceAbsVH = WAIT_Y_VALUE - distanceAbsVH;
          }
        } else {
          // 往下滑
          // console.log('等待的情况 还向下滑', distanceAbsVH)
          if (WAIT_Y_VALUE + distanceAbsVH > HIDE_Y_VALUE) {
            // console.log('过了!过最大值了!', WAIT_Y_VALUE + distanceAbsVH, HIDE_Y_VALUE)
            distanceAbsVH = HIDE_Y_VALUE;
          } else {
            // console.log('未过最大值', WAIT_Y_VALUE + distanceAbsVH, HIDE_Y_VALUE)
            distanceAbsVH = WAIT_Y_VALUE + distanceAbsVH;
          }
        }
      } else if (endDistance === SHOW_Y_VALUE) {
        // 已全显示的情况
        if (isToUp) {
          // 往上滑
          //  console.log('已全显示的情况 还向上滑', distanceAbsVH)
          distanceAbsVH = SHOW_Y_VALUE;
        } else {
          // 往下滑
          distanceAbsVH =
            SHOW_Y_VALUE + distanceAbsVH > WAIT_Y_VALUE
              ? WAIT_Y_VALUE
              : SHOW_Y_VALUE + distanceAbsVH;
        }
      }
      this.$refs.listWrapperRef.style.transform = `translate3d(0, ${distanceAbsVH}vh, 0)`; // 赋值
    },

    handleTouchEnd(e) {
      let nextEndDistance;
      let { needSetEndDistance, isToUp } = getParams(
        e.changedTouches[0],
        this.startY
      );
      console.log(
        "最终 表示是不是一个有效的移动 needSetEndDistance",
        needSetEndDistance
      );
      if (needSetEndDistance) {
        console.log("如果有效 就基于现在的位置 去上or下  isToUp", isToUp);
        nextEndDistance = getNextStatusValue(endDistance, isToUp);
      } else {
        nextEndDistance = endDistance;
      }

      console.log("松开后把 y 设置为", nextEndDistance);
      // 停止滑动时的位置 单位vh
      this.setTransform(nextEndDistance, undefined, () => {
        // console.log('关闭禁用!')
        this.setInitialed(false);
        nextEndDistance = undefined;
      });
    }
  }
};
</script>

<style lang="less">
.w-list-container {
  width: 100vw;
  height: 100vh;
  position: fixed;
  z-index: 100;
  top: 0;
  left: 0;
  overflow: hidden;
  .bg {
    position: absolute;
    left: 0;
    top: 0;
    width: 100vw;
    height: 100vh;
    // opacity: 0;
    background: transparent;
  }
  .weather-wrap {
    user-select: none;
    width: 90vw;
    height: 100vh;
    margin-left: 5vw;
    background: transparent;
    border: 8px;
    transition: all 0.3s ease 0s;
    position: fixed;
    bottom: 0;
    z-index: 3;
    transform: translate3d(0, 100vh, 0);
  }

  .list-wrap {
    user-select: none;
    .touch-bar-box {
      position: absolute;
      pointer-events: auto;
      top: 0;
      z-index: 1;
      width: 100%;
      // background: #733acb;
      // opacity: .6;
    }
  }
}
</style>

外部使用的话

<template>
  <div class="weather-home-container" v-loading="isLoading">
    <div id="map" v-loading="isLoading"></div>
    <WList
      ref="WListRef"
      @toggle="$listeners['toggle-w-list']"
      @show="handleShowWList"
      @wait="handleWaitWList"
    >
      <WWeather ref="WWeatherRef" />
      <template #diy-touch>
        <div class="touch-block" @click="handleChangeTouchBlock"></div>
      </template>
    </WList>
  </div>
</template>

 WList是上面那个组件 WWeather是内容

有帮助到你的话 点个赞~谢谢🙏

 

参考自:http://github.com/uglyspoon/h5-cloud-music-ts

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值