Kros的博客 Kros的博客
首页
  • CSS
  • 工具
  • Vue
  • js
  • Vue3
  • 算法
  • 折腾笔记
一言
  • 分类
  • 标签
  • 归档
码云

Kros

凡心所向,素履以往,生如逆旅,一苇以航
首页
  • CSS
  • 工具
  • Vue
  • js
  • Vue3
  • 算法
  • 折腾笔记
一言
  • 分类
  • 标签
  • 归档
码云
  • CSS

  • JavaScript

  • 工具

  • Vue

    • vue批量引入组件
    • vue中watch用法
    • vue使用基础JSX语法
    • vue首页白屏
    • vue长列表优化
    • 模板文件中script、template、style存在必要性
    • popup弹窗组件
    • 使用$mount和extend扩展vue
    • 扩展toast合并popup(按需加载)
    • canvas实现弧形进度条
    • vue中使用CSS Modules
    • vue实现无限轮播图
    • vue半圆形菜单
    • 纯CSS实现圆形进度条
    • v-model简单实现
    • vue带箭头下拉框
    • vue事件修饰符
    • vue鼠标悬浮显示提示文字
    • vue添加发布版本号方法
    • vue配置全局样式文件
    • vue在env development中设置全局变量不生效
    • vue引入less或scss全局变量
    • vue中使用Keepalive
    • vue2为什么只支持一个根节点
    • vue打包优化分析工具—webpack-bundle-analyzer
    • vue使用echarts(按需加载)
    • echarts柱形图重合堆叠
    • vue使用draggable实现多列拖拽(解决空列不能拖拽问题)
    • vue禁止三方库打包到bundle中
    • npm run serve运行背后的思考
    • vxe-table树结构不允许insert
    • vue组件延迟加载
    • 多页面应用配置
    • vue项目常用优化
    • vue实现拖动拼图验证码
    • vue虚拟dom
    • canvas心形动画
    • canvas绘制玫瑰曲线
    • element表单validateField验证部分字段
    • element日期选择限制今天以后并精确到小时
    • 前端使用JSEncrypt和node-rsa进行rsa加密传输和接收解密
    • elementui日期选择器生日只选择月和日不选择年并隐藏年份
    • el-upload组件第二次点击手动submit时不生效
    • element表单内输入框使用@keyup enter native回车时会刷新页面
  • antdv踩坑记录

  • Vue3

  • 前端
  • Vue
kros
2021-01-10

vue半圆形悬浮菜单

我们使用的悬浮菜单按钮一般是固定布局要不就是伸缩布局,固定菜单不用多说很简单,里主要介绍伸缩菜单的写法。

# 固定方向伸缩菜单

固定方向是指在水平或竖直方向伸缩,水平方向控制width变化,竖直方向控制高度变化即可,可以将menu提到外层与列表按钮分开更好控制一点。代码仅做参考如需使用悬浮菜单请参考Fab悬浮按钮 效果如下:

效果

代码:

<template>
	<div class="container">
		<!-- 向下menu -->
		<!-- <ul :style="{width: open ? '135px' : '45px'}" style="display: flex; flex-direction: row;">					 -->
		<ul :style="{height: open ? '135px' : '45px'}">	
			<li class="menu" @click="open = !open"></li>
			<li class="item" v-show="open">
				<img src="../../assets/item.png" alt="">
				<div>item1</div>
			</li>
			<li class="item" v-show="open">
				<img src="../../assets/item.png" alt="">
				<div>item2</div>
			</li>
		</ul>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				open: false
			}
		}
	}
</script>

<style scoped>
	.container {
		position: fixed;
		z-index: 10000;
		height: 100vh;
	}	
	
	ul {
		position: absolute;
		top: 60%;
		left: 10px;
		width: 45px;
		padding: 0;
		height: 45px;
		list-style-type: none;
		border-radius: 45px;
		background-color: #007AFF;
		transition: all .3s;
	}
	
	ul .menu {		
		width: 45px;
		height: 45px;
		background-color: #FFFFFF;
		border-radius: 50%;
		background-image: url(../../assets/plus.png);
		background-size: 25px 25px;
		background-position: center;
		background-repeat: no-repeat;
		box-shadow: 0 0 5px rgba(1,1,1,.4);
	}
	
	ul .item {
		width: 45px;
		height: 45px;
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: space-around;
	}
	
	ul .item img {
		width: 20px;
		height: 20px;
	}
	
	ul .item div {
		font-size: 10px;
		color: #FFFFFF;
	}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

# 旋转菜单

