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>
相关推荐
东方小月6 小时前
从零开发一个 Coding Agent(四):使用状态机校验大模型事件流
前端·人工智能·后端
Csvn6 小时前
🧩 ESM vs CJS 混用的 7 个「天坑」——从 TypeScript 编译到 Node 与浏览器
前端
Csvn6 小时前
🎯 Web 性能 API 集合:Performance Observer 的 5 个冷门妙用
前端
Csvn6 小时前
深入 React 闭包陷阱:从根源上理解并根治 stale closure
前端
whyfail7 小时前
前端学 Spring Boot(8):接口为什么越用越慢?
前端·spring boot·后端
用户059540174467 小时前
LangChain 记忆测试踩坑实录:这两个坑让我排查了 4 小时
前端·css
程序员黑豆7 小时前
鸿蒙应用开发:@Monitor 装饰器使用教程
前端·harmonyos
用户938515635078 小时前
从零构建《天龙八部》知识库:EPUB 加载→文本分割→向量嵌入→Milvus 存储→RAG 问答,一条链路打通
javascript·人工智能·全栈
SamChan908 小时前
在Web应用中集成PDF多语言翻译功能:PDFTranslator API实战指南
前端·python·ai·pdf·yapi·机器翻译
kyriewen8 小时前
AI Agent 9秒删光了生产数据库——我给自己的项目做了5个紧急检查
前端·ai编程·claude