/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ import React, { useState, useRef, useImperativeHandle, forwardRef, type Ref } from "react"; import { createPortal } from 'react-dom'; import { clsn } from '~/utils'; const parseMeta = (metaStr: string) => { try { const meta = JSON.parse(metaStr as string); if (!meta) { return undefined; } if ("FNumber" in meta) { const parts = (meta.FNumber as string).split("/"); // 分割字符串 const numerator = parseFloat(parts[0]); // 获取分子 const denominator = parseFloat(parts[1]); // 获取分母 meta.FNumber = numerator / denominator; // 计算结果 } return meta as Record; } catch (e) { return undefined; } }; export interface LightBoxProps { className?: string; list: Array; } export interface LightBoxInst { open: (index: number) => void; } function LightBox({ className, list }: LightBoxProps, ref: Ref) { const [state, setState] = useState({ loading: false, visible: false, current: 0, fadeOut: false, }); const selectorRef = useRef(null); let lastScrollTime = 0; useImperativeHandle(ref, () => ({ open: (index: number) => { setState({ ...state, loading: true, visible: true, current: index }); }, })); const isFirstItem = state.current === 0; const isLastItem = state.current === list.length - 1; const onPrev = (ev: React.MouseEvent) => { ev.stopPropagation(); if (isFirstItem) { return; } setState({ ...state, loading: true, current: state.current - 1 }); }; const onNext = (ev: React.MouseEvent) => { ev.stopPropagation(); if (isLastItem) { return; } setState({ ...state, loading: true, current: state.current + 1 }); }; const onClickThumb = (ev: React.MouseEvent, index: number) => { ev.stopPropagation(); setState({ ...state, loading: true, current: index }); selectorRef.current?.children[state.current].scrollIntoView({ block: "nearest", inline: "center", behavior: "smooth", }); }; const onLoad = () => { setState({ ...state, loading: false }); }; const onClose = () => { if (state.fadeOut) { return; } setState({ ...state, fadeOut: true }); setTimeout(() => { setState({ ...state, visible: false, fadeOut: false }); }, 300); }; const onScroll = (ev: React.WheelEvent) => { ev.stopPropagation(); ev.preventDefault(); const now = Date.now(); if (now - lastScrollTime < 500) { return; } lastScrollTime = now; if (ev.deltaY < 0) { onPrev(ev); } else { onNext(ev); } }; if (!state.visible && !state.fadeOut) { return null; } const item = list[state.current]; if (!item) { return null; } const title = item.title; const desc = item.content; const src = item.url; const isVideo = src.includes("mp4"); const meta = parseMeta(item.meta as string); return createPortal(
{isVideo ? (
{list.map((item, index) => ( {item.title} onClickThumb(ev, index)} /> ))}

{title}

{item.take_time}

{desc}

{meta && (
{meta.Model}
{meta.Make}
{meta.Artist}
{meta.FocalLengthIn35mmFilm} mm
F/{meta.FNumber}
{meta.ExposureTime} s
ISO {meta.ISOSpeedRatings}
)}

© 版权归创作者 {item.author || meta?.Artist || "奇趣保罗"}{" "} 所有,未经许可严禁转载和使用。

, document.body ); } export default forwardRef(LightBox); export const useLightBox = () => { const lightBoxRef = useRef(null); return { ref: lightBoxRef, open: (index: number) => { lightBoxRef.current?.open(index); }, }; };