performance

This commit is contained in:
tangly1024
2023-03-11 11:14:57 +08:00
parent 284e877136
commit f75103699f
14 changed files with 301 additions and 287 deletions

View File

@@ -49,7 +49,7 @@ const Slug = props => {
})
}
}
}, 20 * 1000)
}, 8 * 1000) // 404时长
const meta = { title: `${props?.siteInfo?.title || BLOG.TITLE} | loading`, image: siteInfo?.pageCover || BLOG.HOME_BANNER_IMAGE }
return <ThemeComponents.LayoutSlug {...props} showArticleInfo={true} meta={meta} />
}

View File

@@ -3,7 +3,7 @@ import React from 'react'
import BlogCard from './BlogCard'
import BlogPostListEmpty from './BlogListEmpty'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
/**
* 文章列表分页表格
* @param page 当前页
@@ -45,13 +45,15 @@ const BlogListScroll = props => {
}
// 监听滚动自动分页加载
const scrollTrigger = React.useCallback(throttle(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
}, 500))
const scrollTrigger = () => {
requestAnimationFrame(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
})
}
React.useEffect(() => {
updateCol()
@@ -68,22 +70,22 @@ const BlogListScroll = props => {
return <BlogPostListEmpty />
} else {
return (
<div id="container" ref={targetRef} >
{/* 文章列表 */}
<div style={{ columnCount: colCount }}>
{postsToShow?.map(post => (
<div key={post.id} className='justify-center flex' style={{ breakInside: 'avoid' }}>
<BlogCard key={post.id} post={post} siteInfo={siteInfo} />
</div>
))}
</div>
<div id="container" ref={targetRef} >
{/* 文章列表 */}
<div style={{ columnCount: colCount }}>
{postsToShow?.map(post => (
<div key={post.id} className='justify-center flex' style={{ breakInside: 'avoid' }}>
<BlogCard key={post.id} post={post} siteInfo={siteInfo} />
</div>
))}
</div>
<div className="w-full my-4 py-4 text-center cursor-pointer "
onClick={handleGetMore}>
{' '}
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
</div>
</div>
<div className="w-full my-4 py-4 text-center cursor-pointer "
onClick={handleGetMore}>
{' '}
{hasMore ? locale.COMMON.MORE : `${locale.COMMON.NO_MORE} 😰`}{' '}
</div>
</div>
)
}
}

View File

