[Style] navbar animation when changing fixed stat
This commit is contained in:
parent
17a204f281
commit
32608acdc3
@ -2,19 +2,32 @@
|
|||||||
@import '../font';
|
@import '../font';
|
||||||
|
|
||||||
$mobileView: 1024px;
|
$mobileView: 1024px;
|
||||||
|
$fixedTopPadding: 25px;
|
||||||
|
$fixedRightPadding: 50px;
|
||||||
|
|
||||||
#news-nav {
|
#news-nav {
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
|
// prevent slow animation update
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 25px;
|
top: $fixedTopPadding;
|
||||||
|
|
||||||
&.fixed {
|
&.fixed {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 25px;
|
top: $fixedTopPadding;
|
||||||
right: 50px;
|
right: $fixedRightPadding;
|
||||||
|
|
||||||
|
.news-nav.animating {
|
||||||
|
// from section to fixed
|
||||||
|
animation-name: move-to-side;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.returning {
|
||||||
|
// prevent navbar overlap header
|
||||||
|
position: sticky;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@ -34,8 +47,14 @@ $mobileView: 1024px;
|
|||||||
|
|
||||||
margin-left: 25px;
|
margin-left: 25px;
|
||||||
|
|
||||||
transform: translateX(0);
|
animation-duration: 0.5s;
|
||||||
transition: transform 0.5s ease-in-out;
|
animation-timing-function: ease-in-out;
|
||||||
|
animation-fill-mode: forwards;
|
||||||
|
|
||||||
|
&.animating {
|
||||||
|
// from fixed to section
|
||||||
|
animation-name: move-to-section;
|
||||||
|
}
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
margin: 0 8px;
|
margin: 0 8px;
|
||||||
@ -74,25 +93,36 @@ $mobileView: 1024px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.nav-control-button {
|
.nav-control-button {
|
||||||
color: $mid-blue;
|
display: flex;
|
||||||
position: fixed;
|
|
||||||
z-index: 5;
|
|
||||||
top: 25px;
|
|
||||||
right: 50px;
|
|
||||||
border-radius: 5px;
|
|
||||||
border: none;
|
|
||||||
font-size: 24px;
|
|
||||||
position: flex;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
position: fixed;
|
||||||
|
top: $fixedTopPadding;
|
||||||
|
right: $fixedRightPadding;
|
||||||
|
z-index: 5;
|
||||||
|
padding: 0 15px;
|
||||||
width: 55px;
|
width: 55px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: $mid-blue;
|
||||||
|
font-size: 24px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
box-shadow: -2px 0 15px $mid-blue;
|
box-shadow: -2px 0 15px $mid-blue;
|
||||||
padding: 0 15px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
p {
|
p {
|
||||||
text-align: end;
|
text-align: end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// narrower padding
|
||||||
|
@media screen and (max-width: 1365px) {
|
||||||
|
.news-nav.animating {
|
||||||
|
animation-name: slide-to-section;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.fixed .news-nav.animating {
|
||||||
|
animation-name: slide-to-side;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobileView) {
|
@media screen and (max-width: $mobileView) {
|
||||||
.nav-control-button {
|
.nav-control-button {
|
||||||
@ -103,14 +133,57 @@ $mobileView: 1024px;
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.news-nav.animating,
|
||||||
|
&.fixed .news-nav.animating {
|
||||||
|
animation-name: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $mobileView) {
|
@media screen and (max-width: $mobileView) {
|
||||||
#news-nav,
|
|
||||||
#news-nav.fixed {
|
#news-nav.fixed {
|
||||||
position: fixed;
|
|
||||||
top: 25px;
|
|
||||||
right: 20px;
|
right: 20px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move-to-section {
|
||||||
|
0% {
|
||||||
|
transform: translateX(280px - $fixedRightPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes move-to-side {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-(280px - $fixedRightPadding));
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// for narrower padding
|
||||||
|
@keyframes slide-to-section {
|
||||||
|
0% {
|
||||||
|
transform: translateX(120px - $fixedRightPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-to-side {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-(120px - $fixedRightPadding));
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav id="news-nav" :class="{ fixed: isNavbarFixed }">
|
<nav id="news-nav" :class="{ fixed: (isNavbarFixed || isMobileView), returning: isNavbarReturning }">
|
||||||
<div v-show="!isMobileView || navVisible" class="news-nav" v-scrollspy="{ selectors: navbarItems }">
|
<div
|
||||||
|
class="news-nav"
|
||||||
|
:class="{ animating: isNavbarAnimating }"
|
||||||
|
v-show="!isMobileView || navVisible"
|
||||||
|
v-scrollspy="{ selectors: navbarItems }"
|
||||||
|
>
|
||||||
<a class="news-nav-item" href="#schedule">重要時程</a>
|
<a class="news-nav-item" href="#schedule">重要時程</a>
|
||||||
<a class="news-nav-item" href="#example">投稿主題範例</a>
|
<a class="news-nav-item" href="#example">投稿主題範例</a>
|
||||||
<a class="news-nav-item" href="#code-of-conduct">Code of Conduct</a>
|
<a class="news-nav-item" href="#code-of-conduct">Code of Conduct</a>
|
||||||
@ -30,7 +35,9 @@ import ScrollSpyDirective from './ScrollSpyDirective';
|
|||||||
})
|
})
|
||||||
export default class Navbar extends Vue {
|
export default class Navbar extends Vue {
|
||||||
private isMobileView: boolean = false;
|
private isMobileView: boolean = false;
|
||||||
private isNavbarFixed = false;
|
private isNavbarFixed: boolean = false;
|
||||||
|
private isNavbarAnimating: boolean = false;
|
||||||
|
private isNavbarReturning: boolean = false;
|
||||||
private navVisible: boolean = false;
|
private navVisible: boolean = false;
|
||||||
private navbarItems: string[] = [
|
private navbarItems: string[] = [
|
||||||
'#schedule',
|
'#schedule',
|
||||||
@ -60,16 +67,32 @@ export default class Navbar extends Vue {
|
|||||||
// observe header: header appears => no fix; header disappears => fix.
|
// observe header: header appears => no fix; header disappears => fix.
|
||||||
const fixObserver: IntersectionObserver = new IntersectionObserver((entries, observer) => {
|
const fixObserver: IntersectionObserver = new IntersectionObserver((entries, observer) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
if (!entry.isIntersecting) {
|
// no animation for mobile
|
||||||
this.isNavbarFixed = true;
|
if (!this.isMobileView) {
|
||||||
} else {
|
// only show animation if fix status has changed
|
||||||
this.isNavbarFixed = false;
|
if (!entry.isIntersecting !== this.isNavbarFixed) {
|
||||||
|
if (!entry.isIntersecting) {
|
||||||
|
this.isNavbarFixed = true;
|
||||||
|
} else {
|
||||||
|
this.isNavbarFixed = false;
|
||||||
|
this.isNavbarReturning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isNavbarAnimating = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, { rootMargin: '25px 0px 0px 0px', threshold: 0 });
|
}, { rootMargin: '25px 0px 0px 0px', threshold: 0 });
|
||||||
this.$nextTick().then(() => {
|
this.$nextTick().then(() => {
|
||||||
fixObserver.observe(document.querySelector('#news-header') as Element);
|
fixObserver.observe(document.querySelector('#news-header') as Element);
|
||||||
|
|
||||||
|
document.querySelector('.news-nav')?.addEventListener('animationend', (ev) => {
|
||||||
|
if (!this.isNavbarFixed) {
|
||||||
|
this.isNavbarReturning = false;
|
||||||
|
}
|
||||||
|
this.isNavbarAnimating = false;
|
||||||
|
});
|
||||||
|
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
const hash = location.hash;
|
const hash = location.hash;
|
||||||
// scroll to anchor
|
// scroll to anchor
|
||||||
|
Loading…
x
Reference in New Issue
Block a user