uniapp + h5实现悬浮活动按钮组件

ActivityFloatButton 悬浮活动按钮组件代码

当前说项目活动悬浮窗,点击跳转到活动页面,其他需求可重新更改当前组件

html 复制代码
<template>
  <view class="activity-float-button" :style="{
    left: buttonX + 'px',
    top: buttonY + 'px'
  }" @touchstart.stop="handleTouchStart" @touchmove.stop="handleTouchMove" @touchend.stop="handleTouchEnd"
        @click.stop="handleClick">
    <image class="button-img" src="https://cdn.joytup.com/imgs/icon/cj.gif" mode="scaleToFill" />
    <!-- <view class="button-text">活动</view> -->
  </view>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'

// 按钮尺寸
const BUTTON_WIDTH = 120 // 按钮宽度 rpx
const BUTTON_HEIGHT = 120 // 按钮高度 rpx


// 按钮位置
const buttonX = ref(0)
const buttonY = ref(0)
const isDragging = ref(false)
const startX = ref(0)
const startY = ref(0)
const offsetX = ref(0)
const offsetY = ref(0)
const hasMoved = ref(false)

const pixelRatio = ref(2)

// 获取系统信息
const getSystemInfo = () => {
  return new Promise((resolve) => {
    uni.getSystemInfo({
      success: (res) => resolve(res),
      fail: () => resolve({})
    })
  })
}

// 初始化按钮位置
const initPosition = async () => {
  const systemInfo = await getSystemInfo()
  const { windowWidth, windowHeight } = systemInfo

  pixelRatio.value = 750 / windowWidth;  // 推荐

  // 将 rpx 转换为 px
  const buttonWidthPx = BUTTON_WIDTH / 2 / pixelRatio.value * 2
  const buttonHeightPx = BUTTON_HEIGHT / 2 / pixelRatio.value * 2

  // 固定到右下角,留出安全边距
  const margin = 40 / pixelRatio.value // px
  const marginRight = 40 / 2 / pixelRatio.value * 2 // 右边缘留 40rpx
  buttonX.value = windowWidth - buttonWidthPx - margin
  buttonY.value = windowHeight - buttonHeightPx - margin
}

// 触摸开始
const handleTouchStart = (e) => {
  isDragging.value = true
  hasMoved.value = false
  startX.value = e.touches[0].clientX
  startY.value = e.touches[0].clientY
  offsetX.value = buttonX.value
  offsetY.value = buttonY.value
}

// 触摸移动
const handleTouchMove = (e) => {
  if (!isDragging.value) return

  e.preventDefault()

  const clientX = e.touches[0].clientX
  const clientY = e.touches[0].clientY

  // 计算移动距离
  const deltaX = clientX - startX.value
  const deltaY = clientY - startY.value

  // 判断是否真的移动了(避免点击时误判为拖动)
  if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
    hasMoved.value = true
  }

  // 更新按钮位置
  buttonX.value = offsetX.value + deltaX
  buttonY.value = offsetY.value + deltaY
}

// 触摸结束,吸附到屏幕边缘
const handleTouchEnd = async () => {
  isDragging.value = false

  const systemInfo = await getSystemInfo()
  const { windowWidth, windowHeight } = systemInfo


  const buttonWidthPx = BUTTON_WIDTH / 2 / pixelRatio.value * 2
  const buttonHeightPx = BUTTON_HEIGHT / 2 / pixelRatio.value * 2
  const marginRight = 40 / 2 / pixelRatio.value * 2 // 右边缘留 40rpx

  const centerX = buttonX.value + buttonWidthPx / 2

  // 吸附到左边缘或右边缘
  if (centerX < windowWidth / 2) {
    buttonX.value = 10
  } else {
    console.log('右边:', windowWidth, buttonWidthPx, marginRight);
    buttonX.value = windowWidth - buttonWidthPx - marginRight
  }

  // 限制 Y 轴范围,防止超出屏幕
  const maxY = windowHeight - buttonHeightPx - 20 // 底部留 20px 边距
  if (buttonY.value < 20) { // 顶部留 20px 边距
    buttonY.value = 20
  } else if (buttonY.value > maxY) {
    buttonY.value = maxY
  }
}

// 点击跳转
const handleClick = () => {
  // 如果是拖动操作,不触发点击
  if (hasMoved.value) return

  uni.navigateTo({
    url: '/pagesActivity/list/list'
  })
}

onMounted(() => {
  initPosition()
})
</script>

<style lang="scss" scoped>
.activity-float-button {
  position: fixed;
  width: 120rpx;
  height: 120rpx;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  // cursor: pointer;
  user-select: none;
  // background: linear-gradient(135deg, #FF631B 0%, #FF8C42 100%);
  // box-shadow: 0 4rpx 12rpx rgba(255, 99, 27, 0.3);


  .button-img {
    width: 120rpx;
    height: 120rpx;
  }

  .button-text {
    font-size: 28rpx;
    font-weight: 500;
    color: #FFFFFF;
    text-align: center;
    line-height: 1;
  }
}
</style>

ActivityFloatButton 悬浮活动按钮组件文档