@@ -38,18 +38,20 @@ const LayoutBase = props => {
const { onLoading } = useGlobal()
const scrollListener = () => {
const targetRef = document.getElementById('wrapper')
const clientHeight = targetRef?.clientHeight
const scrollY = window.pageYOffset
const fullHeight = clientHeight - window.outerHeight
let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
if (per > 100) per = 100
const shouldShow = scrollY > 100 && per > 0
requestAnimationFrame(() => {
const targetRef = document.getElementById('wrapper')
const clientHeight = targetRef?.clientHeight
const scrollY = window.pageYOffset
const fullHeight = clientHeight - window.outerHeight
let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
if (per > 100) per = 100
const shouldShow = scrollY > 100 && per > 0
if (shouldShow !== show) {
switchShow(shouldShow)
}
if (shouldShow !== show) {
switchShow(shouldShow)
}
// changePercent(per)
})
}
useEffect(() => {
document.addEventListener('scroll', scrollListener)
@@ -78,12 +80,7 @@ const LayoutBase = props => {
{/* 右下角悬浮 */}
<div className={(show ? 'opacity-100 ' : 'invisible opacity-0') + ' duration-300 transition-all bottom-12 right-1 fixed justify-end z-20 text-white bg-indigo-500 dark:bg-hexo-black-gray rounded-sm'}>
<div
className={
' justify-center flex flex-col items-center cursor-pointer '
}
>
<div className={'justify-center flex flex-col items-center cursor-pointer'}>
<FloatDarkModeButton />
{floatSlot}
<JumpToTopButton />

View File

@@ -7,7 +7,8 @@ import LayoutBase from './LayoutBase'
import React from 'react'
export const LayoutIndex = (props) => {
return <LayoutBase {...props} headerSlot={CONFIG_HEXO.HOME_BANNER_ENABLE && <Header {...props} />}>
const headerSlot = CONFIG_HEXO.HOME_BANNER_ENABLE && <Header {...props} />
return <LayoutBase {...props} headerSlot={headerSlot}>
{BLOG.POST_LIST_STYLE === 'page' ? <BlogPostListPage {...props} /> : <BlogPostListScroll {...props} />}
</LayoutBase>

View File

@@ -2,7 +2,6 @@ import BLOG from '@/blog.config'
import BlogPostCard from './BlogPostCard'
import BlogPostListEmpty from './BlogPostListEmpty'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import React from 'react'
import CONFIG_HEXO from '../config_hexo'
import { getListByPage } from '@/lib/utils'
@@ -31,13 +30,15 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_HE
}
// 监听滚动自动分页加载
const scrollTrigger = React.useCallback(throttle(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
}, 500))
const scrollTrigger = () => {
requestAnimationFrame(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
})
}
// 监听滚动
React.useEffect(() => {

View File

@@ -1,5 +1,4 @@
import React, { useRef } from 'react'
import throttle from 'lodash.throttle'
import { uuidToId } from 'notion-utils'
import Progress from './Progress'
import { useGlobal } from '@/lib/global'
@@ -28,33 +27,34 @@ const Catalog = ({ toc }) => {
// 同步选中目录事件
const [activeSection, setActiveSection] = React.useState(null)
const throttleMs = 100
const actionSectionScrollSpy = React.useCallback(throttle(() => {
const sections = document.getElementsByClassName('notion-h')
let prevBBox = null
let currentSectionId = activeSection
for (let i = 0; i < sections.length; ++i) {
const section = sections[i]
if (!section || !(section instanceof Element)) continue
if (!currentSectionId) {
currentSectionId = section.getAttribute('data-id')
const actionSectionScrollSpy = () => {
requestAnimationFrame(() => {
const sections = document.getElementsByClassName('notion-h')
let prevBBox = null
let currentSectionId = activeSection
for (let i = 0; i < sections.length; ++i) {
const section = sections[i]
if (!section || !(section instanceof Element)) continue
if (!currentSectionId) {
currentSectionId = section.getAttribute('data-id')
}
const bbox = section.getBoundingClientRect()
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
const offset = Math.max(150, prevHeight / 4)
// GetBoundingClientRect returns values relative to viewport
if (bbox.top - offset < 0) {
currentSectionId = section.getAttribute('data-id')
prevBBox = bbox
continue
}
// No need to continue loop, if last element has been detected
break
}
const bbox = section.getBoundingClientRect()
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
const offset = Math.max(150, prevHeight / 4)
// GetBoundingClientRect returns values relative to viewport
if (bbox.top - offset < 0) {
currentSectionId = section.getAttribute('data-id')
prevBBox = bbox
continue
}
// No need to continue loop, if last element has been detected
break
}
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
}, throttleMs))
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
})
}
// 无目录就直接返回空
if (!toc || toc.length < 1) {

View File

@@ -6,6 +6,7 @@ import NavButtonGroup from './NavButtonGroup'
let wrapperTop = 0
let windowTop = 0
let autoScroll = false
const enableAutoScroll = false // 是否开启自动吸附滚动
/**
*
@@ -16,6 +17,7 @@ const Header = props => {
const { siteInfo } = props
useEffect(() => {
updateHeaderHeight()
if (!typed && window && document.getElementById('typed')) {
changeType(
new Typed('#typed', {
@@ -28,6 +30,7 @@ const Header = props => {
})
)
}
if (enableAutoScroll) {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
@@ -52,7 +55,7 @@ const Header = props => {
return (
<header
id="header"
className="duration-500 md:bg-fixed w-full bg-cover bg-center h-screen bg-black text-white relative z-10"
className="md:bg-fixed w-full bg-cover bg-center h-screen bg-black text-white relative z-10"
style={{
backgroundImage:
`linear-gradient(rgba(0, 0, 0, 0.9), rgba(0,0,0,0.5), rgba(0,0,0,0.3), rgba(0,0,0,0.5), rgba(0, 0, 0, 0.9) ),url("${siteInfo?.pageCover}")`
@@ -68,10 +71,9 @@ const Header = props => {
{ CONFIG_HEXO.HOME_NAV_BUTTONS && <NavButtonGroup {...props}/>}
</div>
<div
onClick={() => {
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
}}
onClick={() => { window.scrollTo({ top: wrapperTop, behavior: 'smooth' }) }}
className="cursor-pointer w-full text-center py-4 text-3xl absolute bottom-10 text-white"
>
<i className='animate-bounce fas fa-angle-down'/>
@@ -80,8 +82,6 @@ const Header = props => {
)
}
const enableAutoScroll = false // 是否开启自动吸附滚动
const autoScrollEnd = () => {
if (autoScroll) {
windowTop = window.scrollY
@@ -93,24 +93,26 @@ const autoScrollEnd = () => {
* 自动吸附滚动,移动端体验不好暂时关闭
*/
const scrollTrigger = () => {
if (screen.width <= 768) {
return
}
requestAnimationFrame(() => {
if (screen.width <= 768) {
return
}
const scrollS = window.scrollY
// 自动滚动
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
) {
autoScroll = true
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
requestAnimationFrame(autoScrollEnd)
}
if ((scrollS < windowTop) && (scrollS < window.innerHeight) && !autoScroll) {
autoScroll = true
window.scrollTo({ top: 0, behavior: 'smooth' })
requestAnimationFrame(autoScrollEnd)
}
windowTop = scrollS
const scrollS = window.scrollY
// 自动滚动
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
) {
autoScroll = true
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
requestAnimationFrame(autoScrollEnd)
}
if ((scrollS < windowTop) && (scrollS < window.innerHeight) && !autoScroll) {
autoScroll = true
window.scrollTo({ top: 0, behavior: 'smooth' })
requestAnimationFrame(autoScrollEnd)
}
windowTop = scrollS
})
}
export default Header

View File

@@ -11,10 +11,11 @@ import CONFIG_HEXO from '../config_hexo'
* @constructor
*/
const JumpToTopButton = ({ showPercent = true, percent }) => {
const { locale } = useGlobal()
if (!CONFIG_HEXO.WIDGET_TO_TOP) {
return <></>
}
const { locale } = useGlobal()
return (<div className='space-x-1 items-center justify-center transform hover:scale-105 duration-200 w-7 h-auto pb-1 text-center' onClick={() => window.scrollTo({ top: 0, behavior: 'smooth' })} >
<div title={locale.POST.TOP} ><i className='fas fa-arrow-up text-xs' /></div>
{showPercent && (<div className='text-xs hidden lg:block'>{percent}</div>)}

View File

@@ -1,5 +1,4 @@
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import Link from 'next/link'
import { useEffect, useRef, useState } from 'react'
import CategoryGroup from './CategoryGroup'
@@ -25,37 +24,39 @@ const TopNav = props => {
const { isDarkMode } = useGlobal()
const router = useRouter()
const scrollTrigger = throttle(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
const header = document.querySelector('#header')
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight)// 非首页无大图时影藏顶部 滚动条置顶时隐藏
// 是否将导航栏透明
const navTransparent = (scrollS < document.documentElement.clientHeight - 12 && router.route === '/') || scrollS < 300 // 透明导航条的条件
const scrollTrigger = () => {
requestAnimationFrame(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
const header = document.querySelector('#header')
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight)// 非首页无大图时影藏顶部 滚动条置顶时隐藏
// 是否将导航栏透明
const navTransparent = (scrollS < document.documentElement.clientHeight - 12 && router.route === '/') || scrollS < 300 // 透明导航条的条件
if (header && navTransparent) {
nav && nav.classList.replace('bg-white', 'bg-none')
nav && nav.classList.replace('text-black', 'text-white')
nav && nav.classList.replace('border', 'border-transparent')
nav && nav.classList.replace('drop-shadow-md', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-white')
nav && nav.classList.replace('text-white', 'text-black')
nav && nav.classList.replace('border-transparent', 'border')
nav && nav.classList.replace('shadow-none', 'drop-shadow-md')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (header && navTransparent) {
nav && nav.classList.replace('bg-white', 'bg-none')
nav && nav.classList.replace('text-black', 'text-white')
nav && nav.classList.replace('border', 'border-transparent')
nav && nav.classList.replace('drop-shadow-md', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-white')
nav && nav.classList.replace('text-white', 'text-black')
nav && nav.classList.replace('border-transparent', 'border')
nav && nav.classList.replace('shadow-none', 'drop-shadow-md')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
navDarkMode()
}, 200)
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
navDarkMode()
})
}
const navDarkMode = () => {
const nav = document.getElementById('sticky-nav')
@@ -69,86 +70,85 @@ const TopNav = props => {
}
}
// 监听滚动
useEffect(() => {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
}, [])
const [isOpen, changeShow] = useState(false)
const toggleMenuOpen = () => {
changeShow(!isOpen)
}
// 监听滚动
useEffect(() => {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
return () => {
window.removeEventListener('scroll', scrollTrigger)
}
}, [])
const searchDrawerSlot = <>
{ categories && (
<section className='mt-8'>
<div className='text-sm flex flex-nowrap justify-between font-light px-2'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-th-list' />{locale.COMMON.CATEGORY}</div>
<Link
href={'/category'}
passHref
className='mb-3 text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{categories && (
<section className='mt-8'>
<div className='text-sm flex flex-nowrap justify-between font-light px-2'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-th-list' />{locale.COMMON.CATEGORY}</div>
<Link
href={'/category'}
passHref
className='mb-3 text-gray-400 hover:text-black dark:text-gray-400 dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<CategoryGroup currentCategory={currentCategory} categories={categories} />
</section>
) }
</Link>
</div>
<CategoryGroup currentCategory={currentCategory} categories={categories} />
</section>
)}
{ tags && (
<section className='mt-4'>
<div className='text-sm py-2 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-tag'/>{locale.COMMON.TAGS}</div>
<Link
href={'/tag'}
passHref
className='text-gray-400 hover:text-black dark:hover:text-white hover:underline cursor-pointer'>
{tags && (
<section className='mt-4'>
<div className='text-sm py-2 px-2 flex flex-nowrap justify-between font-light dark:text-gray-200'>
<div className='text-gray-600 dark:text-gray-200'><i className='mr-2 fas fa-tag' />{locale.COMMON.TAGS}</div>
<Link
href={'/tag'}
passHref
className='text-gray-400 hover:text-black dark:hover:text-white hover:underline cursor-pointer'>
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
{locale.COMMON.MORE} <i className='fas fa-angle-double-right' />
</Link>
</div>
<div className='p-2'>
<TagGroups tags={tags} currentTag={currentTag} />
</div>
</section>
) }
</Link>
</div>
<div className='p-2'>
<TagGroups tags={tags} currentTag={currentTag} />
</div>
</section>
)}
</>
return (<div id='top-nav' className='z-40'>
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot}/>
<SearchDrawer cRef={searchDrawer} slot={searchDrawerSlot} />
{/* 导航栏 */}
<div id='sticky-nav' className={'top-0 shadow-none fixed bg-none dark:bg-hexo-black-gray dark:text-gray-200 text-black w-full z-20 transform transition-all duration-200 border-transparent dark:border-transparent'}>
<div className='w-full flex justify-between items-center px-4 py-2'>
<div className='flex'>
<Logo {...props}/>
</div>
{/* 导航栏 */}
<div id='sticky-nav' className={'top-0 shadow-none fixed bg-none dark:bg-hexo-black-gray dark:text-gray-200 text-black w-full z-20 transform transition-all duration-200 border-transparent dark:border-transparent'}>
<div className='w-full flex justify-between items-center px-4 py-2'>
<div className='flex'>
<Logo {...props} />
</div>
{/* 右侧功能 */}
<div className='mr-1 justify-end items-center '>
<div className='hidden lg:flex'> <MenuButtonGroupTop {...props}/></div>
<div onClick={toggleMenuOpen} className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{ isOpen ? <i className='fas fa-times'/> : <i className='fas fa-bars'/> }
</div>
</div>
</div>
{/* 右侧功能 */}
<div className='mr-1 justify-end items-center '>
<div className='hidden lg:flex'> <MenuButtonGroupTop {...props} /></div>
<div onClick={toggleMenuOpen} className='w-8 justify-center items-center h-8 cursor-pointer flex lg:hidden'>
{isOpen ? <i className='fas fa-times' /> : <i className='fas fa-bars' />}
</div>
</div>
</div>
<Collapse type='vertical' isOpen={isOpen} className='shadow-xl'>
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 px-5 lg:hidden '>
<MenuList {...props}/>
<Collapse type='vertical' isOpen={isOpen} className='shadow-xl'>
<div className='bg-white dark:bg-hexo-black-gray pt-1 py-2 px-5 lg:hidden '>
<MenuList {...props} />
</div>
</Collapse>
</div>
</Collapse>
</div>
</div>)
</div>)
}
export default TopNav

View File

@@ -22,18 +22,20 @@ const LayoutBase = props => {
const { onLoading } = useGlobal()
const scrollListener = () => {
const targetRef = document.getElementById('wrapper')
const clientHeight = targetRef?.clientHeight
const scrollY = window.pageYOffset
const fullHeight = clientHeight - window.outerHeight
let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
if (per > 100) per = 100
const shouldShow = scrollY > 300 && per > 0
requestAnimationFrame(() => {
const targetRef = document.getElementById('wrapper')
const clientHeight = targetRef?.clientHeight
const scrollY = window.pageYOffset
const fullHeight = clientHeight - window.outerHeight
let per = parseFloat(((scrollY / fullHeight) * 100).toFixed(0))
if (per > 100) per = 100
const shouldShow = scrollY > 300 && per > 0
if (shouldShow !== show) {
switchShow(shouldShow)
}
if (shouldShow !== show) {
switchShow(shouldShow)
}
// changePercent(per)
})
}
useEffect(() => {
document.addEventListener('scroll', scrollListener)

View File

@@ -2,7 +2,6 @@ import BLOG from '@/blog.config'
import BlogPostCard from './BlogPostCard'
import BlogPostListEmpty from './BlogPostListEmpty'
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import React from 'react'
import CONFIG_MATERY from '../config_matery'
import { getListByPage } from '@/lib/utils'
@@ -31,13 +30,15 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_MA
}
// 监听滚动自动分页加载
const scrollTrigger = React.useCallback(throttle(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
}, 500))
const scrollTrigger = () => {
requestAnimationFrame(() => {
const scrollS = window.scrollY + window.outerHeight
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
if (scrollS > clientHeight + 100) {
handleGetMore()
}
})
}
// 监听滚动
React.useEffect(() => {

View File

@@ -1,5 +1,4 @@
import React, { useRef } from 'react'
import throttle from 'lodash.throttle'
import { uuidToId } from 'notion-utils'
import Progress from './Progress'
import { useGlobal } from '@/lib/global'
@@ -27,33 +26,34 @@ const Catalog = ({ toc }) => {
// 同步选中目录事件
const [activeSection, setActiveSection] = React.useState(null)
const throttleMs = 100
const actionSectionScrollSpy = React.useCallback(throttle(() => {
const sections = document.getElementsByClassName('notion-h')
let prevBBox = null
let currentSectionId = activeSection
for (let i = 0; i < sections.length; ++i) {
const section = sections[i]
if (!section || !(section instanceof Element)) continue
if (!currentSectionId) {
currentSectionId = section.getAttribute('data-id')
const actionSectionScrollSpy = () => {
requestAnimationFrame(() => {
const sections = document.getElementsByClassName('notion-h')
let prevBBox = null
let currentSectionId = activeSection
for (let i = 0; i < sections.length; ++i) {
const section = sections[i]
if (!section || !(section instanceof Element)) continue
if (!currentSectionId) {
currentSectionId = section.getAttribute('data-id')
}
const bbox = section.getBoundingClientRect()
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
const offset = Math.max(150, prevHeight / 4)
// GetBoundingClientRect returns values relative to viewport
if (bbox.top - offset < 0) {
currentSectionId = section.getAttribute('data-id')
prevBBox = bbox
continue
}
// No need to continue loop, if last element has been detected
break
}
const bbox = section.getBoundingClientRect()
const prevHeight = prevBBox ? bbox.top - prevBBox.bottom : 0
const offset = Math.max(150, prevHeight / 4)
// GetBoundingClientRect returns values relative to viewport
if (bbox.top - offset < 0) {
currentSectionId = section.getAttribute('data-id')
prevBBox = bbox
continue
}
// No need to continue loop, if last element has been detected
break
}
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
}, throttleMs))
setActiveSection(currentSectionId)
const index = tocIds.indexOf(currentSectionId) || 0
tRef?.current?.scrollTo({ top: 28 * index, behavior: 'smooth' })
})
}
// 无目录就直接返回空
if (!toc || toc.length < 1) {

View File

@@ -5,6 +5,7 @@ import CONFIG_MATERY from '../config_matery'
let wrapperTop = 0
let windowTop = 0
let autoScroll = false
const enableAutoScroll = false // 是否开启自动吸附滚动
/**
*
@@ -28,11 +29,14 @@ const Header = props => {
})
)
}
window.addEventListener('scroll', scrollTrigger)
window.addEventListener('resize', updateHeaderHeight)
if (enableAutoScroll) {
scrollTrigger()
window.addEventListener('scroll', scrollTrigger)
} window.addEventListener('resize', updateHeaderHeight)
return () => {
window.removeEventListener('scroll', scrollTrigger)
window.removeEventListener('resize', updateHeaderHeight)
if (enableAutoScroll) {
window.removeEventListener('scroll', scrollTrigger)
} window.removeEventListener('resize', updateHeaderHeight)
}
})
@@ -48,32 +52,34 @@ const Header = props => {
* @returns
*/
const scrollTrigger = () => {
if (screen.width <= 768) {
return
}
requestAnimationFrame(() => {
if (screen.width <= 768) {
return
}
const scrollS = window.scrollY
const scrollS = window.scrollY
// 自动滚动
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
) {
autoScroll = true
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
setTimeout(autoScrollEnd, 500)
}
if ((scrollS < windowTop) && (scrollS < window.innerHeight) && !autoScroll) {
autoScroll = true
window.scrollTo({ top: 0, behavior: 'smooth' })
setTimeout(autoScrollEnd, 500)
}
windowTop = scrollS
// 自动滚动
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
) {
autoScroll = true
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
setTimeout(autoScrollEnd, 500)
}
if ((scrollS < windowTop) && (scrollS < window.innerHeight) && !autoScroll) {
autoScroll = true
window.scrollTo({ top: 0, behavior: 'smooth' })
setTimeout(autoScrollEnd, 500)
}
windowTop = scrollS
})
}
function updateHeaderHeight () {
setTimeout(() => {
requestAnimationFrame(() => {
const wrapperElement = document.getElementById('wrapper')
wrapperTop = wrapperElement?.offsetTop
}, 500)
})
}
return (

View File

@@ -1,5 +1,4 @@
import { useGlobal } from '@/lib/global'
import throttle from 'lodash.throttle'
import Link from 'next/link'
import { useEffect, useRef, useState } from 'react'
import CategoryGroup from './CategoryGroup'
@@ -23,35 +22,37 @@ const TopNav = props => {
const searchDrawer = useRef()
const { isDarkMode } = useGlobal()
const scrollTrigger = throttle(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
const header = document.querySelector('#header')
const showNav = scrollS <= windowTop || scrollS < 5 // 非首页无大图时影藏顶部 滚动条置顶时隐藏
// 是否将导航栏透明
const navTransparent = header && scrollS < 300 // 透明导航条的条件
const scrollTrigger = () => {
requestAnimationFrame(() => {
const scrollS = window.scrollY
const nav = document.querySelector('#sticky-nav')
const header = document.querySelector('#header')
const showNav = scrollS <= windowTop || scrollS < 5 // 非首页无大图时影藏顶部 滚动条置顶时隐藏
// 是否将导航栏透明
const navTransparent = header && scrollS < 300 // 透明导航条的条件
if (navTransparent) {
nav && nav.classList.replace('bg-indigo-700', 'bg-none')
nav && nav.classList.replace('text-black', 'text-white')
nav && nav.classList.replace('drop-shadow-xl', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-indigo-700')
nav && nav.classList.replace('text-white', 'text-black')
nav && nav.classList.replace('shadow-none', 'drop-shadow-xl')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (navTransparent) {
nav && nav.classList.replace('bg-indigo-700', 'bg-none')
nav && nav.classList.replace('text-black', 'text-white')
nav && nav.classList.replace('drop-shadow-xl', 'shadow-none')
nav && nav.classList.replace('dark:bg-hexo-black-gray', 'transparent')
} else {
nav && nav.classList.replace('bg-none', 'bg-indigo-700')
nav && nav.classList.replace('text-white', 'text-black')
nav && nav.classList.replace('shadow-none', 'drop-shadow-xl')
nav && nav.classList.replace('transparent', 'dark:bg-hexo-black-gray')
}
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
navDarkMode()
}, 200)
if (!showNav) {
nav && nav.classList.replace('top-0', '-top-20')
windowTop = scrollS
} else {
nav && nav.classList.replace('-top-20', 'top-0')
windowTop = scrollS
}
navDarkMode()
})
}
const navDarkMode = () => {
const nav = document.getElementById('sticky-nav')