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

相关推荐
浅水壁虎7 小时前
vue基础(第二章 )
前端·javascript·vue.js
腻害兔8 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:CRM 客户关系模块深度解析——从线索到回款,一套完整的 B2B 销售闭环是怎么搭的?
java·前端·javascript·vue.js·产品经理·ai编程
m0_5474866613 小时前
《Vue.js + uni-app全栈开发从入门到实践 》全套PPT课件2026版
vue.js·uni-app
达子66615 小时前
第10章_HarmonyOs开发图解 用JS开发UI
vue.js
奶糖 肥晨15 小时前
想return数据 返回的确是函数问题处理
vue.js
无巧不成书021816 小时前
Vue+Vue-CLI全平台零基础搭建教程
前端·javascript·vue.js·vue·前端开发·vue-cli·前端环境搭建
腻害兔17 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro IoT 物联网模块:从协议网关到物模型,一文讲透开源 IoT 平台架构
前端·vue.js·物联网·产品经理·ai编程
张人玉17 小时前
基于 Vue 3 + ECharts + Express + SQLite 的可视化大屏与业务管理系统——YOLOv8 高精度车辆行人检测与计数系统
数据库·vue.js·yolo·sqlite·echarts·express
腻害兔1 天前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
sugar__salt1 天前
Vue3 表单双向绑定与响应式核心 API 技术详解
前端·javascript·vue.js