uniapp浮动面板-movable-area

优点:拖动响应快

缺点:动画有点奇怪

TypeScript 复制代码
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";

interface Props {
  /**
   * 头部高度(px)
   * 默认 50
   */
  headHeight?: number;
  /**
   * 内容高度(px)
   * 默认 300
   */
  contentHeight?: number;
  /**
   * 锚点,单位px
   * 默认为[50, 200]
   */
  anchors?: number[];
}
const props = withDefaults(defineProps<Props>(), {
  headHeight: 50,
  //   contentHeight: 300,
  //   anchors: () => [50, 200],
  contentHeight: 600,
  anchors: () => [100, 300],
});
// 屏幕显示区域高度
const maxAnchorsHeight = computed(() => {
  return Math.max(...props.anchors);
});

// 拖动区域
const dragAreaHeight = computed(() => {
  return props.contentHeight + props.headHeight + maxAnchorsHeight.value;
});

// 偏移offsetY
const offsetY = ref(0);
// 当前拖动的y
let curY = 0;
// 结束定时器
let dragEndTimer: any = null;

onMounted(() => {
  offsetY.value = getOffsetYByAnchor(Math.min(...props.anchors));
});
const getOffsetYByAnchor = (anchor: number): number => {
  const anchorHeight = maxAnchorsHeight.value;
  return anchorHeight - anchor;
};

// 管理拖动结束
const handleDragEnd = () => {
  console.log("拖动结束");
  dragEndTimer = null;
  if (curY === offsetY.value) return;
  const anchor = findNearestAnchor(getOffsetYByAnchor(curY));
  // 这里的锚点是相对于屏幕的,而offsetY是相对于拖动区域的,需要转换
  const result = getOffsetYByAnchor(anchor);
  offsetY.value = result === offsetY.value ? result - 0.01 : result;
  console.log("最终位置", offsetY.value);
};

// 鼠标松开事件
const handleTouchEnd = () => {
  if (dragEndTimer) clearTimeout(dragEndTimer);
  dragEndTimer = setTimeout(handleDragEnd, 100);
  dragEndTimer = null;
};

// 拖动中事件
const handleTouchMove = (e: any) => {
  curY = e.detail.y;
  if (!dragEndTimer) return;
  // 如果有结束定时器,说明已经拖动结束了,在做惯性动画
  // 在做惯性动画,重新开启定时器,一直没有拖动为直
  clearTimeout(dragEndTimer);
  dragEndTimer = setTimeout(handleDragEnd, 100);
  console.log("重新开启定时器");
};

// 计算最近的锚点(用于可能的自动吸附)
const findNearestAnchor = (currentY: number): number => {
  return props.anchors.reduce((prev, curr) => {
    return Math.abs(curr - currentY) < Math.abs(prev - currentY) ? curr : prev;
  });
};
</script>

<template>
  <view class="floating-panel">
    <movable-area
      class="floating-panel__drag-area"
      :style="{
        bottom: maxAnchorsHeight - dragAreaHeight + 'px',
        height: `${dragAreaHeight}px`,
      }"
    >
      <movable-view
        class="floating-panel__movable-view"
        :style="{
          height: `${props.contentHeight + props.headHeight}px`,
        }"
        :y="offsetY"
        direction="vertical"
        @touchend="handleTouchEnd"
        @change="handleTouchMove"
      >
        <view>
          <slot name="head">
            <view
              class="floating-panel__head"
              :style="{
                height: `${props.headHeight}px`,
              }"
            ></view>
          </slot>
          <view
            class="floating-panel__content"
            :style="{
              height: `${props.contentHeight}px`,
            }"
          >
            <slot></slot>
          </view>
        </view>
      </movable-view>
    </movable-area>
  </view>
</template>

<style lang="scss" scoped>
.floating-panel {
  position: fixed;
  bottom: 0;
  left: 0;
  z-index: 20;
  color: #fff;
  width: 100%;
  .floating-panel__drag-area {
    position: absolute;
    left: 0;
    bottom: 200px;
    width: 100%;
    .floating-panel__movable-view {
      width: 100%;
      background-color: pink;
    }
  }
}
</style>
相关推荐
进击的尘埃1 分钟前
组合式函数的设计模式:如何写出真正可复用的 Vue3 Composables
javascript
Moment1 分钟前
想要长期陪伴你的助理?先从部署一个 OpenClaw 开始 😍😍😍
前端·后端·github
前端Hardy1 分钟前
别再用 $emit 满天飞了!Vue 3 组件通信的 4 种正确姿势,第 3 种 90% 的人不知道
前端·vue.js·面试
古时的风筝4 分钟前
花10 分钟时间,把终端改造成“生产力武器”:Ghostty + Yazi + Lazygit 配置全流程
前端·后端·程序员
Cache技术分享5 分钟前
340. Java Stream API - 理解并行流的额外开销
前端·后端
我叫黑大帅16 分钟前
前端如何利用 GitHub Actions 自动构建并发布到 GitHub Pages?
前端·面试·github
smallLabel20 分钟前
记一次 OpenClaw 飞书插件接入填坑指南: Error: spawn EINVAL
前端
zzjyr22 分钟前
react前端项目 fetch原生 与 umijs request 四种请求区别
前端
我叫黑大帅22 分钟前
前端总说的防抖与节流到底是什么?
前端·javascript·面试
小时前端23 分钟前
微信小程序选不了本地文件?用 web-view + H5 一招搞定
前端·微信小程序·uni-app