breaking changes: 移除博天api

breaking changes: 移除 waline 的 avatar 和 avatar cdn 配置
feat: anchor 不再限制 post 頁開啟,可以在任何頁面開啟
feat: 文章標題支持點擊跳轉到此標題開始閲讀 closed #653
feat: toc可以設置全部展開 closed #709
feat: 增加 新的評論系統 giscus
feat: 支持新的評論名寫法,主題會處理評論名字大小寫,舊的會兼容
feat: 友情鏈接列表增加 fetch url 獲取
improvement: 鼠標移到最新評論內容,增加 title 顯示
fix: 修復 rightside 遮擋內容,導致內容無法點擊的 bug
fix: 修復 mermaid 在某些頁面(有元素 id 為 mermaid 時) 會無法加載的 bug
fix: 修復 搜索框不會自動 focus 的 bug
This commit is contained in:
Jerry
2021-11-14 17:50:11 +08:00
parent f7c50586ce
commit ffeab5e20c
30 changed files with 363 additions and 225 deletions

View File

@@ -117,7 +117,7 @@ if hexo-config('enter_transitions')
#site-title,
#site-subtitle
animation: titlescale 1s
animation: titleScale 1s
#nav.show
animation: headerNoOpacity 1s
@@ -187,7 +187,7 @@ if hexo-config('avatar.effect') == true
margin-top: 0
opacity: 1
@keyframes titlescale
@keyframes titleScale
0%
opacity: 0
transform: scale(.7)

View File

@@ -32,6 +32,7 @@
--timeline-bg: $timeline-content-bg
--timeline-border-color: $timeline-border-color
--pseudo-hover: $pseudo-hover
--headline-presudo: #a0a0a0
body
position: relative

View File

@@ -265,16 +265,17 @@
+maxWidth900()
max-height: calc(100vh - 140px)
.toc-child
display: none
&:not(.is-expand)
.toc-child
display: none
+maxWidth900()
display: block !important
+maxWidth900()
display: block !important
.toc-item
&.active
.toc-child
display: block
.toc-item
&.active
.toc-child
display: block
ol,
li

View File

@@ -7,7 +7,7 @@ beautify()
font-size: unit(fontsize, 'px')
&:hover
padding-left: unit(fontsize + 12, 'px')
padding-left: unit(fontsize + 18, 'px')
h1,
h2,
@@ -102,6 +102,32 @@ beautify()
font-family: Monaco, 'Ubuntu Mono', monospace
line-height: 1em
if hexo-config('anchor')
a.headerlink
&:after
@extend .fontawesomeIcon
float: right
color: var(--headline-presudo)
content: '\f0c1'
font-size: .95em
opacity: 0
transition: all .3s
&:hover
&:after
color: var(--pseudo-hover)
h1,
h2,
h3,
h4,
h5,
h6
&:hover
a.headerlink
&:after
opacity: 1
ol,
ul
ol,

View File

@@ -7,11 +7,7 @@
transition: all .5s
#rightside-config-hide
transition: transform .4s
transform: translate(48px, 0)
&.show
transform: translate(0, 0) !important
display: none
& > div
& > button,
@@ -39,3 +35,19 @@
+maxWidth900()
#hide-aside-btn
display: none
@keyframes rightside-item-in
0%
transform: translate(48px, 0)
100%
transform: translate(0, 0)
@keyframes rightside-item-out
0%
display: block
transform: translate(0, 0)
100%
display: none
transform: translate(48px, 0)

View File

@@ -9,7 +9,6 @@
width: 600px
border-radius: 8px
background: var(--search-bg)
animation: titlescale .5s
+maxWidth768()
top: 0

View File

