feature(header): add header config

This commit is contained in:
Dillon 2020-02-16 20:36:36 +08:00
parent d25cbf6cb6
commit 88576721cc
12 changed files with 142 additions and 81 deletions

View file

@ -62,6 +62,7 @@
.page {
max-width: 100%;
padding-top: $page-padding-top-mobile;
.categories-card {
.card-item {
@ -80,8 +81,4 @@
}
}
}
.dynamic-to-top {
display: none !important;
}
}

View file

@ -3,7 +3,7 @@
width: 100%;
max-width: 980px;
margin: 0 auto;
padding-top: 6rem;
padding-top: $page-padding-top-desktop;
}
@import "_single";

View file

@ -1,8 +1,6 @@
header {
position: fixed;
width: 100%;
height: $header-height;
line-height: $header-height;
z-index: 100;
background-color: $header-background-color;
.dark-theme & {
@ -12,7 +10,9 @@ header {
#header-desktop {
display: block;
z-index: 100;
position: $header-position-desktop;
height: $header-height-desktop;
line-height: $header-height-desktop;
.header-wrapper {
width: auto;
@ -47,12 +47,14 @@ header {
#header-mobile {
display: none;
z-index: 100;
transition: all 0.3s ease 0s;
position: $header-position-mobile;
height: $header-height-mobile;
line-height: $header-height-mobile;
.header-wrapper {
padding: 0;
margin: 0;
transition: all 0.3s ease 0s;
.header-container {
display: flex;

View file

@ -7,7 +7,7 @@
border-left: 1px solid $global-border-color;
overflow-wrap: break-word;
box-sizing: border-box;
top: 12rem;
top: $post-toc-top;
.dark-theme & {
border-left: 1px solid $global-border-color-dark;

View file

@ -47,7 +47,8 @@ $selection-color-dark: rgba(38, 139, 211, 0.3) !default;
// ========== Header ========== //
// Height of the header
$header-height: 4rem !default;
$header-height-desktop: 4rem !default;
$header-height-mobile: 4rem !default;
// Color of the header background
$header-background-color: #fafafa !default;

View file

@ -8,6 +8,24 @@
$home-posts: false;
{{- end -}}
{{- if eq .Site.Params.desktopHeaderMode "normal" -}}
$header-position-desktop: static;
$page-padding-top-desktop: 1rem;
$post-toc-top: 7rem;
{{- else -}}
$header-position-desktop: fixed;
$page-padding-top-desktop: 6rem;
$post-toc-top: 12rem;
{{- end -}}
{{- if eq .Site.Params.mobileHeaderMode "normal" -}}
$header-position-mobile: static;
$page-padding-top-mobile: 1rem;
{{- else -}}
$header-position-mobile: fixed;
$page-padding-top-mobile: 6rem;
{{- end -}}
@import "_variables";
{{- if fileExists "config/css/_custom.scss" -}}

View file

@ -1,14 +1,26 @@
(() => {
'use strict';
const forEachElement = (elements, handler) => {
elements = elements || [];
for (let i = 0; i < elements.length; i++) {
handler(elements[i]);
class Util {
forEach(elements, handler) {
elements = elements || [];
for (let i = 0; i < elements.length; i++) {
handler(elements[i]);
}
}
};
getScrollTop() {
return (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
}
}
class Theme {
constructor() {
this.util = new Util();
this.scrollTop = 0;
this.scrollEvents = [];
}
initMobileMenu() {
document.getElementById('menu-toggle').onclick = () => {
document.getElementById('menu-toggle').classList.toggle('active');
@ -17,7 +29,7 @@
}
initSwitchTheme() {
forEachElement(document.getElementsByClassName('theme-switch'), (button) => {
this.util.forEach(document.getElementsByClassName('theme-switch'), (button) => {
button.onclick = () => {
document.body.classList.toggle('dark-theme');
window.isDark = !window.isDark;
@ -27,55 +39,14 @@
});
}
initDynamicToTop() {
const min = 300;
const toTopButton = document.getElementById('dynamic-to-top');
document.addEventListener('scroll', () => {
const scrollTop = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
if (typeof document.body.style.maxHeight === 'undefined') {
toTopButton.style.position = 'absolute';
toTopButton.style.top = scrollTop + window.document.documentElement.clientHeight - 20;
}
if (scrollTop > min) {
(function fadeIn(el, display){
display = display || 'block';
if (el.style.display !== display) {
el.style.opacity = 0;
el.style.display = display;
(function fade() {
let val = parseFloat(el.style.opacity);
if ((val += .1) <= 1) {
el.style.opacity = val;
requestAnimationFrame(fade);
}
})();
}
})(document.getElementById('dynamic-to-top'));
} else {
(function fadeOut(el){
if (el.style.display !== 'none') {
el.style.opacity = 1;
(function fade() {
if ((el.style.opacity -= .1) < 0) {
el.style.display = 'none';
} else {
requestAnimationFrame(fade);
}
})();
}
})(document.getElementById('dynamic-to-top'));
}
}, false);
}
initHighlight() {
forEachElement(document.querySelectorAll('.highlight > .chroma'), (block) => {
this.util.forEach(document.querySelectorAll('.highlight > .chroma'), (block) => {
const codes = block.querySelectorAll('pre.chroma > code');
const code = codes[codes.length - 1];
const lang = code ? code.className.toLowerCase() : '';
block.className += ' ' + lang;
});
forEachElement(document.querySelectorAll('.highlight > pre.chroma'), (block) => {
this.util.forEach(document.querySelectorAll('.highlight > pre.chroma'), (block) => {
const chroma = document.createElement('div');
chroma.className = block.className;
const table = document.createElement('table');
@ -92,7 +63,7 @@
}
initTable() {
forEachElement(document.querySelectorAll('.content table'), (table) => {
this.util.forEach(document.querySelectorAll('.content table'), (table) => {
const wrapper = document.createElement('div');
wrapper.className = 'table-wrapper';
table.parentElement.replaceChild(wrapper, table);
@ -102,7 +73,7 @@
initHeaderLink() {
for (let num = 1; num <= 6; num++) {
forEachElement(document.querySelectorAll('.content > h' + num), (header) => {
this.util.forEach(document.querySelectorAll('.content > h' + num), (header) => {
header.classList.add('headerLink');
header.innerHTML = `<a href="#${header.id}"></a>${header.innerHTML}`;
});
@ -110,7 +81,7 @@
}
_refactorToc(toc) {
forEachElement(toc.querySelectorAll('a:first-child'), (link) => {
this.util.forEach(toc.querySelectorAll('a:first-child'), (link) => {
link.classList.add('toc-link');
});
@ -127,19 +98,21 @@
_initTocState(tocContainer) {
if (window.getComputedStyle(tocContainer, null).display !== 'none') {
const TOP_SPACING = 80;
const fixed = window.desktopHeaderMode !== 'normal';
const fixedHeight = document.getElementById('header-desktop').getBoundingClientRect().height;
const TOP_SPACING = 20 + (fixed ? fixedHeight : 0);
const minTop = tocContainer.offsetTop;
const minScrollTop = minTop - TOP_SPACING;
const minScrollTop = minTop - TOP_SPACING + (fixed ? 0 : fixedHeight);
const footerTop = document.getElementById('post-footer').offsetTop;
const toclinks = tocContainer.getElementsByClassName('toc-link');
const headerLinks = document.getElementsByClassName('headerLink') || [];
const tocLinkLis = tocContainer.querySelectorAll('.post-toc-content li');
const INDEX_SPACING = document.getElementById('header-desktop').getBoundingClientRect().height + 5;
const INDEX_SPACING = 5 + (fixed ? fixedHeight : 0);
const changeTocState = () => {
const scrollTop = document.documentElement.scrollTop;
const scrollTop = this.util.getScrollTop();
const maxTop = footerTop - tocContainer.getBoundingClientRect().height;
const maxScrollTop = maxTop - TOP_SPACING;
const maxScrollTop = maxTop - TOP_SPACING + (fixed ? 0 : fixedHeight);
if (scrollTop < minScrollTop) {
tocContainer.style.position = 'absolute';
tocContainer.style.top = `${minTop}px`;
@ -151,8 +124,8 @@
tocContainer.style.top = `${TOP_SPACING}px`;
}
forEachElement(toclinks, (link) => { link.classList.remove('active'); });
forEachElement(tocLinkLis, (link) => { link.classList.remove('has-active'); });
this.util.forEach(toclinks, (link) => { link.classList.remove('active'); });
this.util.forEach(tocLinkLis, (link) => { link.classList.remove('has-active'); });
let activeTocIndex = headerLinks.length - 1;
for (let i = 0; i < headerLinks.length - 1; i++) {
const thisTop = headerLinks[i].getBoundingClientRect().top;
@ -175,7 +148,7 @@
changeTocState();
if (!this._initTocOnce) {
document.addEventListener('scroll', changeTocState, false);
this.scrollEvents.push(changeTocState);
this._initTocOnce = true;
}
}
@ -190,7 +163,7 @@
} else {
this._refactorToc(toc);
this._initTocState(tocContainer);
window.addEventListener("resize", () => {
window.addEventListener('resize', () => {
window.setTimeout(() => {
this._initTocState(tocContainer);
}, 0);
@ -258,14 +231,71 @@
}
}
initSmoothScroll() {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'});
initScroll() {
for (let i = 0; i < this.scrollEvents.length; i++) {
document.addEventListener('scroll', this.scrollEvents[i], false);
}
const initSmoothScroll = () => {
const isMobile = window.matchMedia('only screen and (max-width: 560px)').matches;
if ((!isMobile && window.desktopHeaderMode === 'normal')
|| (isMobile && window.mobileHeaderMode === 'normal')) {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true});
} else {
new SmoothScroll('[href^="#"]', {speed: 300, speedAsDuration: true, header: '#header-desktop'});
}
};
initSmoothScroll();
window.addEventListener('resize', () => {
window.setTimeout(() => {
initSmoothScroll();
}, 0);
}, false);
const headers = [];
if (window.desktopHeaderMode === 'auto') headers.push(document.getElementById('header-desktop'));
if (window.mobileHeaderMode === 'auto') headers.push(document.getElementById('header-mobile'));
this.util.forEach(headers, (header) => {
header.classList.add('animated');
header.classList.add('faster');
});
const toTopButton = document.getElementById('dynamic-to-top');
document.addEventListener('scroll', () => {
const scrollTop = this.util.getScrollTop();
this.util.forEach(headers, (header) => {
if (this.scrollTop < scrollTop) {
if (!header.classList.contains('fadeOutUp')) {
header.classList.remove('fadeInDown');
header.classList.add('fadeOutUp');
}
} else {
if (!header.classList.contains('fadeInDown')) {
header.classList.remove('fadeOutUp');
header.classList.add('fadeInDown');
}
}
if (scrollTop > 600) {
if (this.scrollTop < scrollTop) {
if (!toTopButton.classList.contains('fadeOut')) {
toTopButton.classList.remove('fadeIn');
toTopButton.classList.add('fadeOut');
}
} else {
toTopButton.style.display = 'block';
if (!toTopButton.classList.contains('fadeIn')) {
toTopButton.classList.remove('fadeOut');
toTopButton.classList.add('fadeIn');
}
}
} else {
toTopButton.style.display = 'none';
}
});
this.scrollTop = scrollTop;
}, false);
}
init() {
this.initMobileMenu();
this.initSwitchTheme();
this.initDynamicToTop();
this.initHighlight();
this.initTable();
this.initHeaderLink();
@ -273,7 +303,7 @@
this.initEcharts();
this.initTypeit();
this.initToc();
this.initSmoothScroll();
this.initScroll();
}
}
@ -285,6 +315,6 @@
if (document.readyState !== 'loading') {
themeInit();
} else {
document.addEventListener('DOMContentLoaded', themeInit);
document.addEventListener('DOMContentLoaded', themeInit, false);
}
})();

View file

@ -112,6 +112,10 @@ dateFormatToUse = "2006-01-02"
keywords = ["Theme", "Hugo"]
# site default theme ("light", "dark", "auto")
defaultTheme = "auto"
# desktop header mode ("fixed", "normal", "auto")
desktopHeaderMode = "fixed"
# mobile header mode ("fixed", "normal", "auto")
mobileHeaderMode = "auto"
# Home Page Info
##home mode ("post", "other")

View file

@ -112,6 +112,10 @@ dateFormatToUse = "2006-01-02"
keywords = ["Theme", "Hugo"]
# 网站默认主题 ("light", "dark", "auto")
defaultTheme = "auto"
# 桌面端导航栏模式 ("fixed", "normal", "auto")
desktopHeaderMode = "fixed"
# 移动端导航栏模式 ("fixed", "normal", "auto")
mobileHeaderMode = "auto"
# 主页信息设置
## 主页模式 ("post", "other")

View file

@ -52,7 +52,7 @@
</div>
{{- /* Dynamic to top button */ -}}
<a href="#" class="dynamic-to-top" id="dynamic-to-top">
<a href="#" class="dynamic-to-top animated faster" id="dynamic-to-top">
<span>&nbsp;</span>
</a>

View file

@ -42,3 +42,8 @@
</div>
</div>
</header>
<script>
window.desktopHeaderMode = {{ .Site.Params.desktopHeaderMode }};
window.mobileHeaderMode = {{ .Site.Params.mobileHeaderMode }};
</script>