FloatingActionButton.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <template>
  2. <view class="fab-container" :style="positionStyle">
  3. <!-- 菜单项 -->
  4. <view class="fab-menu" v-if="isExpanded">
  5. <view
  6. v-for="(item, index) in menuItems"
  7. :key="index"
  8. class="menu-item"
  9. @click="handleMenuItemClick(item, index)"
  10. >
  11. <text class="menu-item-text">{{ item.text }}</text>
  12. <text class="menu-item-icon">{{ item.icon }}</text>
  13. </view>
  14. </view>
  15. <!-- 主悬浮按钮 -->
  16. <view
  17. class="fab-main"
  18. @click="toggleMenu"
  19. :style="{ backgroundColor: bgColor }"
  20. >
  21. <text class="fab-icon" :class="{ rotated: isExpanded }">{{ icon }}</text>
  22. </view>
  23. </view>
  24. </template>
  25. <script>
  26. export default {
  27. name: 'FloatingActionButton',
  28. props: {
  29. // 菜单项列表 [{ text: '分享', icon: '📤' }]
  30. menuItems: {
  31. type: Array,
  32. default: () => []
  33. },
  34. // 主按钮图标
  35. icon: {
  36. type: String,
  37. default: '+'
  38. },
  39. // 主按钮背景色
  40. bgColor: {
  41. type: String,
  42. default: '#007AFF'
  43. },
  44. // 距离底部的距离(px)
  45. bottom: {
  46. type: Number,
  47. default: 100
  48. },
  49. // 距离右侧的距离(px)
  50. right: {
  51. type: Number,
  52. default: 30
  53. }
  54. },
  55. data() {
  56. return {
  57. isExpanded: false
  58. }
  59. },
  60. computed: {
  61. positionStyle() {
  62. return {
  63. bottom: this.bottom + 'px',
  64. right: this.right + 'px'
  65. }
  66. }
  67. },
  68. methods: {
  69. toggleMenu() {
  70. this.isExpanded = !this.isExpanded
  71. this.$emit('fabClick', this.isExpanded)
  72. },
  73. handleMenuItemClick(item, index) {
  74. this.$emit('menuItemClick', { item, index })
  75. // 点击后自动收起菜单
  76. this.isExpanded = false
  77. },
  78. // 手动展开
  79. expand() {
  80. this.isExpanded = true
  81. },
  82. // 手动收起
  83. collapse() {
  84. this.isExpanded = false
  85. }
  86. }
  87. }
  88. </script>
  89. <style lang="scss" scoped>
  90. .fab-container {
  91. position: fixed;
  92. z-index: 999;
  93. display: flex;
  94. flex-direction: column-reverse;
  95. align-items: flex-end;
  96. }
  97. .fab-main {
  98. width: 100rpx;
  99. height: 100rpx;
  100. border-radius: 50%;
  101. display: flex;
  102. align-items: center;
  103. justify-content: center;
  104. box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.2);
  105. cursor: pointer;
  106. transition: all 0.3s ease;
  107. &:active {
  108. transform: scale(0.95);
  109. }
  110. }
  111. .fab-icon {
  112. font-size: 48rpx;
  113. color: #fff;
  114. transition: transform 0.3s ease;
  115. &.rotated {
  116. transform: rotate(45deg);
  117. }
  118. }
  119. .fab-menu {
  120. display: flex;
  121. flex-direction: column;
  122. margin-bottom: 20rpx;
  123. animation: slideUp 0.3s ease;
  124. }
  125. .menu-item {
  126. display: flex;
  127. align-items: center;
  128. justify-content: flex-end;
  129. margin-bottom: 20rpx;
  130. cursor: pointer;
  131. transition: all 0.3s ease;
  132. &:active {
  133. transform: scale(0.95);
  134. }
  135. }
  136. .menu-item-text {
  137. background: rgba(0, 0, 0, 0.7);
  138. color: #fff;
  139. padding: 10rpx 20rpx;
  140. border-radius: 30rpx;
  141. font-size: 24rpx;
  142. white-space: nowrap;
  143. }
  144. .menu-item-icon {
  145. width: 80rpx;
  146. height: 80rpx;
  147. background: #fff;
  148. border-radius: 50%;
  149. display: flex;
  150. align-items: center;
  151. justify-content: center;
  152. margin-left: 20rpx;
  153. box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
  154. font-size: 40rpx;
  155. }
  156. @keyframes slideUp {
  157. from {
  158. opacity: 0;
  159. transform: translateY(20rpx);
  160. }
  161. to {
  162. opacity: 1;
  163. transform: translateY(0);
  164. }
  165. }
  166. </style>