uniapp 微信小程序Vue3项目使用内置组件movable-area封装悬浮可拖拽按钮(拖拽结束时自动吸附到最近的屏幕边缘)

一、最终效果

二、具体详情请看movable-areamovable-view官方文档说明

三、组件源码

html 复制代码
<template>
  <movable-area class="movable-area" @touchend="onTouchend">
    <movable-view class="movable-view" :x="x" :y="y" direction="all" @change="onChange">
      <view class="addBtn" @tap="handleClick">{{title}}</view>
      <slot />
    </movable-view>
  </movable-area>
</template>

<script lang="ts" setup>
import { debounce } from "@/utils";
defineProps({
  title: {
    type: String
  }
});
const emits = defineEmits(["click"]);
const x = ref(0);
const y = ref(0);
const screenWidth = ref(0);
const screenHeight = ref(0);

onMounted(() => {
  uni.getSystemInfo({
    success: res => {
      screenWidth.value = res.windowWidth;
      screenHeight.value = res.windowHeight;
      // 初始位置在屏幕右下角
      y.value = screenHeight.value - 200;
      x.value = screenWidth.value - 70;
    }
  });
});
// 拖动坐标更新(防抖)
const onChange = (e: { detail: { x: number; y: number } }) => {
  debounce(() => {
    x.value = e.detail.x;
    y.value = e.detail.y;
  }, 500);
};
// 触摸结束时吸附边缘
const onTouchend = () => {
  nextTick(() => {
    const threshold = 50; // 吸附阈值(rpx)
    if (Math.abs(x.value - 0) < threshold) {
      x.value = 0;
    } else if (Math.abs(x.value - screenWidth.value) < threshold) {
      x.value = screenWidth.value;
    }
    if (Math.abs(y.value - 0) < threshold) {
      y.value = 0;
    } else if (Math.abs(y.value - screenHeight.value) < threshold) {
      y.value = screenHeight.value;
    }
  });
};
const handleClick = () => {
  emits("click");
};
</script>

<style lang="scss">
.movable-area {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: calc(100vh - 100px);
  pointer-events: none; /* 关键样式 */
  z-index: 9999;
  .movable-view {
    pointer-events: auto; /* 关键样式 */
    width: 100rpx;
    height: 100rpx;
    will-change: transform;
    .addBtn {
      border-radius: 50%;
      width: 40px;
      height: 40px;
      overflow: hidden;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 14px;
      padding: 8px;
      box-shadow: 0 1px 5px 2px rgba(0, 0, 0, 0.3);
      background: #355db4;
      text-align: center;
    }
  }
}
</style>

相关文章

基于ElementUi再次封装基础组件文档


vue3+ts基于Element-plus再次封装基础组件文档

相关推荐
掘金安东尼1 分钟前
前端周刊第439期(2025年11月3日–11月9日)
前端·javascript·vue.js
有蝉12 分钟前
vue-office——支持多种文件(docx、excel、pdf)预览的vue组件库,支持vue2/3。也支持非Vue框架的预览。
vue.js·pdf·excel
起这个名字31 分钟前
微前端应用通信使用和原理
前端·javascript·vue.js
QuantumLeap丶40 分钟前
《uni-app跨平台开发完全指南》- 06 - 页面路由与导航
前端·vue.js·uni-app
Sheldon一蓑烟雨任平生43 分钟前
Vue3 组件库 Element Plus
vue.js·vue3·element plus·element ui·vue3 常用组件库
用户97141718142743 分钟前
uniapp页面路由
vue.js·uni-app
切糕师学AI1 小时前
.NET Core Web + Vue 项目集成消息推送工具SignalR
vue.js·.netcore·signalr
ttod_qzstudio1 小时前
Vue 3 Props 定义详解:从基础到进阶
前端·vue.js
茶憶1 小时前
uni-app app移动端实现纵向滑块功能,并伴随自动播放
javascript·vue.js·uni-app·html·scss
dcloud_jibinbin1 小时前
【uniapp】解决小程序分包下的json文件编译后生成到主包的问题
前端·性能优化·微信小程序·uni-app·vue·json