mirror of
https://github.com/d0zingcat/NotionNext.git
synced 2026-05-14 07:26:52 +00:00
Plog 图片组件
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { siteConfig } from '@/lib/config'
|
||||
import Head from 'next/head'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
/**
|
||||
* 图片懒加载
|
||||
* @param {*} param0
|
||||
@@ -21,20 +22,21 @@ export default function LazyImage({
|
||||
}) {
|
||||
const maxWidth = siteConfig('IMAGE_COMPRESS_WIDTH')
|
||||
const defaultPlaceholderSrc = siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||
|
||||
const imageRef = useRef(null)
|
||||
const [adjustedSrc, setAdjustedSrc] = useState(
|
||||
placeholderSrc || siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||
const [currentSrc, setCurrentSrc] = useState(
|
||||
placeholderSrc || defaultPlaceholderSrc
|
||||
)
|
||||
|
||||
if (!placeholderSrc) {
|
||||
placeholderSrc = siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片加载成功回调
|
||||
* 占位图加载成功
|
||||
*/
|
||||
const handleImageLoad = () => {
|
||||
const handleThumbnailLoaded = () => {
|
||||
if (typeof onLoad === 'function') {
|
||||
// onLoad() // 触发传递的onLoad回调函数
|
||||
}
|
||||
}
|
||||
// 原图加载完成
|
||||
const handleImageLoaded = img => {
|
||||
if (typeof onLoad === 'function') {
|
||||
onLoad() // 触发传递的onLoad回调函数
|
||||
}
|
||||
@@ -44,13 +46,27 @@ export default function LazyImage({
|
||||
*/
|
||||
const handleImageError = () => {
|
||||
if (imageRef.current) {
|
||||
imageRef.current.src = defaultPlaceholderSrc
|
||||
// 尝试加载 placeholderSrc,如果失败则加载 defaultPlaceholderSrc
|
||||
if (imageRef.current.src !== placeholderSrc && placeholderSrc) {
|
||||
imageRef.current.src = placeholderSrc
|
||||
} else {
|
||||
imageRef.current.src = defaultPlaceholderSrc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const adjustedImageSrc = adjustImgSize(src, maxWidth)
|
||||
setAdjustedSrc(adjustedImageSrc)
|
||||
const adjustedImageSrc =
|
||||
adjustImgSize(src, maxWidth) || defaultPlaceholderSrc
|
||||
|
||||
// 加载原图
|
||||
const img = new Image()
|
||||
img.src = adjustedImageSrc
|
||||
img.onload = () => {
|
||||
setCurrentSrc(adjustedImageSrc)
|
||||
handleImageLoaded(adjustedImageSrc)
|
||||
}
|
||||
img.onerror = handleImageError
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
entries => {
|
||||
@@ -79,9 +95,9 @@ export default function LazyImage({
|
||||
// 动态添加width、height和className属性,仅在它们为有效值时添加
|
||||
const imgProps = {
|
||||
ref: imageRef,
|
||||
src: priority ? adjustedSrc : placeholderSrc,
|
||||
src: currentSrc,
|
||||
alt: alt,
|
||||
onLoad: handleImageLoad,
|
||||
onLoad: handleThumbnailLoaded, // 缩略图加载完成
|
||||
onError: handleImageError // 添加onError处理函数
|
||||
}
|
||||
|
||||
@@ -106,6 +122,7 @@ export default function LazyImage({
|
||||
if (style) {
|
||||
imgProps.style = style
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
@@ -113,24 +130,25 @@ export default function LazyImage({
|
||||
{/* 预加载 */}
|
||||
{priority && (
|
||||
<Head>
|
||||
<link rel='preload' as='image' href={adjustedSrc} />
|
||||
<link rel='preload' as='image' href={adjustImgSize(src, maxWidth)} />
|
||||
</Head>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据窗口尺寸决定压缩图片宽度
|
||||
* @param {*} src
|
||||
* @param {*} maxWidth
|
||||
* @returns
|
||||
*/
|
||||
|
||||
const adjustImgSize = (src, maxWidth) => {
|
||||
if (!src) {
|
||||
return siteConfig('IMG_LAZY_LOAD_PLACEHOLDER')
|
||||
return null
|
||||
}
|
||||
const screenWidth = window.screen.width
|
||||
const screenWidth =
|
||||
(typeof window !== 'undefined' && window?.screen?.width) || maxWidth
|
||||
|
||||
// 屏幕尺寸大于默认图片尺寸,没必要再压缩
|
||||
if (screenWidth > maxWidth) {
|
||||
|
||||
@@ -14,7 +14,9 @@ export default function Modal(props) {
|
||||
usePlogGlobal()
|
||||
const { siteInfo, posts } = props
|
||||
const cancelButtonRef = useRef(null)
|
||||
const img = compressImage(
|
||||
const thumbnail =
|
||||
modalContent?.pageCoverThumbnail || siteInfo?.pageCoverThumbnail
|
||||
const bigImage = compressImage(
|
||||
modalContent?.pageCover || siteInfo?.pageCover,
|
||||
1200,
|
||||
85,
|
||||
@@ -92,63 +94,74 @@ export default function Modal(props) {
|
||||
<Dialog.Panel className='group relative transform overflow-hidden rounded-xl text-left shadow-xl transition-all '>
|
||||
{/* 添加onLoad事件处理函数 */}
|
||||
{/* 添加loading状态 */}
|
||||
{/* <div
|
||||
className={`bg-hexo-black-gray w-32 h-32 flex justify-center items-center `}> */}
|
||||
<div
|
||||
className={`bg-hexo-black-gray w-32 h-32 flex justify-center items-center ${loading ? '' : 'hidden'}`}>
|
||||
<ArrowPath className='w-10 h-10 animate-spin text-gray-200' />
|
||||
className={`absolute right-0 bottom-0 m-4 ${loading ? '' : 'hidden'}`}>
|
||||
<ArrowPath
|
||||
className={`w-10 h-10 animate-spin text-gray-200`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* </div> */}
|
||||
|
||||
<Link href={modalContent?.href}>
|
||||
<LazyImage
|
||||
onLoad={handleImageLoad}
|
||||
src={img}
|
||||
placeholderSrc={thumbnail}
|
||||
src={bigImage}
|
||||
ref={imgRef}
|
||||
style={{ display: loading ? 'none' : 'block' }}
|
||||
className={`w-full select-none max-w-7xl max-h-[90vh] shadow-xl ${!loading ? ' animate__animated animate__fadeIn' : ''}`}
|
||||
className={`w-full select-none max-w-7xl max-h-[90vh] shadow-xl animate__animated animate__fadeIn'`}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{!loading && (
|
||||
<>
|
||||
<div className='absolute bottom-0 left-0 m-4 z-20'>
|
||||
<div className='flex'>
|
||||
<h2
|
||||
style={{ textShadow: '0.1em 0.1em 0.2em black' }}
|
||||
className='text-2xl md:text-5xl text-white mb-4 px-2 py-1 rounded-lg'>
|
||||
{modalContent?.title}
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
<>
|
||||
<div className='absolute bottom-0 left-0 m-4 z-20'>
|
||||
<div className='flex'>
|
||||
<h2
|
||||
style={{ textShadow: '0.1em 0.1em 0.2em black' }}
|
||||
className={
|
||||
'line-clamp-3 md:line-clamp-none overflow-hidden cursor-pointer text-gray-50 rounded-lg m-2'
|
||||
}>
|
||||
{modalContent?.summary}
|
||||
</div>
|
||||
className='text-2xl md:text-5xl text-white mb-4 px-2 py-1 rounded-lg'>
|
||||
{modalContent?.title}
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
style={{ textShadow: '0.1em 0.1em 0.2em black' }}
|
||||
className={
|
||||
'line-clamp-3 md:line-clamp-none overflow-hidden cursor-pointer text-gray-50 rounded-lg m-2'
|
||||
}>
|
||||
{modalContent?.summary}
|
||||
</div>
|
||||
|
||||
{modalContent?.category && (
|
||||
<div className='flex'>
|
||||
<Link
|
||||
href={`/category/${modalContent?.category}`}
|
||||
className='text-xs rounded-lg mt-3 px-2 py-1 bg-black bg-opacity-20 text-white hover:bg-blue-700 hover:text-white duration-200'>
|
||||
{modalContent?.category}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* <div className="z-10 absolute hover:opacity-50 opacity-0 duration-200 transition-opacity w-full top-0 left-0 px-4 h-full items-center flex justify-between"> */}
|
||||
<div
|
||||
onClick={prev}
|
||||
className='z-10 absolute left-0 top-1/2 -mt-12 group-hover:opacity-50 opacity-0 duration-200 transition-opacity'>
|
||||
<ChevronLeft className='cursor-pointer w-24 h-32 hover:opacity-100 stroke-white stroke-1 scale-y-150' />
|
||||
</div>
|
||||
<div
|
||||
onClick={next}
|
||||
className='z-10 absolute right-0 top-1/2 -mt-12 group-hover:opacity-50 opacity-0 duration-200 transition-opacity'>
|
||||
<ChevronRight className='cursor-pointer w-24 h-32 hover:opacity-100 stroke-white stroke-1 scale-y-150' />
|
||||
</div>
|
||||
{/* </div> */}
|
||||
</>
|
||||
)}
|
||||
{modalContent?.category && (
|
||||
<div className='flex'>
|
||||
<Link
|
||||
href={`/category/${modalContent?.category}`}
|
||||
className='text-xs rounded-lg mt-3 px-2 py-1 bg-black bg-opacity-20 text-white hover:bg-blue-700 hover:text-white duration-200'>
|
||||
{modalContent?.category}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 卡片的阴影遮罩,为了凸显图片上的文字 */}
|
||||
<div className='h-1/2 w-full absolute left-0 bottom-0'>
|
||||
<div className='h-full w-full absolute opacity-80 group-hover:opacity-100 transition-all duration-1000 bg-gradient-to-b from-transparent to-black'></div>
|
||||
</div>
|
||||
|
||||
{/* <div className="z-10 absolute hover:opacity-50 opacity-0 duration-200 transition-opacity w-full top-0 left-0 px-4 h-full items-center flex justify-between"> */}
|
||||
|
||||
<div
|
||||
onClick={prev}
|
||||
className='z-10 absolute left-0 top-1/2 -mt-12 group-hover:opacity-50 opacity-0 duration-200 transition-opacity'>
|
||||
<ChevronLeft className='cursor-pointer w-24 h-32 hover:opacity-100 stroke-white stroke-1 scale-y-150' />
|
||||
</div>
|
||||
<div
|
||||
onClick={next}
|
||||
className='z-10 absolute right-0 top-1/2 -mt-12 group-hover:opacity-50 opacity-0 duration-200 transition-opacity'>
|
||||
<ChevronRight className='cursor-pointer w-24 h-32 hover:opacity-100 stroke-white stroke-1 scale-y-150' />
|
||||
</div>
|
||||
{/* </div> */}
|
||||
</>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { useState } from 'react'
|
||||
import { Dialog } from '@headlessui/react'
|
||||
|
||||
/**
|
||||
* 图片弹出模态框
|
||||
*/
|
||||
export default function PlogModal(props) {
|
||||
const [isOpen, setIsOpen] = useState(true)
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onClose={() => setIsOpen(false)}>
|
||||
<Dialog.Panel>
|
||||
<Dialog.Title>Deactivate account</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
This will permanently deactivate your account
|
||||
</Dialog.Description>
|
||||
|
||||
<p>
|
||||
Are you sure you want to deactivate your account? All of your data
|
||||
will be permanently removed. This action cannot be undone.
|
||||
</p>
|
||||
|
||||
<button onClick={() => setIsOpen(false)}>Deactivate</button>
|
||||
<button onClick={() => setIsOpen(false)}>Cancel</button>
|
||||
</Dialog.Panel>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user