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>
相关推荐
云上工程笔记28 分钟前
AstraFlow + React/Vite:如何用自然语言开发并部署一个 AI 咖啡网站原型
前端·人工智能·react.js
Q一件事1 小时前
ArcGIS中TypeError: can‘t multiply sequence by non-int of type ‘str‘错误
前端·javascript·arcgis
美狐美颜SDK开放平台1 小时前
从视频处理到实时渲染:直播APP中视频美颜SDK的关键技术解析
前端·人工智能·架构·实时互动·音视频·视频美颜sdk
WeiXin_DZbishe1 小时前
基于springboot大学生提问箱系统-计算机毕设【课程设计】72593
javascript·vue.js·spring boot·vscode·python·node.js·php
yume_sibai1 小时前
ES6 核心知识点完全总结(变量 + 箭头函数 + 解构 + Promise + 模块化)
前端·ecmascript·es6
Moment2 小时前
为什么市面上的 coding agent 大多数都基于Nodejs?
前端·后端·面试
吃瓜的猹2 小时前
Gemini 3.8 Flash 是真的站起来了,一起来看看我到底做了什么吧~
前端
光影少年2 小时前
React18 对RN 的影响
android·前端·react.js·ios·前端框架
tryCbest2 小时前
Vue Router学习及使用
前端·vue.js
杉氧2 小时前
丝滑的奥秘:Reanimated 3 动画引擎与手势处理(Gesture Handler)
android·前端·react native