mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
Merge branch 'main' into patch-01
This commit is contained in:
@@ -133,6 +133,8 @@ export const isMobile = () => {
|
||||
if (!isBrowser()) {
|
||||
return isMobile
|
||||
}
|
||||
|
||||
// 这个判断会引发 TypeError: navigator.userAgentData.mobile is undefined 问题,导致博客无法正常工作
|
||||
// if (!isMobile && navigator.userAgentData.mobile) {
|
||||
// isMobile = true
|
||||
// }
|
||||
|
||||
@@ -24,6 +24,7 @@ import smoothscroll from 'smoothscroll-polyfill'
|
||||
|
||||
import AOS from 'aos'
|
||||
import 'aos/dist/aos.css' // You can also use <link> for styles
|
||||
import { isMobile } from '@/lib/utils'
|
||||
|
||||
const Ackee = dynamic(() => import('@/components/Ackee'), { ssr: false })
|
||||
const Gtag = dynamic(() => import('@/components/Gtag'), { ssr: false })
|
||||
@@ -57,7 +58,9 @@ const MyApp = ({ Component, pageProps }) => {
|
||||
|
||||
useEffect(() => {
|
||||
AOS.init()
|
||||
smoothscroll.polyfill()
|
||||
if (isMobile()) {
|
||||
smoothscroll.polyfill()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
@@ -78,5 +78,5 @@ export const BlogListScroll = props => {
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import CommonHead from '@/components/CommonHead'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import throttle from 'lodash.throttle'
|
||||
import Footer from './components/Footer'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
import SideRight from './components/SideRight'
|
||||
@@ -42,23 +42,20 @@ const LayoutBase = props => {
|
||||
</>
|
||||
)
|
||||
const { onLoading } = useGlobal()
|
||||
const throttleMs = 200
|
||||
const scrollListener = useCallback(throttle(() => {
|
||||
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
|
||||
|
||||
const scrollListener = () => {
|
||||
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 !== showFloatButton) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
// changePercent(per)
|
||||
})
|
||||
}
|
||||
if (shouldShow !== showFloatButton) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
}, throttleMs))
|
||||
useEffect(() => {
|
||||
document.addEventListener('scroll', scrollListener)
|
||||
return () => document.removeEventListener('scroll', scrollListener)
|
||||
|
||||
@@ -7,7 +7,7 @@ const Announcement = ({ post, className }) => {
|
||||
const { locale } = useGlobal()
|
||||
if (post?.blockMap) {
|
||||
return <div className={className}>
|
||||
<section id='announcement-wrapper' className="hover:shadow-md dark:text-gray-300 border dark:border-black rounded-xl px-2 py-4 bg-white dark:bg-hexo-black-gray">
|
||||
<section id='announcement-wrapper' className="dark:text-gray-300 border dark:border-black rounded-xl px-2 py-4 bg-white dark:bg-hexo-black-gray">
|
||||
<div><i className='mr-2 fas fa-bullhorn' />{locale.COMMON.ANNOUNCEMENT}</div>
|
||||
{post && (<div id="announcement-content">
|
||||
<NotionPage post={post} className='text-center ' />
|
||||
|
||||
@@ -1,98 +1,39 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import Link from 'next/link'
|
||||
import React from 'react'
|
||||
import TagItemMini from './TagItemMini'
|
||||
import CONFIG_HEXO from '../config_hexo'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import { BlogPostCardInfo } from './BlogPostCardInfo'
|
||||
// import Image from 'next/image'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary, siteInfo }) => {
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
const showPreview = CONFIG_HEXO.POST_LIST_PREVIEW && post.blockMap
|
||||
if (post && !post.page_cover && CONFIG_HEXO.POST_LIST_COVER_DEFAULT) {
|
||||
post.page_cover = siteInfo?.pageCover
|
||||
}
|
||||
const showPageCover = CONFIG_HEXO.POST_LIST_COVER && post?.page_cover
|
||||
const delay = (index % 2) * 200
|
||||
|
||||
return (
|
||||
<div
|
||||
key={post.id}
|
||||
data-aos="fade-up"
|
||||
data-aso-delay="200"
|
||||
data-aos-duration="200"
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className={`flex md:flex-row flex-col-reverse ${CONFIG_HEXO.POST_LIST_IMG_CROSSOVER ? 'even:md:flex-row-reverse' : ''}
|
||||
w-full md:h-72 h-96 justify-between overflow-hidden shadow-md
|
||||
w-full md:h-52 justify-between overflow-hidden
|
||||
border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray`}>
|
||||
|
||||
<div className={`lg:p-8 p-4 flex flex-col ${showPageCover ? 'md:w-7/12 w-full' : 'w-full'}`}>
|
||||
<Link
|
||||
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||
passHref
|
||||
className={`replace cursor-pointer hover:underline text-2xl ${showPreview ? 'text-center' : ''
|
||||
} leading-tight text-gray-600 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}>
|
||||
{/* 文字内容 */}
|
||||
<div
|
||||
data-aos="fade-up"
|
||||
data-aos-duration="200"
|
||||
data-aos-delay={delay}
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className={`flex flex-col lg:p-6 p-4 ${showPageCover ? 'md:w-7/12 w-full ' : 'w-full'}`}>
|
||||
|
||||
{post.title}
|
||||
<BlogPostCardInfo post={post} showPreview={showPreview} showSummary={showSummary}/>
|
||||
|
||||
</Link>
|
||||
|
||||
<div
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
||||
>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
<i className="far fa-calendar-alt mr-1" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{(!showPreview || showSummary) && !post.results && (
|
||||
<p style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: '4', WebkitBoxOrient: 'vertical' }}
|
||||
className="replace h-full max-h-32 my-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* 搜索结果 */}
|
||||
{post.results && (
|
||||
<p className="mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{showPreview && (
|
||||
<div className="overflow-ellipsis truncate">
|
||||
<NotionPage post={post} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="text-gray-400 justify-between flex">
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
|
||||
</Link>
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 图片封面 */}
|
||||
{showPageCover && !showPreview && post?.page_cover && (
|
||||
<div className="flex overflow-hidden md:w-5/12 h-full">
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
@@ -100,6 +41,7 @@ const BlogPostCard = ({ post, showSummary, siteInfo }) => {
|
||||
<img
|
||||
src={post?.page_cover}
|
||||
alt={post.title}
|
||||
loading='lazy'
|
||||
className="w-full cursor-pointer object-cover duration-200 hover:scale-125 "
|
||||
/>
|
||||
{/* <div className='relative w-full h-full'>
|
||||
|
||||
79
themes/hexo/components/BlogPostCardInfo.js
Normal file
79
themes/hexo/components/BlogPostCardInfo.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import NotionPage from '@/components/NotionPage'
|
||||
import Link from 'next/link'
|
||||
import TagItemMini from './TagItemMini'
|
||||
|
||||
export const BlogPostCardInfo = ({ post, showPreview, showSummary }) => {
|
||||
return <>
|
||||
{/* 标题 */}
|
||||
<Link
|
||||
href={`${BLOG.SUB_PATH}/${post.slug}`}
|
||||
passHref
|
||||
className={`replace cursor-pointer hover:underline text-2xl ${showPreview ? 'text-center' : ''
|
||||
} leading-tight text-gray-600 dark:text-gray-100 hover:text-indigo-700 dark:hover:text-indigo-400`}>
|
||||
|
||||
{post.title}
|
||||
|
||||
</Link>
|
||||
|
||||
{/* 日期 */}
|
||||
<div
|
||||
className={`flex mt-2 items-center ${showPreview ? 'justify-center' : 'justify-start'
|
||||
} flex-wrap dark:text-gray-500 text-gray-400 hover:text-indigo-700 dark:hover:text-indigo-400`}
|
||||
>
|
||||
<Link
|
||||
href={`/archive#${post?.date?.start_date?.substr(0, 7)}`}
|
||||
passHref
|
||||
className="font-light hover:underline cursor-pointer text-sm leading-4 mr-3">
|
||||
|
||||
<i className="far fa-calendar-alt mr-1" />
|
||||
{post.date?.start_date || post.lastEditedTime}
|
||||
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* 摘要 */}
|
||||
{(!showPreview || showSummary) && !post.results && (
|
||||
<p style={{ overflow: 'hidden', textOverflow: 'ellipsis', display: '-webkit-box', WebkitLineClamp: '2', WebkitBoxOrient: 'vertical' }}
|
||||
className="replace py-3 h-28 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.summary}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* 搜索结果 */}
|
||||
{post.results && (
|
||||
<p className="mt-4 text-gray-700 dark:text-gray-300 text-sm font-light leading-7">
|
||||
{post.results.map(r => (
|
||||
<span key={r}>{r}</span>
|
||||
))}
|
||||
</p>
|
||||
)}
|
||||
{/* 预览 */}
|
||||
{showPreview && (
|
||||
<div className="overflow-ellipsis truncate">
|
||||
<NotionPage post={post} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 分类标签 */}
|
||||
<div className="text-gray-400 justify-between flex">
|
||||
<Link
|
||||
href={`/category/${post.category}`}
|
||||
passHref
|
||||
className="cursor-pointer font-light text-sm hover:underline hover:text-indigo-700 dark:hover:text-indigo-400 transform">
|
||||
|
||||
<i className="mr-1 far fa-folder" />
|
||||
{post.category}
|
||||
|
||||
</Link>
|
||||
<div className="md:flex-nowrap flex-wrap md:justify-start inline-block">
|
||||
<div>
|
||||
{' '}
|
||||
{post.tagItems.map(tag => (
|
||||
<TagItemMini key={tag.name} tag={tag} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
@@ -22,7 +22,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
||||
{/* 文章列表 */}
|
||||
<div className="space-y-6 px-2">
|
||||
{posts.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} siteInfo={siteInfo}/>
|
||||
<BlogPostCard index={posts.indexOf(post)} key={post.id} post={post} siteInfo={siteInfo}/>
|
||||
))}
|
||||
</div>
|
||||
{showPagination && <PaginationNumber page={page} totalPage={totalPage} />}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// import Image from 'next/image'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import Typed from 'typed.js'
|
||||
import CONFIG_HEXO from '../config_hexo'
|
||||
import NavButtonGroup from './NavButtonGroup'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
let wrapperTop = 0
|
||||
let windowTop = 0
|
||||
@@ -53,6 +54,34 @@ const Header = props => {
|
||||
})
|
||||
}
|
||||
|
||||
const autoScrollEnd = () => {
|
||||
if (autoScroll) {
|
||||
windowTop = window.scrollY
|
||||
autoScroll = false
|
||||
}
|
||||
}
|
||||
const throttleMs = 200
|
||||
const scrollTrigger = useCallback(throttle(() => {
|
||||
if (screen.width <= 768) {
|
||||
return
|
||||
}
|
||||
|
||||
const scrollS = window.scrollY
|
||||
// 自动滚动
|
||||
if ((scrollS > windowTop) & (scrollS < window.innerHeight) && !autoScroll
|
||||
) {
|
||||
autoScroll = true
|
||||
window.scrollTo({ top: wrapperTop, behavior: 'smooth' })
|
||||
autoScrollEnd()
|
||||
}
|
||||
if ((scrollS < windowTop) && (scrollS < window.innerHeight) && !autoScroll) {
|
||||
autoScroll = true
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
autoScrollEnd()
|
||||
}
|
||||
windowTop = scrollS
|
||||
}, throttleMs))
|
||||
|
||||
return (
|
||||
<header
|
||||
id="header"
|
||||
@@ -89,37 +118,4 @@ const Header = props => {
|
||||
)
|
||||
}
|
||||
|
||||
const autoScrollEnd = () => {
|
||||
if (autoScroll) {
|
||||
windowTop = window.scrollY
|
||||
autoScroll = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动吸附滚动,移动端体验不好暂时关闭
|
||||
*/
|
||||
const scrollTrigger = () => {
|
||||
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' })
|
||||
autoScrollEnd()
|
||||
}
|
||||
if ((scrollS < windowTop) && (scrollS < window.innerHeight) && !autoScroll) {
|
||||
autoScroll = true
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
autoScrollEnd()
|
||||
}
|
||||
windowTop = scrollS
|
||||
})
|
||||
}
|
||||
|
||||
export default Header
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import BLOG from '@/blog.config'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Image from 'next/image'
|
||||
// import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
@@ -40,14 +40,16 @@ const LatestPostsGroup = ({ latestPosts, siteInfo }) => {
|
||||
className={'my-2 flex'}>
|
||||
|
||||
<div className="w-20 h-16 overflow-hidden relative">
|
||||
<Image
|
||||
{/* <Image
|
||||
src={headerImage}
|
||||
fill
|
||||
style={{ objectFit: 'cover' }}
|
||||
placeholder='blur'
|
||||
blurDataURL='/bg_image.jpg'
|
||||
quality={10}
|
||||
alt={post.title} />
|
||||
alt={post.title} /> */}
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img src={headerImage} className='object-cover w-full h-full'/>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import CategoryGroup from './CategoryGroup'
|
||||
import Collapse from './Collapse'
|
||||
import Logo from './Logo'
|
||||
@@ -9,6 +9,7 @@ import TagGroups from './TagGroups'
|
||||
import MenuButtonGroupTop from './MenuButtonGroupTop'
|
||||
import MenuList from './MenuList'
|
||||
import { useRouter } from 'next/router'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
let windowTop = 0
|
||||
|
||||
@@ -39,39 +40,40 @@ const TopNav = props => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const scrollTrigger = () => {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollS = window.scrollY
|
||||
const nav = document.querySelector('#sticky-nav')
|
||||
const header = document.querySelector('#header')
|
||||
// 是否将导航栏透明
|
||||
const navTransparent = (scrollS < document.documentElement.clientHeight - 12 && router.route === '/') || scrollS < 300 // 透明导航条的条件
|
||||
const throttleMs = 200
|
||||
|
||||
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')
|
||||
}
|
||||
const scrollTrigger = useCallback(throttle(() => {
|
||||
const scrollS = window.scrollY
|
||||
const nav = document.querySelector('#sticky-nav')
|
||||
const header = document.querySelector('#header')
|
||||
// 是否将导航栏透明
|
||||
const navTransparent = (scrollS < document.documentElement.clientHeight - 12 && router.route === '/') || scrollS < 300 // 透明导航条的条件
|
||||
|
||||
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight * 2)// 非首页无大图时影藏顶部 滚动条置顶时隐藏
|
||||
if (!showNav) {
|
||||
nav && nav.classList.replace('top-0', '-top-20')
|
||||
windowTop = scrollS
|
||||
} else {
|
||||
nav && nav.classList.replace('-top-20', 'top-0')
|
||||
windowTop = scrollS
|
||||
}
|
||||
navDarkMode()
|
||||
})
|
||||
}
|
||||
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')
|
||||
}
|
||||
|
||||
const showNav = scrollS <= windowTop || scrollS < 5 || (header && scrollS <= header.clientHeight * 2)// 非首页无大图时影藏顶部 滚动条置顶时隐藏
|
||||
if (!showNav) {
|
||||
nav && nav.classList.replace('top-0', '-top-20')
|
||||
windowTop = scrollS
|
||||
} else {
|
||||
nav && nav.classList.replace('-top-20', 'top-0')
|
||||
windowTop = scrollS
|
||||
}
|
||||
navDarkMode()
|
||||
}, throttleMs)
|
||||
)
|
||||
|
||||
const navDarkMode = () => {
|
||||
const nav = document.getElementById('sticky-nav')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import CommonHead from '@/components/CommonHead'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import Footer from './components/Footer'
|
||||
import JumpToTopButton from './components/JumpToTopButton'
|
||||
@@ -9,6 +9,7 @@ import LoadingCover from './components/LoadingCover'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import BLOG from '@/blog.config'
|
||||
import FloatDarkModeButton from './components/FloatDarkModeButton'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
/**
|
||||
* 基础布局 采用左右两侧布局,移动端使用顶部导航栏
|
||||
@@ -21,15 +22,15 @@ const LayoutBase = props => {
|
||||
const [show, switchShow] = useState(false)
|
||||
const { onLoading } = useGlobal()
|
||||
|
||||
const scrollListener = () => {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollY = window.pageYOffset
|
||||
const shouldShow = scrollY > 300
|
||||
if (shouldShow !== show) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
})
|
||||
}
|
||||
const throttleMs = 200
|
||||
const scrollListener = useCallback(throttle(() => {
|
||||
const scrollY = window.pageYOffset
|
||||
const shouldShow = scrollY > 300
|
||||
if (shouldShow !== show) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
}, throttleMs))
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('scroll', scrollListener)
|
||||
return () => document.removeEventListener('scroll', scrollListener)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import React, { useCallback, useEffect } from 'react'
|
||||
import { ArticleLock } from './components/ArticleLock'
|
||||
import HeaderArticle from './components/HeaderArticle'
|
||||
import LayoutBase from './LayoutBase'
|
||||
@@ -9,21 +9,21 @@ import ArticleCopyright from './components/ArticleCopyright'
|
||||
import { ArticleInfo } from './components/ArticleInfo'
|
||||
import Catalog from './components/Catalog'
|
||||
import JumpToCommentButton from './components/JumpToCommentButton'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
export const LayoutSlug = props => {
|
||||
const { post, lock, validPassword } = props
|
||||
|
||||
const [show, switchShow] = React.useState(false)
|
||||
const throttleMs = 200
|
||||
|
||||
const scrollListener = () => {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollY = window.pageYOffset
|
||||
const shouldShow = scrollY > 220 && post?.toc?.length > 0
|
||||
if (shouldShow !== show) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
})
|
||||
}
|
||||
const scrollListener = useCallback(throttle(() => {
|
||||
const scrollY = window.pageYOffset
|
||||
const shouldShow = scrollY > 220 && post?.toc?.length > 0
|
||||
if (shouldShow !== show) {
|
||||
switchShow(shouldShow)
|
||||
}
|
||||
}, throttleMs))
|
||||
useEffect(() => {
|
||||
document.addEventListener('scroll', scrollListener)
|
||||
return () => document.removeEventListener('scroll', scrollListener)
|
||||
|
||||
@@ -5,18 +5,20 @@ import TagItemMini from './TagItemMini'
|
||||
import CONFIG_MATERY from '../config_matery'
|
||||
// import Image from 'next/image'
|
||||
|
||||
const BlogPostCard = ({ post, showSummary, siteInfo }) => {
|
||||
const BlogPostCard = ({ index, post, showSummary, siteInfo }) => {
|
||||
const showPreview = CONFIG_MATERY.POST_LIST_PREVIEW && post.blockMap
|
||||
// matery 主题默认强制显示图片
|
||||
if (post && !post.page_cover) {
|
||||
post.page_cover = siteInfo?.pageCover
|
||||
}
|
||||
const showPageCover = CONFIG_MATERY.POST_LIST_COVER && !showPreview && post?.page_cover
|
||||
const delay = (index % 3) * 300
|
||||
return (
|
||||
<div
|
||||
data-aos="zoom-in"
|
||||
data-aos-duration="200"
|
||||
data-aos-once="false"
|
||||
data-aos-duration="500"
|
||||
data-aos-delay={delay}
|
||||
data-aos-once="true"
|
||||
data-aos-anchor-placement="top-bottom"
|
||||
className="w-full mb-4 h-full overflow-auto shadow-md border dark:border-black rounded-xl bg-white dark:bg-hexo-black-gray">
|
||||
|
||||
@@ -26,7 +28,8 @@ const BlogPostCard = ({ post, showSummary, siteInfo }) => {
|
||||
{/* 头部图片 填充卡片 */}
|
||||
{showPageCover && (
|
||||
<Link href={`${BLOG.SUB_PATH}/${post.slug}`} passHref legacyBehavior>
|
||||
<div className="flex flex-grow w-full relative duration-200 bg-black rounded-t-md cursor-pointer transform overflow-hidden">
|
||||
<div
|
||||
className="flex flex-grow w-full relative duration-200 bg-black rounded-t-md cursor-pointer transform overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={post?.page_cover}
|
||||
|
||||
@@ -23,7 +23,7 @@ const BlogPostListPage = ({ page = 1, posts = [], postCount, siteInfo }) => {
|
||||
{/* 文章列表 */}
|
||||
<div className="px-4 pt-4 xl:columns-3 md:columns-2 pb-24" >
|
||||
{posts.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} siteInfo={siteInfo} />
|
||||
<BlogPostCard index={posts.indexOf(post)} key={post.id} post={post} siteInfo={siteInfo} />
|
||||
))}
|
||||
</div>
|
||||
{showPagination && <PaginationSimple page={page} totalPage={totalPage} />}
|
||||
|
||||
@@ -2,9 +2,10 @@ import BLOG from '@/blog.config'
|
||||
import BlogPostCard from './BlogPostCard'
|
||||
import BlogPostListEmpty from './BlogPostListEmpty'
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import CONFIG_MATERY from '../config_matery'
|
||||
import { getListByPage } from '@/lib/utils'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
/**
|
||||
* 博客列表滚动分页
|
||||
@@ -29,8 +30,8 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_MA
|
||||
updatePage(page + 1)
|
||||
}
|
||||
|
||||
// 监听滚动自动分页加载
|
||||
const scrollTrigger = () => {
|
||||
const throttleMs = 200
|
||||
const scrollTrigger = useCallback(throttle(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollS = window.scrollY + window.outerHeight
|
||||
const clientHeight = targetRef ? (targetRef.current ? (targetRef.current.clientHeight) : 0) : 0
|
||||
@@ -38,14 +39,13 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_MA
|
||||
handleGetMore()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}, throttleMs))
|
||||
// 监听滚动
|
||||
React.useEffect(() => {
|
||||
// window.addEventListener('scroll', scrollTrigger)
|
||||
// return () => {
|
||||
// window.removeEventListener('scroll', scrollTrigger)
|
||||
// }
|
||||
window.addEventListener('scroll', scrollTrigger)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', scrollTrigger)
|
||||
}
|
||||
})
|
||||
|
||||
const targetRef = React.useRef(null)
|
||||
@@ -59,7 +59,7 @@ const BlogPostListScroll = ({ posts = [], currentSearch, showSummary = CONFIG_MA
|
||||
{/* 文章列表 */}
|
||||
<div className='flex flex-wrap space-y-1 lg:space-y-4 px-2'>
|
||||
{postsToShow.map(post => (
|
||||
<BlogPostCard key={post.id} post={post} showSummary={showSummary} siteInfo={siteInfo}/>
|
||||
<BlogPostCard index={posts.indexOf(post)} key={post.id} post={post} showSummary={showSummary} siteInfo={siteInfo}/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// import Image from 'next/image'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import Typed from 'typed.js'
|
||||
import CONFIG_MATERY from '../config_matery'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
let wrapperTop = 0
|
||||
let windowTop = 0
|
||||
let autoScroll = false
|
||||
const enableAutoScroll = false // 是否开启自动吸附滚动
|
||||
const throttleMs = 200
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -53,29 +55,27 @@ const Header = props => {
|
||||
* 吸附滚动,移动端关闭
|
||||
* @returns
|
||||
*/
|
||||
const scrollTrigger = () => {
|
||||
requestAnimationFrame(() => {
|
||||
if (screen.width <= 768) {
|
||||
return
|
||||
}
|
||||
const scrollTrigger = useCallback(throttle(() => {
|
||||
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
|
||||
}, throttleMs))
|
||||
|
||||
function updateHeaderHeight() {
|
||||
requestAnimationFrame(() => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useGlobal } from '@/lib/global'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import CategoryGroup from './CategoryGroup'
|
||||
import Logo from './Logo'
|
||||
import SearchDrawer from './SearchDrawer'
|
||||
@@ -8,6 +8,7 @@ import TagGroups from './TagGroups'
|
||||
import MenuButtonGroupTop from './MenuButtonGroupTop'
|
||||
import SideBarDrawer from '@/components/SideBarDrawer'
|
||||
import SideBar from './SideBar'
|
||||
import throttle from 'lodash.throttle'
|
||||
|
||||
let windowTop = 0
|
||||
|
||||
@@ -21,8 +22,8 @@ const TopNav = props => {
|
||||
const { locale } = useGlobal()
|
||||
const searchDrawer = useRef()
|
||||
const { isDarkMode } = useGlobal()
|
||||
|
||||
const scrollTrigger = () => {
|
||||
const throttleMs = 200
|
||||
const scrollTrigger = useCallback(throttle(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const scrollS = window.scrollY
|
||||
const nav = document.querySelector('#sticky-nav')
|
||||
@@ -52,7 +53,7 @@ const TopNav = props => {
|
||||
}
|
||||
navDarkMode()
|
||||
})
|
||||
}
|
||||
}, throttleMs))
|
||||
|
||||
const navDarkMode = () => {
|
||||
const nav = document.getElementById('sticky-nav')
|
||||
|
||||
@@ -23,8 +23,8 @@ export const ArticleInfo = (props) => {
|
||||
<div className="mb-4 text-sm text-gray-700 dark:text-gray-300">
|
||||
<span> <i className="fa-regular fa-user"></i> <a href={CONFIG_SIMPLE.AUTHOR_LINK}>{BLOG.AUTHOR}</a></span>
|
||||
<span> - <i className="fa-regular fa-clock"></i> {post.date?.start_date || post.createdTime}</span>
|
||||
{post.category && <span> - <i className="fa-regular fa-folder"></i> <a href="#" className="hover:text-red-400 transition-all duration-200">{post.category}</a></span>}
|
||||
{post.tags && post.tags?.length > 0 && post.tags.map(t => <span key={t}> / <Link href={`tag/${t}`}><span className=' hover:text-red-400 transition-all duration-200'>{t}</span></Link></span>)}
|
||||
{post.category && <span> - <i className="fa-regular fa-folder"></i> <a href={`/category/${post.category}`} className="hover:text-red-400 transition-all duration-200">{post.category}</a></span>}
|
||||
{post.tags && post.tags?.length > 0 && post.tags.map(t => <span key={t}> / <Link href={`/tag/${t}`}><span className=' hover:text-red-400 transition-all duration-200'>{t}</span></Link></span>)}
|
||||
</div>
|
||||
</>)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user