旋转菜单是指菜单项以旋转方式分布,效果如下:

旋转

有两种实现方式,一种是对菜单项进行absolute定位并计算在圆弧位置上的left和top,二是让菜单项以菜单按钮进行不同角度旋转旋转。

使用absolute方法,假设菜单按钮在圆中心点,半径r,那么对应角度的位置可以计算得到:left=sin(a)*r,top=cos(a)*r,这里有一点要注意:top是以父物体为起点计算出的值恰好跟真实值相反,所有真正的top等于-cos(a)*r。

absolute

旋转方法,旋转方法比较简单,以菜单按钮为中心固定半径和角度旋转即可,这里主要利用css的两个属性:

  • transform: rotateZ():以z轴方向旋转
  • transform-origin:设置物体中心点,物体以中心点旋转

旋转

完整代码:

<template>
	<div class="container">
		<!-- 固定left top -->
		<!-- <div class="menu" @click="open = !open">
			<div class="item" v-for="(item, index) in 4" :key="index" :style="{left: open ? leftDirPos(index) : 0, top: open ? topDirPos(index) : 0, display: open ? 'flex' : 'none'}">
				<img src="../../assets/item.png" alt="">
				<div>item{{index}}</div>
			</div>
		</div> -->
		<!-- 弧形item -->
		<div class="menu" @click="clickMenu" :style="{overflow: hidden ? 'hidden' : 'visible'}">
			<div class="item rotateItem" v-for="(item, index) in 4" :key="index" :style="[rotateStyle(index)]">
				<div class="item" :style="{transform: 'rotateZ(' + -index * angle + 'deg)'}">
					<img src="../../assets/item.png" alt="">
					<div>idx{{index}}</div>
				</div>
			</div>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				hidden: false, // 解决display切换transition不触发
				open: true,    // 是否显示item
				r: 60,         // 半径
				initial: 10,   // 初始旋转角度
				angle: 60,     // item间隔角度
			}
		},
		computed: {
			topDirPos() {
				return (index) => {
					// 注意角度要先转弧度 转换关系:角度*2*pi/360
					let c =  Math.cos(2 * Math.PI * (this.angle * index + this.initial) / 360) * this.r ;
					return -c + 'px';
				}
			},
			leftDirPos() {
				return (index) => {
					let c =  Math.sin(2 * Math.PI * (this.angle * index + this.initial) / 360) * this.r;
					return c + 'px';
				}
			},
			rotateStyle() {
				return (index) => {
						if(!this.open) {
							return {
								transform: 'rotateZ(0deg)',
							}
						}else {
							return {
								transform: 'rotateZ(' + index * this.angle + 'deg)',
							}
						}
						
					}
			}
		},
		methods: {
			clickMenu() {
				if(this.open){
					this.open = false;
					setTimeout(() => {
						this.hidden = true;
					}, 300);
				}else {
					this.hidden = false;
					setTimeout(() => {
						this.open = true;
					}, 50);
					console.log(this.hidden);
				}
			}
		},
		created() {
		}
	}
</script>

<style scoped>
	.container {
		position: fixed;
		z-index: 10000;
		height: 100vh;
	}	
	
	.menu {
		position: absolute;
		top: 60%;
		left: 10px;
		width: 45px;
		height: 45px;
		border-radius: 50%;
		background-color: #FFFFFF;
		background-image: url(../../assets/plus.png);
		background-size: 25px 25px;
		background-position: center;
		background-repeat: no-repeat;
		box-shadow: 0 0 5px rgba(1,1,1,.4);
	}
	
	.menu .item{
		position: absolute;
		left: 0;
		top: 0;
		width: 40px;
		height: 40px;
		display: flex;
		align-items: center;
		flex-direction: column;
		justify-content: space-around;
		border-radius: 40px;
		background-color: #0074D9;
		transition: all .3s;
	}
	
	.menu .rotateItem{
		top: -60px;
		transform-origin: center 80px; /* 以父物体中心点旋转 top + item.height/2 */
	}
	
	.menu .item img {
		width: 15px;
		height: 15px;
	}
	
	.menu .item div {
		font-size: 10px;
		color: #FFFFFF;
	}
</style>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#vue
上次更新: 2025/09/05, 8:09:00
vue实现无限轮播图
纯CSS实现圆形进度条

← vue实现无限轮播图 纯CSS实现圆形进度条→

最近更新
01
Find the next perfect square
09-05
02
Regex validate PIN code
09-05
03
Find the odd int
09-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 kros king
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
icon-heart-o icon-heart icon-infinity icon-pause icon-play link next prev