Files
jiang13-bbs/frontend/components/AdaptiveCoverSlot.tsx
freefire 4f3f3a265c feat: 友链/书库/移动端导航/排行榜等多模块功能
新增模块:
- 友链(friendlink)后端处理 + 前端管理页与友链板
- 书库(library):导入、章节、封面、阅读页、横竖版自适应 AdaptiveCoverSlot
- 顶栏导航(header_nav)配置与 MobileTabBar/MobileRailDrawers 移动端抽屉
- 排行榜 service 测试、站点页面测试、时间线发布(timeline_release)

其它改动:
- 后端 handlers/services 全量小幅调整
- 前端组件、库函数、URL/品牌/站点 URL 工具更新
- Lightbox 图片、MdEntries 条目卡、coverColor 派生色相等前端能力
2026-09-30 16:30:07 +08:00

54 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useEffect, useRef, useState, type ReactNode } from "react";
/** 顶部横版槽位的最小门槛:自然宽度低于该值的横版图视为过小,不占顶部布局、归入左栏 */
const MIN_TOP_WIDTH = 600;
/**
* 封面自适应槽位:同一封面在两个位置各放一个槽位(左栏=竖版、顶部全宽=横版),
* 组件用隐藏探测图读取自然宽高,只渲染与自身方向匹配的槽位。
* 竖版槽默认显示(书封多为竖版),探测为横版时两槽自动换位,避免闪空。
* 横版但过小(自然宽度 < MIN_TOP_WIDTH)的图同样只渲染在左栏槽位。
*/
export default function AdaptiveCoverSlot({
src,
orientation,
children,
}: {
src: string;
orientation: "portrait" | "landscape";
children: ReactNode;
}) {
const ref = useRef<HTMLImageElement>(null);
const [match, setMatch] = useState(orientation === "portrait");
const measure = (img: HTMLImageElement) => {
if (!img.naturalWidth || !img.naturalHeight) return;
const landscape = img.naturalWidth > img.naturalHeight;
// 仅“横版且足够大”才配得上顶部槽位;过小的横版图随竖版走左栏
const topWorthy = landscape && img.naturalWidth >= MIN_TOP_WIDTH;
setMatch(orientation === "landscape" ? topWorthy : !topWorthy);
};
useEffect(() => {
// 缓存图可能在 hydration 前就完成加载而错过 onLoad
if (ref.current?.complete) measure(ref.current);
}, []);
return (
<>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
ref={ref}
src={src}
alt=""
aria-hidden
className="hidden"
onLoad={(e) => measure(e.currentTarget)}
/>
{match ? children : null}
</>
);
}