mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-06-02 07:26:45 +00:00
theme-game 列表效果微调; + LazyImage; +阴影+hover动效*Ad遮罩+边距+历史记录移除
This commit is contained in:
@@ -9,7 +9,7 @@ export const Footer = props => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<footer
|
<footer
|
||||||
className={`z-10 dark:bg-black bg-white p-2 rounded-lg relative mt-6 flex-shrink-0 m-auto w-full dark:text-gray-200 `}>
|
className={`z-10 dark:bg-black bg-white p-2 rounded-lg relative mt-6 flex-shrink-0 mb-4 w-full shadow dark:text-gray-200 `}>
|
||||||
{/* <hr className='my-2 border-black dark:border-gray-100' /> */}
|
{/* <hr className='my-2 border-black dark:border-gray-100' /> */}
|
||||||
{/* 页面底部 */}
|
{/* 页面底部 */}
|
||||||
<div className='w-full flex justify-between p-4 '>
|
<div className='w-full flex justify-between p-4 '>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { AdSlot } from '@/components/GoogleAdsense'
|
import { AdSlot } from '@/components/GoogleAdsense'
|
||||||
|
import LazyImage from '@/components/LazyImage'
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@@ -88,8 +89,8 @@ export const GameListIndexCombine = ({ posts }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='game-list-wrapper flex justify-center w-full px-2'>
|
<div className='game-list-wrapper flex justify-center w-full'>
|
||||||
<div className='game-grid mx-auto w-full h-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2'>
|
<div className='game-grid mx-auto w-full h-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 px-2 md:p-0'>
|
||||||
{components?.map((ItemComponent, index) => {
|
{components?.map((ItemComponent, index) => {
|
||||||
return ItemComponent
|
return ItemComponent
|
||||||
})}
|
})}
|
||||||
@@ -104,20 +105,24 @@ export const GameListIndexCombine = ({ posts }) => {
|
|||||||
*/
|
*/
|
||||||
const GameAd = () => {
|
const GameAd = () => {
|
||||||
return (
|
return (
|
||||||
<div className='card-group rounded game-ad h-[20rem] w-full overflow-hidden'>
|
<div className='card-group relative rounded-lg game-ad h-80 w-full overflow-hidden'>
|
||||||
<AdSlot type='flow' />
|
<AdSlot type='flow' />
|
||||||
|
<div className='absolute left-0 right-0 w-full h-full flex flex-col justify-center items-center bg-white'>
|
||||||
|
<p className='text-2xl'>{siteConfig('TITLE')}</p>
|
||||||
|
<p>{siteConfig('DESCRIPTION')}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 4卡组成一个大卡
|
* 大卡由2行2列小卡构成
|
||||||
* @param {*} param0
|
* @param {*} param0
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const GameItemGroup = ({ items }) => {
|
const GameItemGroup = ({ items }) => {
|
||||||
return (
|
return (
|
||||||
<div className='card-group h-[20rem] w-full grid grid-cols-2 grid-rows-2 gap-2'>
|
<div className='card-group h-80 w-full grid grid-cols-2 grid-rows-2 gap-3'>
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
<GameItem key={index} item={item} />
|
<GameItem key={index} item={item} />
|
||||||
))}
|
))}
|
||||||
@@ -140,38 +145,39 @@ const GameItem = ({ item, isLargeCard }) => {
|
|||||||
const video = item?.ext?.video
|
const video = item?.ext?.video
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
|
title={title}
|
||||||
href={`${url}`}
|
href={`${url}`}
|
||||||
|
className={`card-single ${isLargeCard ? 'h-80 ' : 'h-full text-xs'} w-full transition-all duration-200 hover:scale-105 shadow-md hover:shadow-lg relative rounded-lg overflow-hidden flex justify-center items-center
|
||||||
|
group hover:border-purple-400`}
|
||||||
onMouseOver={() => {
|
onMouseOver={() => {
|
||||||
setShowType('video')
|
setShowType('video')
|
||||||
}}
|
}}
|
||||||
onMouseOut={() => {
|
onMouseOut={() => {
|
||||||
setShowType('img')
|
setShowType('img')
|
||||||
}}
|
}}>
|
||||||
title={title}
|
|
||||||
className={`card-single ${
|
|
||||||
isLargeCard ? 'h-[20rem]' : 'h-full'
|
|
||||||
} w-full relative shadow rounded-md overflow-hidden flex justify-center items-center
|
|
||||||
group hover:border-purple-400`}>
|
|
||||||
<div className='text-center absolute bottom-0 invisible group-hover:bottom-2 group-hover:visible transition-all duration-200 text-white z-30'>
|
<div className='text-center absolute bottom-0 invisible group-hover:bottom-2 group-hover:visible transition-all duration-200 text-white z-30'>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
<div className='h-1/2 w-full absolute left-0 bottom-0 z-20 opacity-0 group-hover:opacity-75 transition-all duration-200'>
|
|
||||||
|
<div className='h-2/3 w-full absolute left-0 bottom-0 z-20 opacity-0 group-hover:opacity-75 transition-all duration-200'>
|
||||||
<div className='h-full w-full absolute bg-gradient-to-b from-transparent to-black'></div>
|
<div className='h-full w-full absolute bg-gradient-to-b from-transparent to-black'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showType === 'video' && (
|
{showType === 'video' && (
|
||||||
<video
|
<video
|
||||||
className={`z-10 object-cover w-full ${isLargeCard ? 'h-[20rem]' : 'h-full'} absolute overflow-hidden`}
|
className={`z-10 object-cover w-full ${isLargeCard ? 'h-80' : 'h-full'} absolute overflow-hidden`}
|
||||||
loop='true'
|
loop='true'
|
||||||
autoPlay
|
autoPlay
|
||||||
preload='none'>
|
preload='none'>
|
||||||
<source src={video} type='video/mp4' />
|
<source src={video} type='video/mp4' />
|
||||||
</video>
|
</video>
|
||||||
)}
|
)}
|
||||||
<img
|
<LazyImage
|
||||||
className='w-full h-full absolute object-cover group-hover:scale-105 duration-100 transition-all'
|
className='w-full h-full absolute object-cover group-hover:scale-105 duration-100 transition-all'
|
||||||
src={img}
|
src={img}
|
||||||
|
priority
|
||||||
alt={title}
|
alt={title}
|
||||||
|
fill='full'
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ export const GameListRelate = ({ posts }) => {
|
|||||||
const GameItem = ({ item }) => {
|
const GameItem = ({ item }) => {
|
||||||
const { title } = item
|
const { title } = item
|
||||||
const [showType, setShowType] = useState('img') // img or video
|
const [showType, setShowType] = useState('img') // img or video
|
||||||
const url = checkContainHttp(item.slug) ? sliceUrlFromHttp(item.slug) : `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
const url = checkContainHttp(item.slug)
|
||||||
|
? sliceUrlFromHttp(item.slug)
|
||||||
|
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||||
|
|
||||||
const img = item?.pageCoverThumbnail
|
const img = item?.pageCoverThumbnail
|
||||||
const video = item?.ext?.video
|
const video = item?.ext?.video
|
||||||
@@ -60,15 +62,19 @@ const GameItem = ({ item }) => {
|
|||||||
title={title}
|
title={title}
|
||||||
className={`card-single w-24 h-24 relative shadow rounded-md overflow-hidden flex justify-center items-center
|
className={`card-single w-24 h-24 relative shadow rounded-md overflow-hidden flex justify-center items-center
|
||||||
group hover:border-purple-400`}>
|
group hover:border-purple-400`}>
|
||||||
<div className='text-sm text-center absolute bottom-0 invisible group-hover:bottom-2 group-hover:visible transition-all duration-200 text-white z-30'>
|
<div className='text-xs text-center absolute bottom-0 invisible group-hover:bottom-2 group-hover:visible transition-all duration-200 text-white z-30'>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
<div className='h-1/2 w-full absolute left-0 bottom-0 z-20 opacity-0 group-hover:opacity-75 transition-all duration-200'>
|
<div className='h-2/3 w-full absolute left-0 bottom-0 z-20 opacity-0 group-hover:opacity-75 transition-all duration-200'>
|
||||||
<div className='h-full w-full absolute bg-gradient-to-b from-transparent to-black'></div>
|
<div className='h-full w-full absolute bg-gradient-to-b from-transparent to-black'></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showType === 'video' && (
|
{showType === 'video' && (
|
||||||
<video className={`z-10 object-cover w-full h-24 absolute overflow-hidden`} loop='true' autoPlay preload='none'>
|
<video
|
||||||
|
className={`z-10 object-cover w-full h-24 absolute overflow-hidden`}
|
||||||
|
loop='true'
|
||||||
|
autoPlay
|
||||||
|
preload='none'>
|
||||||
<source src={video} type='video/mp4' />
|
<source src={video} type='video/mp4' />
|
||||||
</video>
|
</video>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @next/next/no-img-element */
|
/* eslint-disable @next/next/no-img-element */
|
||||||
import { siteConfig } from '@/lib/config'
|
import { siteConfig } from '@/lib/config'
|
||||||
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
import { checkContainHttp, deepClone, sliceUrlFromHttp } from '@/lib/utils'
|
||||||
import Link from 'next/link'
|
import { useRouter } from 'next/router'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useGameGlobal } from '..'
|
import { useGameGlobal } from '..'
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export const GameListRecent = ({ maxCount = 14 }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='game-list-recent-wrapper w-full max-w-full overflow-x-auto pt-4 px-2'>
|
<div className='game-list-recent-wrapper w-full max-w-full overflow-x-auto pt-4 px-2 md:px-0'>
|
||||||
<div className='game-grid md:flex grid grid-flow-col gap-2'>
|
<div className='game-grid md:flex grid grid-flow-col gap-2'>
|
||||||
{components?.map((ItemComponent, index) => {
|
{components?.map((ItemComponent, index) => {
|
||||||
return ItemComponent
|
return ItemComponent
|
||||||
@@ -49,17 +49,43 @@ export const GameListRecent = ({ maxCount = 14 }) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const GameItem = ({ item }) => {
|
const GameItem = ({ item }) => {
|
||||||
|
const router = useRouter()
|
||||||
|
const { recentGames, setRecentGames } = useGameGlobal()
|
||||||
const { title } = item || {}
|
const { title } = item || {}
|
||||||
const [showType, setShowType] = useState('img') // img or video
|
const [showType, setShowType] = useState('img') // img or video
|
||||||
const url = checkContainHttp(item.slug)
|
const url = checkContainHttp(item.slug)
|
||||||
? sliceUrlFromHttp(item.slug)
|
? sliceUrlFromHttp(item.slug)
|
||||||
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
: `${siteConfig('SUB_PATH', '')}/${item.slug}`
|
||||||
|
|
||||||
|
const [isClockVisible, setClockVisible] = useState(true)
|
||||||
|
const toggleIcons = () => {
|
||||||
|
setClockVisible(!isClockVisible)
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 移除最近
|
||||||
|
*/
|
||||||
|
const removeRecent = () => {
|
||||||
|
const updatedRecentGames = deepClone(recentGames) // 创建一个 recentGames 的副本
|
||||||
|
const indexToRemove = updatedRecentGames.findIndex(
|
||||||
|
game => game?.title === item.title
|
||||||
|
) // 找到要移除的项的索引
|
||||||
|
if (indexToRemove !== -1) {
|
||||||
|
updatedRecentGames.splice(indexToRemove, 1) // 使用 splice 方法删除项
|
||||||
|
setRecentGames(updatedRecentGames) // 更新 recentGames 状态
|
||||||
|
localStorage.setItem('recent_games', JSON.stringify(updatedRecentGames))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleButtonClick = () => {
|
||||||
|
router.push(url) // 如果是 Next.js
|
||||||
|
}
|
||||||
|
|
||||||
const img = item?.pageCoverThumbnail
|
const img = item?.pageCoverThumbnail
|
||||||
const video = item?.ext?.video
|
const video = item?.ext?.video
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<div
|
||||||
href={`${url}`}
|
onClick={handleButtonClick}
|
||||||
onMouseOver={() => {
|
onMouseOver={() => {
|
||||||
setShowType('video')
|
setShowType('video')
|
||||||
}}
|
}}
|
||||||
@@ -67,11 +93,23 @@ const GameItem = ({ item }) => {
|
|||||||
setShowType('img')
|
setShowType('img')
|
||||||
}}
|
}}
|
||||||
title={title}
|
title={title}
|
||||||
className={`card-single h-28 w-28 relative shadow rounded-md overflow-hidden flex justify-center items-center
|
className={`cursor-pointer card-single h-28 w-28 relative shadow rounded-md overflow-hidden flex justify-center items-center
|
||||||
group hover:border-purple-400`}>
|
group hover:border-purple-400`}>
|
||||||
<div className='absolute right-0.5 top-1 z-20'>
|
<button
|
||||||
<i className='fas fa-clock-rotate-left w-6 h-6 flex items-center justify-center shadow rounded-full bg-white text-blue-500 text-sm' />
|
className='absolute right-0.5 top-1 z-20'
|
||||||
</div>
|
onClick={e => {
|
||||||
|
e.stopPropagation() // 阻止事件冒泡,防止触发父级元素的点击事件
|
||||||
|
removeRecent()
|
||||||
|
}}
|
||||||
|
onMouseEnter={toggleIcons}
|
||||||
|
onMouseLeave={toggleIcons}>
|
||||||
|
{isClockVisible ? (
|
||||||
|
<i className='fas fa-clock-rotate-left w-6 h-6 flex items-center justify-center shadow rounded-full bg-white text-blue-500 text-sm'></i>
|
||||||
|
) : (
|
||||||
|
<i className='fas fa-trash-can w-6 h-6 flex items-center justify-center shadow rounded-full bg-white text-red-500 text-sm'></i>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className='absolute text-sm bottom-2 transition-all duration-200 text-white z-30'>
|
<div className='absolute text-sm bottom-2 transition-all duration-200 text-white z-30'>
|
||||||
{title}
|
{title}
|
||||||
</div>
|
</div>
|
||||||
@@ -93,6 +131,6 @@ const GameItem = ({ item }) => {
|
|||||||
src={img}
|
src={img}
|
||||||
alt={title}
|
alt={title}
|
||||||
/>
|
/>
|
||||||
</Link>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,11 +62,6 @@ const LayoutBase = props => {
|
|||||||
const [sideBarVisible, setSideBarVisible] = useState(false)
|
const [sideBarVisible, setSideBarVisible] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setRecentGames(
|
|
||||||
localStorage.getItem('recent_games')
|
|
||||||
? JSON.parse(localStorage.getItem('recent_games'))
|
|
||||||
: []
|
|
||||||
)
|
|
||||||
loadWowJS()
|
loadWowJS()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -110,9 +105,8 @@ const LayoutBase = props => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 右侧 */}
|
{/* 右侧 */}
|
||||||
<main className='flex-grow w-full h-full flex flex-col min-h-screen overflow-x-auto'>
|
<main className='flex-grow w-full h-full flex flex-col min-h-screen overflow-x-auto md:p-2'>
|
||||||
<div className='flex-grow h-full'>{children}</div>
|
<div className='flex-grow h-full'>{children}</div>
|
||||||
|
|
||||||
<Footer />
|
<Footer />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,6 +268,7 @@ const LayoutArchive = props => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const LayoutSlug = props => {
|
const LayoutSlug = props => {
|
||||||
|
const { setRecentGames } = useGameGlobal()
|
||||||
const { post, siteInfo, allNavPages, recommendPosts, lock, validPassword } =
|
const { post, siteInfo, allNavPages, recommendPosts, lock, validPassword } =
|
||||||
props
|
props
|
||||||
|
|
||||||
@@ -298,6 +293,8 @@ const LayoutSlug = props => {
|
|||||||
recentGames.unshift(existingGame)
|
recentGames.unshift(existingGame)
|
||||||
}
|
}
|
||||||
localStorage.setItem('recent_games', JSON.stringify(recentGames))
|
localStorage.setItem('recent_games', JSON.stringify(recentGames))
|
||||||
|
|
||||||
|
setRecentGames(recentGames)
|
||||||
}, [post])
|
}, [post])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -305,14 +302,14 @@ const LayoutSlug = props => {
|
|||||||
{lock && <ArticleLock validPassword={validPassword} />}
|
{lock && <ArticleLock validPassword={validPassword} />}
|
||||||
|
|
||||||
{!lock && (
|
{!lock && (
|
||||||
<div id='article-wrapper' className='md:px-2'>
|
<div id='article-wrapper'>
|
||||||
<div className='game-detail-wrapper w-full grow flex md:px-2'>
|
<div className='game-detail-wrapper w-full grow flex'>
|
||||||
<div className={`w-full py-1 md:py-4 `}>
|
<div className={`w-full md:py-2`}>
|
||||||
{/* 游戏窗口 */}
|
{/* 游戏窗口 */}
|
||||||
<GameEmbed post={post} siteInfo={siteInfo} />
|
<GameEmbed post={post} siteInfo={siteInfo} />
|
||||||
|
|
||||||
{/* 资讯 */}
|
{/* 资讯 */}
|
||||||
<div className='game-info dark:text-white py-4 px-2 md:px-0 mt-14 md:mt-0'>
|
<div className='game-info dark:text-white py-2 px-2 md:px-0 mt-14 md:mt-0'>
|
||||||
{/* 关联游戏 */}
|
{/* 关联游戏 */}
|
||||||
<div className='w-full'>
|
<div className='w-full'>
|
||||||
<GameListRelate posts={relateGames} />
|
<GameListRelate posts={relateGames} />
|
||||||
@@ -320,7 +317,7 @@ const LayoutSlug = props => {
|
|||||||
|
|
||||||
{/* 详情描述 */}
|
{/* 详情描述 */}
|
||||||
{post && (
|
{post && (
|
||||||
<div className='bg-white shadow-md my-2 p-2 rounded-md dark:bg-black'>
|
<div className='bg-white shadow-md my-2 p-4 rounded-md dark:bg-black'>
|
||||||
<PostInfo post={post} />
|
<PostInfo post={post} />
|
||||||
<NotionPage post={post} />
|
<NotionPage post={post} />
|
||||||
{/* 广告嵌入 */}
|
{/* 广告嵌入 */}
|
||||||
|
|||||||
Reference in New Issue
Block a user