?背景
?前几天在某短视频平台刷到一个类似的效果,觉得蛮好玩的,将它应用到你的小程序吧~
?文末分享源代码。记得点赞+关注+收藏!
1.实现效果?
2.实现原理☕️
2.1 引入阿里巴巴矢量图标
可参考文章:微信小程序引入外部icon(阿里巴巴矢量图标)
注:生成彩色的图标,需在项目设置中勾选彩色哦~
2.2 圆环菜单布局
1️⃣将菜单基于圆环布局,假设此圆只有一个菜单,让它定位到下图黄色菜单所示位置
注意:transform执行顺序从最外面字段开始~
2️⃣ rotate角度的获取:一个圆为360deg,将其等分,2个菜单偏移角度为180deg,4个菜单偏移角度90deg,以此类推可以得到:
rotate角度为: 360deog/菜单的个数
3️⃣rotate旋转带动子元素图标的转动,将子元素设置反方向的转动
2.3 CSS3 2D 转换+transaction
CSS3 transition 属性:
transition 属性设置元素当过渡效果。注:始终指定transition-duration属性,否则持续时间为0,transition不会有任何效果。
CSS3 2D 转换:
函数 | 描述 |
---|---|
translateX(n) | 定义 2D 转换,沿着 X 轴移动元素。 |
scale(x,y) | 定义 2D 缩放转换,改变元素的宽度和高度。 |
3.实现步骤?
1️⃣:定义菜单显示与否字段show_menu(默认false),当前所选菜单索引currIndex(默认’ ')
2️⃣:画一个盒子包括以下内容,默认设置属性为:
opacity: 0;
transform: scale(0);
visibility: hidden;
transition: all 0.5s;
3️⃣:画一个盒子元素,里面放一个可爱的图标,盒子设置伪元素 “点我吧”,当show_menu为true,为该盒子添加激活样式,即伪元素opacity设置为0
4️⃣:当show_menu为true,设置圆形菜单scale(1);opacity:1; visibility: visible;计算菜单个数,设置rotate值,并调整合适的translateX偏移
- rotate角度根据菜单个数定
- 偏移量设置
--n:{{index}};//index为菜单当前索引
--deg:{{360/ menu.length}}deg;//menu为菜单列表
transform: rotate(calc(var(--deg) * var(--n))) translateX(-140rpx);
5️⃣:设置选中菜单样式
文章来源:https://uudwc.com/A/rXk96
- 当currIndex===index,选中菜单背景,基于圆形absolute垂直居中定位,设置rotate+translateX,rotate角度为-deg*currIndex相关,translateX设置偏移量向外扩大一丢丢即可
<view class="menu-box-active" style="--n:{{currIndex}};--deg:{{360/ menu.length}}deg"</view>
transform: rotate(calc(var(--deg) * var(--n))) translateX(-200rpx);
opacity: 1;
visibility: visible;
- 当currIndex===index,选中菜单,translateX设置偏移量向外扩大一丢丢即可,为图标并添加scale缩放效果
<view style="--n:{{index}};--deg:{{360/ menu.length}}deg" class="menu-box-item {{currIndex===index && 'active'}}" catchtap="clickActive" data-index="{{index}}">
<icon class="iconfont icon {{item.icon}}" style="--deg:{{-360/ menu.length}}deg"></icon>
</view>
.menu-box-item.active {
transform: rotate(calc(var(--deg) * var(--n))) translateX(-200rpx);
}
.menu-box-item.active icon {
animation: scale 1s ease-in-out;
}
@keyframes scale {
100% {
transform: scale(1.9);
}
}
4.实现代码?
<view class="container">
<view class="menu-box {{show_menu && 'active'}}">
<block wx:for="{{menu}}" wx:key="menu">
<view style="--n:{{index}};--deg:{{360/ menu.length}}deg" class="menu-box-item {{currIndex===index && 'active'}}" catchtap="clickActive" data-index="{{index}}">
<icon class="iconfont icon {{item.icon}}" style="--deg:{{-360/ menu.length}}deg"></icon>
</view>
</block>
<view class="menu-box-active" style="--n:{{currIndex}};--deg:{{360/ menu.length}}deg"></view>
</view>
<view class="menu-add-box {{show_menu && 'active'}}" catchtap="showMenu">
<icon class="iconfont icon {{menu_add}}"></icon>
</view>
</view>
@import "../utils/icon-font.wxss";
page {
--bg: pink;
background: var(--bg);
height: 100%;
display: flex;
align-items: center;
justify-content: center;
--active: orange;
--bgcolor: rgb(190, 127, 67);
}
/* 圆形菜单 */
.container {
width: 400rpx;
height: 400rpx;
position: relative;
}
.menu-box {
width: 400rpx;
height: 400rpx;
position: relative;
opacity: 0;
transform: scale(0);
visibility: hidden;
transition: all 0.5s;
}
/*filter(滤镜)修饰父元素背景,影响子元素问题解决 */
.menu-box::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: var(--bgcolor);
border-radius: 50%;
filter: drop-shadow(0px 0px 8px var(--active));
-webkit-filter: drop-shadow(0px 0px 8px var(--active));
}
.menu-box.active {
transform: scale(1);
opacity: 1;
visibility: visible;
}
.icon {
font-size: 56rpx;
transition: all 0.5s;
}
/* 点我吧 */
.menu-add-box {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s;
position: absolute;
top: calc(50% - 60rpx);
left: calc(50% - 60rpx);
background: var(--bg);
}
.menu-add-box::after {
content: '^点我吧^';
position: absolute;
bottom: -20rpx;
font-size: 22rpx;
color: #222;
font-weight: bold;
font-style: italic;
transition: all .5s;
opacity: 1;
}
.menu-add-box.active::after {
font-size: 0rpx;
opacity: 0;
}
.menu-add-box icon {
transform: scale(1.5);
}
.menu-add-box.active icon {
transform: rotate(135deg);
}
/* 每一项菜单 */
.menu-box-item {
width: 80rpx;
height: 80rpx;
position: absolute;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
top: calc(50% - 40rpx);
left: calc(50% - 40rpx);
transition: all 0.5s;
transform: rotate(calc(var(--deg) * var(--n))) translateX(-140rpx);
}
.menu-box-item icon {
transform: rotate(calc(var(--deg) * var(--n)));
transition: all 0.5s;
}
.menu-box-item.active {
transform: rotate(calc(var(--deg) * var(--n))) translateX(-200rpx);
}
.menu-box-item.active icon {
animation: scale 1s ease-in-out;
}
@keyframes scale {
100% {
transform: scale(1.9);
}
}
/* 选中的背景框 */
.menu-box-active {
position: absolute;
width: 100rpx;
height: 100rpx;
background: var(--active);
box-shadow: 0 0 0 10rpx var(--bg);
border-radius: 50%;
pointer-events: none;
transition: all 0.5s;
transform-origin: center;
top: calc(50% - 50rpx);
left: calc(50% - 50rpx);
z-index: 1;
opacity: 0;
visibility: hidden;
}
.menu-box-item.active~.menu-box-active {
transform: rotate(calc(var(--deg) * var(--n))) translateX(-200rpx);
opacity: 1;
visibility: visible;
}
Page({
data: {
menu_add: "icon-susumakalong",
menu: [
{
icon: "icon-susushutiao",
name: "薯条",
},
{
icon: "icon-susutiantong",
name: "甜筒",
},
{
icon: "icon-susutongluoshao",
name: "铜锣烧",
},
{
icon: "icon-susuxiaoxiongruantang-46",
name: "小熊软糖",
},
{
icon: "icon-susushiwutubiao-49",
name: "提拉米苏",
},
{
icon: "icon-susunailao",
name: "奶酪",
},
{
icon: "icon-susuhanbao",
name: "汉堡",
},
{
icon: "icon-susupaofu-53",
name: "泡芙",
},
],
show_menu: false,
currIndex: "",
},
showMenu() {
let { show_menu } = this.data;
this.setData({
show_menu: !show_menu,
currIndex: "",
});
},
clickActive(e) {
let { index } = e.currentTarget.dataset;
if (this.data.currIndex === index || index === undefined) return false;
this.setData({
currIndex: index,
});
},
});
5.写在最后?
看完本文如果觉得有用,记得点赞+关注+收藏鸭 ?
更多小程序相关,关注?苏苏的bug,?苏苏的github,?苏苏的码云~
参考链接:?
1️⃣:微信小程序引入外部icon(阿里巴巴矢量图标)
2️⃣:CSS3 transition 属性
3️⃣:CSS3 2D 转换文章来源地址https://uudwc.com/A/rXk96