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 存储。

相关推荐
kyriewen6 分钟前
豆包和千问同时关了智能体,我用它们搭的 3 个自动化全废了——迁移方案整理
前端·javascript·ai编程
前端一小卒19 分钟前
我用 TypeScript 从零手写了一个 Claude Code,然后发现它的核心只有 30 行
前端·agent
铁皮饭盒38 分钟前
用 Bun.cron 定时 7 月 7 日,为啥? 看图1
javascript
大圣编程2 小时前
Python中continue语句的用法是什么?
开发语言·前端·python
yuhaiqiang2 小时前
随手 vibecoding 的浏览器插件已经 6000 多次下载,聊聊他的产品设计
前端·后端·面试
之歆2 小时前
Vue商品详情与放大镜组件
前端·javascript·vue.js
再吃一根胡萝卜3 小时前
如何把小米 MiMo 接入 CodeBuddy,打造私有 Agent
前端
负责的蛋挞4 小时前
异步HttpModule的实现方式
java·服务器·前端
丹宇码农7 小时前
把 HLS 字幕玩出花:zwPlayer 如何让 M3U8 视频支持全文搜索、翻译与码率自适应
前端·javascript·音视频·hls·视频播放器