theme-photo

This commit is contained in:
tangly1024.com
2024-11-04 16:11:27 +08:00
parent c43924db77
commit 67019e9477
9 changed files with 169 additions and 180 deletions

View File

@@ -9,7 +9,7 @@ import MenuHierarchical from './MenuHierarchical'
export const Header = props => {
return (
<>
<header className='w-full px-8 h-20 z-20 flex lg:flex-row md:flex-col justify-center items-center'>
<header className='w-full px-8 h-20 z-30 flex lg:flex-row md:flex-col justify-center items-center'>
{/* 左侧Logo */}
<Link
href='/'
@@ -18,7 +18,7 @@ export const Header = props => {
</Link>
{/* 右侧使用一个三级菜单 */}
<div className='md:w-auto text-center flex space-x-2'>
<div className='ml-6 mt-7'>
<MenuHierarchical {...props} />
</div>
</header>

View File

@@ -2,7 +2,7 @@ import Collapse from '@/components/Collapse'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { usePhotoGlobal } from '..'
import CONFIG from '../config'
import { MenuItemCollapse } from './MenuItemCollapse'
@@ -20,6 +20,9 @@ export default function MenuHierarchical(props) {
const toggleMenuOpen = () => {
setIsOpen(!isOpen)
}
const closeModal = () => {
setIsOpen(false)
}
let links = [
{
id: 1,
@@ -59,19 +62,31 @@ export default function MenuHierarchical(props) {
links = customMenu
}
const [title, setTitle] = useState(siteConfig('BIO'))
useEffect(() => {
const currentLink = links.find(link => link.href === router.pathname)
if (currentLink) {
setTitle(currentLink.name)
}
closeModal()
}, [router])
return (
<div className='absolute top-0'>
<div className='absolute top-0 mt-7'>
{/* 菜单按钮 */}
<div onClick={toggleMenuOpen} className='w-8 cursor-pointer'>
{isOpen ? (
<i className='fas fa-times' />
) : (
<i className='fas fa-bars' />
)}
<div
onClick={toggleMenuOpen}
className=' whitespace-nowrap cursor-pointer'>
{title}
</div>
<Collapse collapseRef={collapseRef} type='vertical' isOpen={isOpen}>
<Collapse
className='z-50'
collapseRef={collapseRef}
type='vertical'
isOpen={isOpen}>
{/* 移动端菜单 */}
<menu id='nav-menu-mobile' className='my-auto justify-start'>
<menu id='nav-menu-mobile' className='my-4 space-y-4 justify-start'>
{links?.map(
(link, index) =>
link &&
@@ -87,6 +102,13 @@ export default function MenuHierarchical(props) {
)}
</menu>
</Collapse>
{/* 遮罩 */}
{isOpen && (
<div
onClick={closeModal}
className='-z-10 fixed top-0 left-0 w-full h-full flex items-center justify-center bg-glassmorphism'
/>
)}
</div>
)
}

View File

@@ -28,14 +28,12 @@ export const MenuItemCollapse = props => {
return (
<>
<div
className='select-none w-full px-6 py-2 text-left '
onClick={toggleShow}>
<div className='select-none w-full text-left ' onClick={toggleShow}>
{!hasSubMenu && (
<Link
href={link?.href}
target={link?.target}
className='flex justify-between pl-2 pr-4 dark:text-gray-200 no-underline tracking-widest'>
className='flex justify-between dark:text-gray-200 no-underline tracking-widest'>
<span className=' transition-all items-center duration-200'>
{link?.icon && <i className={link.icon + ' mr-4'} />}
{link?.name}
@@ -45,7 +43,7 @@ export const MenuItemCollapse = props => {
{hasSubMenu && (
<div
onClick={hasSubMenu ? toggleOpenSubMenu : null}
className='flex items-center justify-between pl-2 pr-4 cursor-pointer dark:text-gray-200 no-underline tracking-widest'>
className='flex items-center justify-between cursor-pointer dark:text-gray-200 no-underline tracking-widest'>
<span className='transition-all items-center duration-200'>
{link?.icon && <i className={link.icon + ' mr-4'} />}
{link?.name}
@@ -66,9 +64,9 @@ export const MenuItemCollapse = props => {
return (
<div
key={index}
className='dark:text-gray-200 text-left px-3 justify-start tracking-widest transition-all duration-200 py-3 pr-6'>
className='dark:text-gray-200 text-left px-3 justify-start py-1 tracking-widest transition-all duration-200 pr-6'>
<Link href={sLink.href} target={link?.target}>
<span className='text-sm ml-4 whitespace-nowrap'>
<span className='ml-4 whitespace-nowrap'>
{link?.icon && <i className={sLink.icon + ' mr-2'} />}{' '}
{sLink.title}
</span>

View File

@@ -3,51 +3,49 @@ import NotionIcon from '@/components/NotionIcon'
import { siteConfig } from '@/lib/config'
import { useGlobal } from '@/lib/global'
import { formatDateFmt } from '@/lib/utils/formatDate'
import Link from 'next/link'
/**
* 普通的博客卡牌
* 带封面图
*/
const PostItemCard = ({ post }) => {
const PostItemCard = ({ post, className }) => {
const { siteInfo } = useGlobal()
const cover = post?.pageCoverThumbnail || siteInfo?.pageCover
return (
<div key={post.id} className='mb-6 max-w-screen-3xl'>
<div className='flex flex-col space-y-3 relative'>
{/* <Link
href={post?.href}
passHref
className={
'cursor-pointer hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
}> */}
<div className='w-full h-3/4 aspect-video overflow-hidden mb-2'>
<div key={post?.id} className={className}>
<div className='space-y-3 relative justify-center items-center text-gray-500'>
<div className='h-full overflow-hidden'>
<LazyImage
alt={post?.title}
src={cover}
style={cover ? {} : { height: '0px' }}
className='w-full h-3/4 aspect-video object-cover select-none pointer-events-none'
className='h-full max-h-[70vh] object-cover select-none pointer-events-none'
/>
</div>
{/* </Link> */}
<div className='absolute bottom-0'>
{/* <Link
<div className='text-center'>
<Link
href={post?.href}
passHref
className={
'text-xl cursor-pointer hover:underline leading-tight text-gray-700 dark:text-gray-300 hover:text-gray-500 dark:hover:text-gray-400'
}> */}
<h2 className='select-none pointer-events-none'>
{siteConfig('POST_TITLE_ICON') && (
<NotionIcon icon={post.pageIcon} />
)}
{post.title}
</h2>
{/* </Link> */}
'cursor-pointer hover:underline leading-tight dark:text-gray-300 '
}>
<h2 className='select-none pointer-events-none'>
{siteConfig('POST_TITLE_ICON') && (
<NotionIcon icon={post?.pageIcon} />
)}
{post?.title}
</h2>
</Link>
<div className='text-sm select-none pointer-events-none'>
{formatDateFmt(post.publishDate, 'yyyy-MM')}
</div>
{/* 发布日期 */}
<Link
className='text-sm'
href={`/archive#${formatDateFmt(post?.publishDate, 'yyyy-MM')}`}
passHref>
{formatDateFmt(post?.publishDate, 'yyyy-MM')}
</Link>
</div>
</div>
</div>

View File

@@ -1,111 +1,101 @@
import { useRef, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import PostItemCard from './PostItemCard'
const Swiper = ({ posts }) => {
const [currentIndex, setCurrentIndex] = useState(0)
const containerRef = useRef(null)
const InertiaCarousel = ({ posts }) => {
const carouselRef = useRef(null)
const [isDragging, setIsDragging] = useState(false)
const [startX, setStartX] = useState(0)
const [scrollLeft, setScrollLeft] = useState(0)
const [lastX, setLastX] = useState(0) // 上一次的位置
const [velocity, setVelocity] = useState(0)
const animationRef = useRef(null)
const touchStartPos = useRef({ x: 0, y: 0 })
const isDragging = useRef(false)
const scrollStartLeft = useRef(0) // 记录拖拽开始时的滚动位置
// 处理鼠标和触摸开始事件
const handleDragStart = e => {
const x = e.touches ? e.touches[0].clientX : e.clientX
touchStartPos.current = { x }
isDragging.current = true
scrollStartLeft.current = containerRef.current.scrollLeft
// 更新鼠标样式
containerRef.current.style.cursor = 'grabbing'
// 开始拖拽事件
const startDrag = e => {
e.preventDefault()
setIsDragging(true)
const startPosition = e.pageX || e.touches[0].pageX
setStartX(startPosition - carouselRef.current.offsetLeft)
setScrollLeft(carouselRef.current.scrollLeft)
setLastX(startPosition) // 初始化上一次的位置
cancelInertiaScroll() // 停止任何正在进行的惯性动画
}
// 处理鼠标和触摸移动事件
const handleDragMove = e => {
if (!isDragging.current) return
// 拖拽中事件
const duringDrag = e => {
if (!isDragging) return
e.preventDefault()
const currentPosition = e.pageX || e.touches[0].pageX
const distance = currentPosition - startX
carouselRef.current.scrollLeft = scrollLeft - distance
const x = e.touches ? e.touches[0].clientX : e.clientX
const deltaX = touchStartPos.current.x - x
// 根据拖动的距离更新滚动位置
containerRef.current.scrollLeft = scrollStartLeft.current + deltaX
// 计算当前速度
const deltaX = currentPosition - lastX
setVelocity(deltaX) // 更新速度
setLastX(currentPosition) // 更新 lastX 为当前位置
}
// 处理鼠标和触摸结束事件
const handleDragEnd = () => {
isDragging.current = false
containerRef.current.style.cursor = 'grab'
// 结束拖拽事件,启动惯性滚动
const endDrag = () => {
setIsDragging(false)
startInertiaScroll(velocity) // 根据最终速度启动惯性滚动
}
// 处理指示器点击事件
const handleIndicatorClick = index => {
setCurrentIndex(index)
scrollToCard(index)
// 惯性滚动函数
const startInertiaScroll = initialVelocity => {
let currentVelocity = initialVelocity
const decay = 0.95 // 惯性衰减系数
const animate = () => {
if (Math.abs(currentVelocity) > 0.5) {
// 仅当速度足够大时继续滚动
carouselRef.current.scrollLeft -= currentVelocity
currentVelocity *= decay // 速度衰减
animationRef.current = requestAnimationFrame(animate)
} else {
cancelAnimationFrame(animationRef.current)
}
}
animate()
}
// 滚动到特定卡片
const scrollToCard = index => {
const container = containerRef.current
if (!container) return
const cardWidth = container.scrollWidth / posts.length
container.scrollTo({
left: index * cardWidth - cardWidth / 6, // 调整位置以居中
behavior: 'smooth'
})
// 取消惯性滚动
const cancelInertiaScroll = () => {
if (animationRef.current) {
cancelAnimationFrame(animationRef.current)
}
}
useEffect(() => {
return () => cancelInertiaScroll() // 清除动画
}, [])
return (
<div className='relative w-full mx-auto px-12 my-8'>
<div
className='absolute inset-y-0 left-0 w-12 z-10 cursor-pointer bg-black hover:opacity-20 opacity-10 duration-100'
onClick={() =>
handleIndicatorClick(
currentIndex === 0 ? posts.length - 1 : currentIndex - 1
)
}></div>
<div
ref={carouselRef}
className={`flex w-screen overflow-x-auto space-x-6 ${
isDragging ? 'cursor-grabbing' : 'cursor-grab'
}`}
onMouseDown={startDrag}
onMouseMove={duringDrag}
onMouseUp={endDrag}
onMouseLeave={endDrag}
onTouchStart={startDrag}
onTouchMove={duringDrag}
onTouchEnd={endDrag}
style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
{/* Carousel items */}
<div
className='absolute inset-y-0 right-0 w-12 z-10 cursor-pointer bg-black hover:opacity-20 opacity-10 duration-100'
onClick={() =>
handleIndicatorClick(
currentIndex === posts.length - 1 ? 0 : currentIndex + 1
)
}></div>
<div
ref={containerRef}
className='relative w-full overflow-x-hidden py-4 cursor-grab'
onTouchStart={handleDragStart}
onTouchMove={handleDragMove}
onTouchEnd={handleDragEnd}
onMouseDown={handleDragStart}
onMouseMove={handleDragMove}
onMouseUp={handleDragEnd}
onMouseLeave={handleDragEnd}
style={{ WebkitOverflowScrolling: 'touch' }}>
<div className='flex gap-x-4 transition-transform'>
{posts.map((item, index) => (
<div key={index} className='w-3/4 flex-shrink-0'>
<PostItemCard post={item} />
</div>
))}
</div>
</div>
<div className='absolute bottom-0 left-0 right-0 flex justify-center space-x-2'>
{posts.map((_, index) => (
<button
<div className='min-w-[33vw]' />
{posts &&
posts?.map((post, index) => (
<PostItemCard
className='min-w-[33vw] flex items-center justify-center'
key={index}
onClick={() => handleIndicatorClick(index)}
className={`w-3 h-3 rounded-full ${
currentIndex === index
? 'bg-black dark:bg-white'
: 'bg-gray-300 dark:bg-gray-700'
}`}></button>
post={post}
/>
))}
</div>
</div>
)
}
export default Swiper
export default InertiaCarousel

View File

@@ -65,7 +65,7 @@ const LayoutBase = props => {
<ThemeGlobalPhoto.Provider
value={{ searchModal, expandMenu, updateExpandMenu, collapseRef }}>
<div
id='theme-movie'
id='theme-photo'
className={`${siteConfig('FONT_STYLE')} dark:text-gray-300 duration-300 transition-all bg-white dark:bg-[#2A2A2A] scroll-smooth min-h-screen flex flex-col justify-between`}>
<Style />
@@ -133,7 +133,7 @@ const LayoutIndex = props => {
*/
const LayoutPostList = props => {
return (
<div className='max-w-[90rem] mx-auto'>
<div className='mx-auto'>
<SlotBar {...props} />
{/* 滑动组件 */}
<Swiper {...props} />
@@ -480,15 +480,14 @@ const LayoutTagIndex = props => {
}
export {
Layout404,
LayoutArchive,
LayoutBase,
LayoutCategoryIndex,
LayoutIndex,
LayoutPostList,
LayoutSearch,
LayoutSlug,
LayoutTagIndex,
CONFIG as THEME_CONFIG
Layout404,
LayoutArchive,
LayoutBase,
LayoutCategoryIndex,
LayoutIndex,
LayoutPostList,
LayoutSearch,
LayoutSlug,
LayoutTagIndex,
CONFIG as THEME_CONFIG
}

View File

@@ -11,39 +11,17 @@ const Style = () => {
.dark body {
background-color: black;
}
.shadow-movie {
box-shadow:
0 26px 58px 0 rgba(0, 0, 0, 0.22),
0 5px 14px 0 rgba(0, 0, 0, 0.18);
// 毛玻璃背景色
.bg-glassmorphism {
background: hsla(0, 0%, 100%, 0.4);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
// 视频聚合走马灯
.notion-carousel {
width: 100%; /* 根据需要调整 */
overflow: hidden;
}
.notion-carousel-wrapper .notion-carousel {
display: none;
}
.notion-carousel-wrapper .notion-carousel.active {
display: block;
}
.notion-carousel-route div {
cursor: pointer;
margin-bottom: 0.2rem;
}
.notion-carousel-route div:hover {
text-decoration: underline;
}
.notion-carousel div {
height: auto !important;
aspect-ratio: 2/1 !important;
.dark .bg-glassmorphism {
background: hsla(0, 0%, 0%, 0.4);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
`}</style>
)