UPD
This commit is contained in:
parent
f3c97db5f8
commit
d22ef62afb
|
|
@ -293,6 +293,35 @@ const RightBrand = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
function usePreloadImages(urls: string[]) {
|
||||
useEffect(() => {
|
||||
if (!urls?.length) return;
|
||||
const unique = Array.from(new Set(urls.filter(Boolean)));
|
||||
|
||||
unique.forEach((url) => {
|
||||
if (!url) return;
|
||||
|
||||
// Проверяем, есть ли уже preload в <head>
|
||||
const exists = document.querySelector<HTMLLinkElement>(
|
||||
`link[rel="preload"][as="image"][href="${url}"]`
|
||||
);
|
||||
if (!exists) {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "preload";
|
||||
link.as = "image";
|
||||
link.href = url;
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
// Дополнительно сразу грузим в memory cache
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
});
|
||||
}, [urls]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ===================== Component ===================== */
|
||||
|
||||
export default function KeyBusinessSegments() {
|
||||
|
|
@ -304,20 +333,19 @@ export default function KeyBusinessSegments() {
|
|||
const segments = useMemo(() => mapSegments(data), [data]);
|
||||
|
||||
// --- предзагрузка иконок ---
|
||||
useEffect(() => {
|
||||
if (!segments?.length) return;
|
||||
const urls = segments.flatMap((seg) =>
|
||||
seg.info.flatMap((block) =>
|
||||
block.items
|
||||
.map((item) => item.infoLogo)
|
||||
.filter((u): u is string => Boolean(u))
|
||||
)
|
||||
);
|
||||
urls.forEach((url) => {
|
||||
const img = new Image();
|
||||
img.src = url;
|
||||
});
|
||||
// собираем все картинки для предзагрузки
|
||||
const preloadUrls = useMemo(() => {
|
||||
if (!segments?.length) return [];
|
||||
return segments.flatMap((seg) =>
|
||||
seg.info.flatMap((block) => [
|
||||
block.brandLogo, // ← бренд
|
||||
...block.items.map((item) => item.infoLogo), // ← иконки
|
||||
])
|
||||
).filter((u): u is string => Boolean(u));
|
||||
}, [segments]);
|
||||
|
||||
// предзагрузка всех логотипов
|
||||
usePreloadImages(preloadUrls);
|
||||
// ---------------------------
|
||||
|
||||
|
||||
|
|
@ -328,11 +356,42 @@ export default function KeyBusinessSegments() {
|
|||
const denom = Math.max(1, N - 1);
|
||||
const defaultCenterIndex = useMemo(() => Math.floor(N / 2), [N]);
|
||||
|
||||
const [activeIndex, setActiveIndex] = useState<number>(defaultCenterIndex);
|
||||
const [activeIndex, _setActiveIndex] = useState<number>(defaultCenterIndex);
|
||||
const [hoverIndex, setHoverIndex] = useState<number | null>(null);
|
||||
const [isOverflow, setIsOverflow] = useState(false);
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
const [isLocked, setIsLocked] = useState(false);
|
||||
const lockTimer = useRef<number | null>(null);
|
||||
|
||||
// обёртка для смены индекса
|
||||
const setActiveIndex = (idx: number) => {
|
||||
if (idx === activeIndex) return;
|
||||
_setActiveIndex(idx);
|
||||
setIsLocked(true);
|
||||
if (lockTimer.current) clearTimeout(lockTimer.current);
|
||||
lockTimer.current = window.setTimeout(() => {
|
||||
setIsLocked(false);
|
||||
}, 250); // чуть больше transition
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const el = scrollerRef.current;
|
||||
if (!el) return;
|
||||
let raf = 0;
|
||||
const onScroll = () => {
|
||||
cancelAnimationFrame(raf);
|
||||
raf = requestAnimationFrame(() => {
|
||||
if (isLocked) return; // 🔒 во время анимации не пересчитываем
|
||||
setActiveIndex(indexFromScroll(el.scrollLeft));
|
||||
});
|
||||
};
|
||||
el.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => el.removeEventListener("scroll", onScroll);
|
||||
}, [denom, isLocked]);
|
||||
|
||||
|
||||
/* ===== reserve-height measuring ===== */
|
||||
const gridWrapRef = useRef<HTMLDivElement>(null);
|
||||
const [gridMinHeight, setGridMinHeight] = useState(0);
|
||||
|
|
@ -393,20 +452,6 @@ export default function KeyBusinessSegments() {
|
|||
return () => window.removeEventListener("resize", onResize);
|
||||
}, [defaultCenterIndex, N]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = scrollerRef.current;
|
||||
if (!el) return;
|
||||
let raf = 0;
|
||||
const onScroll = () => {
|
||||
cancelAnimationFrame(raf);
|
||||
raf = requestAnimationFrame(() => {
|
||||
setActiveIndex(indexFromScroll(el.scrollLeft));
|
||||
});
|
||||
};
|
||||
el.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => el.removeEventListener("scroll", onScroll);
|
||||
}, [denom]);
|
||||
|
||||
const realActiveIndex = hoverIndex ?? activeIndex;
|
||||
const activeSegment = segments[Math.min(Math.max(realActiveIndex, 0), N - 1)];
|
||||
|
||||
|
|
@ -471,9 +516,16 @@ export default function KeyBusinessSegments() {
|
|||
{block.items.map((item) => (
|
||||
<BulletItem key={item.id} $span={item.span}>
|
||||
<ItemRow>
|
||||
<CheckDot
|
||||
<img
|
||||
src={item.infoLogo}
|
||||
alt=""
|
||||
style={{
|
||||
backgroundImage: item.infoLogo ? `url(${item.infoLogo})` : undefined,
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 4,
|
||||
objectFit: "contain",
|
||||
background: "#16a34a",
|
||||
padding: 4,
|
||||
}}
|
||||
/>
|
||||
<Text18_400_dom
|
||||
|
|
|
|||
|
|
@ -405,7 +405,7 @@ const Row = styled.div<{ $gap: number; $hidden?: boolean }>`
|
|||
display: flex;
|
||||
gap: ${(p) => p.$gap}px;
|
||||
visibility: ${(p) => (p.$hidden ? "hidden" : "visible")};
|
||||
padding: 0px 10px;
|
||||
margin-left:10px;
|
||||
`;
|
||||
|
||||
const Frame = styled.div`
|
||||
|
|
|
|||
Loading…
Reference in New Issue