uni-fab.vue 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <view>
  3. <view class="fab-box fab" :class="{
  4. leftBottom: leftBottom,
  5. rightBottom: rightBottom,
  6. leftTop: leftTop,
  7. rightTop: rightTop
  8. }">
  9. <view class="fab-circle" :class="{
  10. left: horizontal === 'left' && direction === 'horizontal',
  11. top: vertical === 'top' && direction === 'vertical',
  12. bottom: vertical === 'bottom' && direction === 'vertical',
  13. right: horizontal === 'right' && direction === 'horizontal'
  14. }"
  15. :style="{ 'background-color': styles.buttonColor }" @click="_onClick">
  16. <text class="uni-icon uni-icon-plusempty" :class="{ active: isShow }"></text>
  17. </view>
  18. <view class="fab-content" :class="{
  19. left: horizontal === 'left',
  20. right: horizontal === 'right',
  21. flexDirection: direction === 'vertical',
  22. flexDirectionStart: flexDirectionStart,
  23. flexDirectionEnd: flexDirectionEnd
  24. }"
  25. :style="{ width: boxWidth, height: boxHeight, background: styles.backgroundColor }">
  26. <view v-if="flexDirectionStart || horizontalLeft" class="fab-item first"></view>
  27. <view class="fab-item" v-for="(item, index) in content" :key="index" :class="{ active: isShow }" :style="{
  28. color: item.active ? styles.selectedColor : styles.color
  29. }"
  30. @click="_onItemClick(index, item)">
  31. <image class="content-image" :src="item.active ? item.selectedIconPath : item.iconPath" mode="widthFix"></image>
  32. <text class="text">{{ item.text }}</text>
  33. </view>
  34. <view v-if="flexDirectionEnd || horizontalRight" class="fab-item first"></view>
  35. </view>
  36. </view>
  37. </view>
  38. </template>
  39. <script>
  40. export default {
  41. props: {
  42. pattern: {
  43. type: Object,
  44. default () {
  45. return {};
  46. }
  47. },
  48. horizontal: {
  49. type: String,
  50. default: 'left'
  51. },
  52. vertical: {
  53. type: String,
  54. default: 'bottom'
  55. },
  56. direction: {
  57. type: String,
  58. default: 'horizontal'
  59. },
  60. content: {
  61. type: Array,
  62. default () {
  63. return [];
  64. }
  65. },
  66. show: {
  67. type: Boolean,
  68. default: false
  69. }
  70. },
  71. data() {
  72. return {
  73. fabShow: false,
  74. flug: true,
  75. isShow: false,
  76. styles: {
  77. color: '#3c3e49',
  78. selectedColor: '#007AFF',
  79. backgroundColor: '#fff',
  80. buttonColor: '#3c3e49'
  81. }
  82. };
  83. },
  84. created() {
  85. this.isShow = this.show;
  86. if (this.top === 0) {
  87. this.fabShow = true;
  88. }
  89. // 初始化样式
  90. this.styles = Object.assign({}, this.styles, this.pattern);
  91. },
  92. methods: {
  93. _onClick() {
  94. this.isShow = !this.isShow;
  95. },
  96. open() {
  97. this.isShow = true;
  98. },
  99. close() {
  100. this.isShow = false;
  101. },
  102. /**
  103. * 按钮点击事件
  104. */
  105. _onItemClick(index, item) {
  106. this.$emit('trigger', {
  107. index,
  108. item
  109. });
  110. },
  111. /**
  112. * 获取 位置信息
  113. */
  114. getPosition(types, paramA, paramB) {
  115. if (types === 0) {
  116. return this.horizontal === paramA && this.vertical === paramB;
  117. } else if (types === 1) {
  118. return this.direction === paramA && this.vertical === paramB;
  119. } else if (types === 2) {
  120. return this.direction === paramA && this.horizontal === paramB;
  121. } else {
  122. return this.isShow && this.direction === paramA ?
  123. this.contentWidth :
  124. this.contentWidthMin;
  125. }
  126. }
  127. },
  128. watch: {
  129. pattern(newValue, oldValue) {
  130. console.log(JSON.stringify(newValue));
  131. this.styles = Object.assign({}, this.styles, newValue);
  132. }
  133. },
  134. computed: {
  135. contentWidth(e) {
  136. return uni.upx2px((this.content.length + 1) * 110 + 20) + 'px';
  137. },
  138. contentWidthMin() {
  139. return uni.upx2px(110) + 'px';
  140. },
  141. // 动态计算宽度
  142. boxWidth() {
  143. return this.getPosition(3, 'horizontal');
  144. },
  145. // 动态计算高度
  146. boxHeight() {
  147. return this.getPosition(3, 'vertical');
  148. },
  149. // 计算左下位置
  150. leftBottom() {
  151. return this.getPosition(0, 'left', 'bottom');
  152. },
  153. // 计算右下位置
  154. rightBottom() {
  155. return this.getPosition(0, 'right', 'bottom');
  156. },
  157. // 计算左上位置
  158. leftTop() {
  159. return this.getPosition(0, 'left', 'top');
  160. },
  161. rightTop() {
  162. return this.getPosition(0, 'right', 'top');
  163. },
  164. flexDirectionStart() {
  165. return this.getPosition(1, 'vertical', 'top');
  166. },
  167. flexDirectionEnd() {
  168. return this.getPosition(1, 'vertical', 'bottom');
  169. },
  170. horizontalLeft() {
  171. return this.getPosition(2, 'horizontal', 'left');
  172. },
  173. horizontalRight() {
  174. return this.getPosition(2, 'horizontal', 'right');
  175. }
  176. }
  177. };
  178. </script>
  179. <style scoped>
  180. .fab-box {
  181. position: fixed;
  182. display: flex;
  183. justify-content: center;
  184. align-items: center;
  185. z-index: 2;
  186. }
  187. .fab-box.top {
  188. width: 60upx;
  189. height: 60upx;
  190. right: 30upx;
  191. bottom: 60upx;
  192. border: 1px #5989b9 solid;
  193. background: #6699cc;
  194. border-radius: 10upx;
  195. color: #fff;
  196. transition: all 0.3;
  197. opacity: 0;
  198. }
  199. .fab-box.active {
  200. opacity: 1;
  201. }
  202. .fab-box.fab {
  203. z-index: 10;
  204. }
  205. .fab-box.fab.leftBottom {
  206. left: 30upx;
  207. bottom: 60upx;
  208. }
  209. .fab-box.fab.leftTop {
  210. left: 30upx;
  211. top: 80upx;
  212. /* #ifdef H5 */
  213. top: calc(80upx + var(--window-top));
  214. /* #endif */
  215. }
  216. .fab-box.fab.rightBottom {
  217. right: 30upx;
  218. bottom: 60upx;
  219. }
  220. .fab-box.fab.rightTop {
  221. right: 30upx;
  222. top: 80upx;
  223. /* #ifdef H5 */
  224. top: calc(80upx + var(--window-top));
  225. /* #endif */
  226. }
  227. .fab-circle {
  228. display: flex;
  229. justify-content: center;
  230. align-items: center;
  231. position: absolute;
  232. width: 110upx;
  233. height: 110upx;
  234. background: #3c3e49;
  235. /* background: #5989b9; */
  236. border-radius: 50%;
  237. box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.2);
  238. z-index: 11;
  239. }
  240. .fab-circle.left {
  241. left: 0;
  242. }
  243. .fab-circle.right {
  244. right: 0;
  245. }
  246. .fab-circle.top {
  247. top: 0;
  248. }
  249. .fab-circle.bottom {
  250. bottom: 0;
  251. }
  252. .fab-circle .uni-icon-plusempty {
  253. color: #ffffff;
  254. font-size: 80upx;
  255. transition: all 0.3s;
  256. font-weight: bold;
  257. }
  258. .fab-circle .uni-icon-plusempty.active {
  259. transform: rotate(135deg);
  260. }
  261. .fab-content {
  262. background: #6699cc;
  263. box-sizing: border-box;
  264. display: flex;
  265. border-radius: 100upx;
  266. overflow: hidden;
  267. box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.1);
  268. transition: all 0.2s;
  269. width: 110upx;
  270. }
  271. .fab-content.left {
  272. justify-content: flex-start;
  273. }
  274. .fab-content.right {
  275. justify-content: flex-end;
  276. }
  277. .fab-content.flexDirection {
  278. flex-direction: column;
  279. justify-content: flex-end;
  280. }
  281. .fab-content.flexDirectionStart {
  282. flex-direction: column;
  283. justify-content: flex-start;
  284. }
  285. .fab-content.flexDirectionEnd {
  286. flex-direction: column;
  287. justify-content: flex-end;
  288. }
  289. .fab-content .fab-item {
  290. display: flex;
  291. flex-direction: column;
  292. justify-content: center;
  293. align-items: center;
  294. width: 110upx;
  295. height: 110upx;
  296. font-size: 24upx;
  297. color: #fff;
  298. opacity: 0;
  299. transition: opacity 0.2s;
  300. }
  301. .fab-content .fab-item.active {
  302. opacity: 1;
  303. }
  304. .fab-content .fab-item .content-image {
  305. width: 50upx;
  306. height: 50upx;
  307. margin-bottom: 5upx;
  308. }
  309. .fab-content .fab-item.first {
  310. width: 110upx;
  311. }
  312. </style>