一个固定在页面右下角的可拖动悬浮按钮组件,用于快速跳转到活动列表页面。

功能特性

  • ✅ 固定在页面右下角显示
  • ✅ 支持触摸拖动,可自由移动位置
  • ✅ 拖动结束后自动吸附到屏幕边缘
  • ✅ 圆形渐变样式,美观大方
  • ✅ 点击跳转到活动列表页面
  • ✅ 区分拖动和点击操作,避免误触
  • ✅ 自动适配不同屏幕尺寸

使用方法

1. 引入组件

html 复制代码
<script setup>
import ActivityFloatButton from "@/components/activity-float-button/activity-float-button.vue";
</script>

2. 使用组件

html 复制代码
<template>
  <view class="page">
    <!-- 你的页面内容 -->

    <!-- 悬浮活动按钮 -->
    <ActivityFloatButton />
  </view>
</template>

<script setup>
import ActivityFloatButton from "@/components/activity-float-button/activity-float-button.vue";
</script>

3. 全局使用(推荐)

App.vue 中引入,即可在所有页面使用:

html 复制代码
<template>
  <view>
    <!-- 你的全局内容 -->
    <router-view />

    <!-- 悬浮活动按钮 -->
    <ActivityFloatButton />
  </view>
</template>

<script setup>
import ActivityFloatButton from "@/components/activity-float-button/activity-float-button.vue";
</script>

组件样式

默认样式

  • 尺寸:100rpx × 100rpx
  • 形状:圆形
  • 背景:橙色渐变(#FF631B → #FF8C42)
  • 文字:白色,28rpx,居中显示"活动"
  • 阴影:橙色半透明阴影,提升立体感

自定义样式

如需自定义样式,可以修改组件的样式部分:

css 复制代码
.activity-float-button {
  width: 120rpx; // 自定义宽度
  height: 120rpx; // 自定义高度
  background: linear-gradient(135deg, #007aff 0%, #5ac8fa 100%); // 自定义背景色

  .button-text {
    font-size: 32rpx; // 自定义文字大小
  }
}

交互说明

拖动规则

  1. 长按并拖动可移动按钮位置
  2. 拖动结束后自动吸附到最近的屏幕边缘(左边缘或右边缘)
  3. 按钮不会超出屏幕边界

点击规则

  • 纯点击:跳转到 /pagesActivity/list/list 页面
  • 拖动后松开:不触发跳转

跳转路径

默认跳转到 pagesActivity/list/list 页面,如需修改跳转路径,请编辑组件源码中的 handleClick 方法:

javascript 复制代码
const handleClick = () => {
  if (hasMoved.value) return;

  uni.navigateTo({
    url: "/your/custom/path", // 修改为你想要的跳转路径
  });
};

技术实现

核心逻辑

  1. 定位 :使用 position: fixed 固定定位
  2. 拖动 :通过 touchstarttouchmovetouchend 事件实现
  3. 吸附:计算屏幕中心点,判断吸附到左边缘或右边缘
  4. 边界限制:防止按钮超出屏幕可视区域
  5. 操作区分:通过移动距离判断是拖动还是点击

注意事项

  • 组件使用 uni.navigateTo 进行页面跳转
  • 按钮层级 z-index: 9999,确保在最上层显示
  • 支持小程序和 H5 平台

兼容性

  • ✅ 微信小程序
  • ✅ H5
  • ✅ 其他 uni-app 支持的平台

示例效果

Q: 按钮遮挡了页面内容怎么办?

A: 长按按钮并拖动到其他位置即可,按钮会自动吸附到屏幕边缘。

Q: 如何修改按钮文字?

A: 编辑组件模板中的 .button-text 内容即可。

Q: 如何在特定页面隐藏按钮?

A: 使用 v-ifv-show 控制组件的显示:

html 复制代码
<ActivityFloatButton v-if="showButton" />

Q: 拖动后位置会保存吗?

A: 当前版本拖动后位置不会持久化保存,刷新页面会重置到右下角。如需保存位置,可以使用 uni.setStorageSync 存储。

相关推荐
Web_Lys1 小时前
css设置滚动条样式不生效【antDesign UI Table滚动条样式无法自定义 解决方案】
前端·css
a1117761 小时前
星球浏览 漫游(纯html 开源)
前端·开源·html
郝学胜-神的一滴1 小时前
FastAPI:Python 高性能 Web 框架的优雅之选
开发语言·前端·数据结构·python·算法·fastapi
慧一居士2 小时前
vite 使用说明和示例演示
前端
CDwenhuohuo2 小时前
var面试题
开发语言·javascript·ecmascript
PD我是你的真爱粉2 小时前
深入理解 Event Loop:JavaScript 的“心脏起搏器”
开发语言·javascript·ecmascript
牢七2 小时前
反序列化重点模块 private Object readOrdinaryObject(boolean unshared)废案与反思
java·服务器·前端
NEXT062 小时前
数组转树与树转数组
前端·数据结构·面试
We་ct2 小时前
浏览器 Reflow(重排)与Repaint(重绘)全解析
前端·面试·edge·edge浏览器