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再次封装基础组件文档

相关推荐
chenhdowue15 小时前
vue3甘特图 vxe-gantt 行内显示子任务,拆分任务展示同时可拖拽调整任务日期
vue.js·甘特图·vxe-gantt
计算机毕设指导615 小时前
基于微信小程序的智能停车场管理系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·intellij-idea
BYSJMG15 小时前
2026计算机毕设推荐:基于大数据的车辆二氧化碳排放量可视化分析系统
大数据·vue.js·python·mysql·django·课程设计
~小仙女~15 小时前
组件的二次封装
前端·javascript·vue.js
这是个栗子15 小时前
AI辅助编程(一) - ChatGPT
前端·vue.js·人工智能·chatgpt
心.c15 小时前
Vue3+Node.js实现文件上传分片上传和断点续传【详细教程】
前端·javascript·vue.js·算法·node.js·哈希算法
2501_9339072115 小时前
如何选择西安优质小程序开发服务与本凡码农合作?
科技·微信小程序·小程序
QQ179580639616 小时前
基于springboot+vue的hive的歌曲音乐筛选推荐系统网站(源码+lw+部署文档+讲解等)
vue.js·hive·spring boot
m0_7482331716 小时前
Laravel+Vue:全栈开发终极指南
vue.js·php·laravel
切糕师学AI16 小时前
Vue 中如何修改地址栏参数并重新加载?
前端·javascript·vue.js