项目地址
Rotating Navigation Animation
展示效果
Rotating Navigation Animation
实现思路
结构主要分为两部分,绕左上角旋转的部分:包括按钮圆盘和内容区,以及左下角移出的导航栏部分。
整个界面只在左上角圆盘的按钮点击时发生改变,而且这种改变只有两种状态:旋转内容和转盘并显示导航栏、内容和转盘归位并隐藏导航栏。因此可以考虑添加一个class属性的方式实现效果,而这个class属性会分别作用在几个需要改变的元素上。
- 点击按钮后旋转,圆盘和内容都绕左上角作为原点进行旋转,可以考虑将两部分放到一个父盒子里,通过添加show-nav属性改变样式
- 圆盘和内容旋转的角度略有不同,圆盘要转动90度,而内容转动只有20度,所以整体应该是选择20度,而圆盘再额外旋转70度
- 导航栏在设计好布局后,先全部移动到页面左侧隐藏区域,在需要显示时再移动回来
实现细节
HTML结构
案例中用到了一些按钮的图标,这些图标来自于font-awesome库中,这里直接在head中声明cdn地址进行引用,在页面加载时会自动载入图标样式。
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"
integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog=="
crossorigin="anonymous" />
<link rel="stylesheet" href="style.css" />
<title>Rotating Navigation</title>
</head>
按钮圆盘和正文内容放到一个container
里面,原案例中圆盘外面还包了一层fixed的circle-container
,然后在里面使用relative定位的circle
来给按钮定位提供参考位置,不过实际上也可以不需要这样做,因为absolute定位参考的父元素位置只要是非static定位就行。不过遵循子绝父相的原则也确实是一种良好习惯。
正文文字直接用乱数假文来测试排版。
导航栏常规用列表进行排布。
<div class="container">
<div class="circle">
<button id="close"><i class="fas fa-times"></i></button>
<button id="open"><i class="fas fa-bars"></i></button>
</div>
<div class="content">
<h1>Amazing Article</h1>
<small>Florin Pop</small>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusantium quia in ratione dolores cupiditate, maxime
aliquid impedit dolorem nam dolor omnis atque fuga labore modi veritatis porro laborum minus, illo, maiores
recusandae cumque ipsa quos. Tenetur, consequuntur mollitia labore pariatur sunt quia harum aut. Eum maxime
dolorem provident natus veritatis molestiae cumque quod voluptates ab non, tempore cupiditate? Voluptatem,
molestias culpa. Corrupti, laudantium iure aliquam rerum sint nam quas dolor dignissimos in error placeat quae
temporibus minus optio eum soluta cupiditate! Cupiditate saepe voluptates laudantium. Ducimus consequuntur
perferendis consequatur nobis exercitationem molestias fugiat commodi omnis. Asperiores quia tenetur nemo ipsa.
</p>
<h3>My Dog</h3>
<img
src="https://images.unsplash.com/photo-1507146426996-ef05306b995a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=2100&q=80"
alt="doggy" />
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Sit libero deleniti rerum quo, incidunt vel
consequatur culpa ullam. Magnam facere earum unde harum. Ea culpa veritatis magnam at aliquid. Perferendis totam
placeat molestias illo laudantium? Minus id minima doloribus dolorum fugit deserunt qui vero voluptas, ut quia
cum amet temporibus veniam ad ea ab perspiciatis, enim accusamus asperiores explicabo provident. Voluptates
sint, neque fuga cum illum, tempore autem maxime similique laborum odio, magnam esse. Aperiam?</p>
</div>
</div>
<nav>
<ul>
<li><i class="fas fa-home"></i><a href="#"> Home</a></li>
<li><i class="fas fa-user-alt"></i><a href="#">About</a></li>
<li><i class="fas fa-envelope"></i><a href="#">Contact</a></li>
</ul>
</nav>
CSS样式
container
容器样式定义盒子内容为全屏显示(去除滚动条),以及旋转变换的原点为左上角
容器带上show-nav的class后整体逆时针旋转20度
.container{
background-color: #fafafa;
padding: 50px;
height: 100vh;
transform-origin: left top;
transition: transform 0.5s linear;
}
转盘
首先设置尺寸来实现圆盘的外观,转盘本身的位置应该是脱离container的文档流,固定在左上角,通过设置偏移来控制在页面中只显示右下角部分。
但是设置偏移量会导致body整体向下移动,顶部会有100px的空区,这会导致之后旋转的原点下移,最终的效果就不一致了。此时设置padding值可以将整个body撑开(这里其实还没完全搞清楚)
.circle{
width: 200px;
height: 200px;
border-radius: 50%;
background-color: #ff7979;
position: fixed;
left: -100px;
top: -100px;
transition: transform 0.5s linear;
}
转盘上两个按钮设置为转盘半径大小,并通过设置偏移让两个按钮恰好显示在右下角圆弧的正中间。同时也定义旋转原点同样是左上角。
.circle button{
height: 100px;
background: transparent;
border: 0;
font-size: 26px;
color: #fff;
position: absolute;
left: 50%;
top: 50%;
cursor: pointer;
transform-origin: left top;
}
然后是对两个按钮的位置进行一些微调,关闭按钮逆时针旋转90度隐藏到页面左边。
.circle #open{
left: 60%;
}
.circle #close{
transform: rotate(90deg);
top: 60%;
}
最后当容器有show-nav的class后,转盘在跟随容器旋转20度的基础上,再旋转70度。
.container.show-nav .circle {
transform: rotate(-70deg);
}
内容区
首先内容整体需要做一些宽度限制,或者至少图片应该做一些宽度限制。
这里需要注意的是
.content {
max-width: 1000px;
background-color: #fafafa;
margin: 50px auto;
}
.content img {
width: 100%;
}
.content h1 {
margin: 0;
}
.content small {
color: #555;
font-style: italic;
}
.content p {
color: #333;
line-height: 1.5;
}
导航栏
导航栏部分固定位置在左下角,去除无序列表的默认样式。
表格的三项根据从上往下依次向右偏移一定距离,在隐藏时也对应需要向左移动更多的距离,当兄弟节点容器有show-nav的class后,将所有列表项的移动距离清零来达到导航栏出现的效果。
.container.show-nav + nav li {
transform: translate(0);
}
nav {
position: fixed;
bottom: 40px;
left: 0px;
z-index: 1000;
}
nav ul {
list-style: none;
padding-left: 30px;
}
nav ul li {
color: #fff;
margin: 30px 0;
transform: translateX(-100%);
transition: transform 0.4s ease-in;
}
nav ul li i {
font-size: 20px;
margin-right: 10px;
}
nav ul li + li {
margin-left: 15px;
transform: translateX(-150%);
}
nav ul li + li + li {
margin-left: 30px;
transform: translateX(-200%);
}
nav a {
text-decoration: none;
color:#fff;
transition: all 0.5s;
}
nav a:hover {
color:#ff7979;
font-weight: bold;
}
JavaScript逻辑
脚本逻辑非常简单,就是给两个按钮绑定 向container元素添加/删除 show-nav 的class即可。
const container = document.querySelector('.container');
const open = document.getElementById('open');
const close = document.getElementById('close');
open.addEventListener('click', () => container.classList.add('show-nav'));
close.addEventListener('click', () => container.classList.remove('show-nav'));
总结
- 分析结构,需要旋转的部分放一块,只需要添加一个class即可完成所有交互效果的变换。
- 旋转中心为左上角,圆盘和内容角度不相同。
- 圆盘定位脱离container文档流,设置偏移后注意body上方的空位会影响旋转变换效果,通过设置padding值消除。