@@ -34,7 +34,7 @@ document.addEventListener('DOMContentLoaded', function () {
open: () => {
btf.sidebarPaddingR()
document.body.style.overflow = 'hidden'
btf.fadeIn(document.getElementById('menu-mask'), 0.5)
btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')
document.getElementById('sidebar-menus').classList.add('open')
mobileSidebarOpen = true
},
@@ -42,7 +42,7 @@ document.addEventListener('DOMContentLoaded', function () {
const $body = document.body
$body.style.overflow = ''
$body.style.paddingRight = ''
btf.fadeOut(document.getElementById('menu-mask'), 0.5)
btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')
document.getElementById('sidebar-menus').classList.remove('open')
mobileSidebarOpen = false
}
@@ -315,82 +315,69 @@ document.addEventListener('DOMContentLoaded', function () {
}
/**
* toc
* toc,anchor
*/
const tocFn = function () {
const $cardTocLayout = document.getElementById('card-toc')
const $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
const $tocLink = $cardToc.querySelectorAll('.toc-link')
const scrollFnToDo = function () {
const isToc = GLOBAL_CONFIG_SITE.isToc
const isAnchor = GLOBAL_CONFIG.isAnchor
const $article = document.getElementById('article-container')
const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
// main of scroll
window.tocScrollFn = function () {
return btf.throttle(function () {
const currentTop = window.scrollY || document.documentElement.scrollTop
scrollPercent(currentTop)
findHeadPosition(currentTop)
}, 100)()
}
window.addEventListener('scroll', tocScrollFn)
if (!($article && (isToc || isAnchor))) return
const scrollPercent = function (currentTop) {
const docHeight = $article.clientHeight
const winHeight = document.documentElement.clientHeight
const headerHeight = $article.offsetTop
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
const scrollPercent = (currentTop - headerHeight) / (contentMath)
const scrollPercentRounded = Math.round(scrollPercent * 100)
const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
$tocPercentage.textContent = percentage
}
let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand
// anchor
const isAnchor = GLOBAL_CONFIG.isanchor
const updateAnchor = function (anchor) {
if (window.history.replaceState && anchor !== window.location.hash) {
if (!anchor) anchor = location.pathname
const title = GLOBAL_CONFIG_SITE.title
window.history.replaceState({
url: location.href,
title: title
}, title, anchor)
if (isToc) {
const $cardTocLayout = document.getElementById('card-toc')
$cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
$tocLink = $cardToc.querySelectorAll('.toc-link')
const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
isExpand = $cardToc.classList.contains('is-expand')
scrollPercent = currentTop => {
const docHeight = $article.clientHeight
const winHeight = document.documentElement.clientHeight
const headerHeight = $article.offsetTop
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
const scrollPercent = (currentTop - headerHeight) / (contentMath)
const scrollPercentRounded = Math.round(scrollPercent * 100)
const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
$tocPercentage.textContent = percentage
}
}
window.mobileToc = {
open: () => {
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
},
window.mobileToc = {
open: () => {
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
},
close: () => {
$cardTocLayout.style.animation = 'toc-close .2s'
setTimeout(() => {
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
}, 100)
close: () => {
$cardTocLayout.style.animation = 'toc-close .2s'
setTimeout(() => {
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
}, 100)
}
}
}
// toc元素點擊
$cardToc.addEventListener('click', (e) => {
e.preventDefault()
const $target = e.target.classList.contains('toc-link')
? e.target
: e.target.parentElement
btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
if (window.innerWidth < 900) {
window.mobileToc.close()
}
})
// toc元素點擊
$cardToc.addEventListener('click', e => {
e.preventDefault()
const $target = e.target.classList.contains('toc-link')
? e.target
: e.target.parentElement
btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
if (window.innerWidth < 900) {
window.mobileToc.close()
}
})
const autoScrollToc = function (item) {
const activePosition = item.getBoundingClientRect().top
const sidebarScrollTop = $cardToc.scrollTop
if (activePosition > (document.documentElement.clientHeight - 100)) {
$cardToc.scrollTop = sidebarScrollTop + 150
}
if (activePosition < 100) {
$cardToc.scrollTop = sidebarScrollTop - 150
autoScrollToc = item => {
const activePosition = item.getBoundingClientRect().top
const sidebarScrollTop = $cardToc.scrollTop
if (activePosition > (document.documentElement.clientHeight - 100)) {
$cardToc.scrollTop = sidebarScrollTop + 150
}
if (activePosition < 100) {
$cardToc.scrollTop = sidebarScrollTop - 150
}
}
}
@@ -398,7 +385,7 @@ document.addEventListener('DOMContentLoaded', function () {
const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
let detectItem = ''
const findHeadPosition = function (top) {
if ($tocLink.length === 0 || top === 0) {
if (top === 0) {
return false
}
@@ -407,37 +394,50 @@ document.addEventListener('DOMContentLoaded', function () {
list.forEach(function (ele, index) {
if (top > btf.getEleTop(ele) - 80) {
currentId = '#' + encodeURI(ele.getAttribute('id'))
const id = ele.id
currentId = id ? '#' + encodeURI(id) : ''
currentIndex = index
}
})
if (detectItem === currentIndex) return
if (isAnchor) updateAnchor(currentId)
if (currentId === '') {
$cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
detectItem = currentIndex
return
}
if (isAnchor) btf.updateAnchor(currentId)
detectItem = currentIndex
$cardToc.querySelectorAll('.active').forEach(item => { item.classList.remove('active') })
const currentActive = $tocLink[currentIndex]
currentActive.classList.add('active')
if (isToc) {
$cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
setTimeout(() => {
autoScrollToc(currentActive)
}, 0)
if (currentId === '') {
return
}
let parent = currentActive.parentNode
const currentActive = $tocLink[currentIndex]
currentActive.classList.add('active')
for (; !parent.matches('.toc'); parent = parent.parentNode) {
if (parent.matches('li')) parent.classList.add('active')
setTimeout(() => {
autoScrollToc(currentActive)
}, 0)
if (isExpand) return
let parent = currentActive.parentNode
for (; !parent.matches('.toc'); parent = parent.parentNode) {
if (parent.matches('li')) parent.classList.add('active')
}
}
}
// main of scroll
window.tocScrollFn = function () {
return btf.throttle(function () {
const currentTop = window.scrollY || document.documentElement.scrollTop
isToc && scrollPercent(currentTop)
findHeadPosition(currentTop)
}, 100)()
}
window.addEventListener('scroll', tocScrollFn)
}
/**
@@ -473,12 +473,20 @@ document.addEventListener('DOMContentLoaded', function () {
}
// handle some cases
typeof utterancesTheme === 'function' && utterancesTheme()
typeof changeGiscusTheme === 'function' && changeGiscusTheme()
typeof FB === 'object' && window.loadFBComment()
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
typeof runMermaid === 'function' && window.runMermaid()
},
showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開
document.getElementById('rightside-config-hide').classList.toggle('show')
const target = document.getElementById('rightside-config-hide')
if (window.rightSideIn) {
window.rightSideIn = false
btf.animateOut(target, 'rightside-item-out 0.5s')
} else {
window.rightSideIn = true
btf.animateIn(target, 'rightside-item-in 0.5s')
}
},
scrollToTop: () => { // Back to top
btf.scrollToDest(0, 500)
@@ -749,7 +757,7 @@ document.addEventListener('DOMContentLoaded', function () {
toggleCardCategory()
}
GLOBAL_CONFIG_SITE.isToc && tocFn()
scrollFnToDo()
GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
addHighlightTool()
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()

View File

@@ -3,9 +3,10 @@ window.addEventListener('load', () => {
const bodyStyle = document.body.style
bodyStyle.width = '100%'
bodyStyle.overflow = 'hidden'
document.querySelector('#algolia-search .search-dialog').style.display = 'block'
document.querySelector('#algolia-search .ais-SearchBox-input').focus()
btf.fadeIn(document.getElementById('search-mask'), 0.5)
btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
btf.animateIn(document.querySelector('#algolia-search .search-dialog'), 'titleScale 0.5s')
setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100)
// shortcut: ESC
document.addEventListener('keydown', function f (event) {
if (event.code === 'Escape') {
@@ -19,10 +20,8 @@ window.addEventListener('load', () => {
const bodyStyle = document.body.style
bodyStyle.width = ''
bodyStyle.overflow = ''
const $searchDialog = document.querySelector('#algolia-search .search-dialog')
$searchDialog.style.animation = 'search_close .5s'
setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
btf.fadeOut(document.getElementById('search-mask'), 0.5)
btf.animateOut(document.querySelector('#algolia-search .search-dialog'), 'search_close .5s')
btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
}
const searchClickFn = () => {

View File

@@ -1,10 +1,12 @@
window.addEventListener('load', () => {
let loadFlag = false
const openSearch = function () {
document.body.style.cssText = 'width: 100%;overflow: hidden'
document.querySelector('#local-search .search-dialog').style.display = 'block'
document.querySelector('#local-search-input input').focus()
btf.fadeIn(document.getElementById('search-mask'), 0.5)
const openSearch = () => {
const bodyStyle = document.body.style
bodyStyle.width = '100%'
bodyStyle.overflow = 'hidden'
btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
btf.animateIn(document.querySelector('#local-search .search-dialog'), 'titleScale 0.5s')
setTimeout(() => { document.querySelector('#local-search-input input').focus() }, 100)
if (!loadFlag) {
search(GLOBAL_CONFIG.localSearch.path)
loadFlag = true
@@ -18,12 +20,12 @@ window.addEventListener('load', () => {
})
}
const closeSearch = function () {
document.body.style.cssText = "width: '';overflow: ''"
const $searchDialog = document.querySelector('#local-search .search-dialog')
$searchDialog.style.animation = 'search_close .5s'
setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
btf.fadeOut(document.getElementById('search-mask'), 0.5)
const closeSearch = () => {
const bodyStyle = document.body.style
bodyStyle.width = ''
bodyStyle.overflow = ''
btf.animateOut(document.querySelector('#local-search .search-dialog'), 'search_close .5s')
btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
}
// click function

View File

@@ -58,16 +58,14 @@ const btf = {
}
},
snackbarShow: (text, showAction, duration) => {
const sa = (typeof showAction !== 'undefined') ? showAction : false
const dur = (typeof duration !== 'undefined') ? duration : 2000
const position = GLOBAL_CONFIG.Snackbar.position
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? GLOBAL_CONFIG.Snackbar.bgLight : GLOBAL_CONFIG.Snackbar.bgDark
snackbarShow: (text, showAction = false, duration = 2000) => {
const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark
Snackbar.show({
text: text,
backgroundColor: bg,
showAction: sa,
duration: dur,
showAction: showAction,
duration: duration,
pos: position
})
},
@@ -151,16 +149,18 @@ const btf = {
})
},
fadeIn: (ele, time) => {
ele.style.cssText = `display:block;animation: to_show ${time}s`
animateIn: (ele, text) => {
ele.style.display = 'block'
ele.style.animation = text
},
fadeOut: (ele, time) => {
animateOut: (ele, text) => {
ele.addEventListener('animationend', function f () {
ele.style.cssText = "display: none; animation: '' "
ele.style.display = ''
ele.style.animation = ''
ele.removeEventListener('animationend', f)
})
ele.style.animation = `to_hide ${time}s`
ele.style.animation = text
},
getParents: (elem, selector) => {
@@ -180,7 +180,6 @@ const btf = {
},
/**
*
* @param {*} selector
* @param {*} eleType the type of create element
* @param {*} options object key: value
@@ -263,5 +262,16 @@ const btf = {
})
}
})
},
updateAnchor: (anchor) => {
if (anchor !== window.location.hash) {
if (!anchor) anchor = location.pathname
const title = GLOBAL_CONFIG_SITE.title
window.history.replaceState({
url: location.href,
title: title
}, title, anchor)
}
}
}