forked from 77media/video-flow
1626 lines
62 KiB
TypeScript
1626 lines
62 KiB
TypeScript
"use client";
|
||
|
||
import { useState, useRef, useEffect, useMemo } from "react";
|
||
import "./style/home-page2.css";
|
||
import { usePathname, useRouter } from "next/navigation";
|
||
import { Swiper, SwiperSlide } from "swiper/react";
|
||
import "swiper/swiper-bundle.css"; // 引入样式
|
||
import "swiper/css/effect-coverflow";
|
||
import "swiper/css/effect-cube";
|
||
import "swiper/css/pagination";
|
||
import {
|
||
Autoplay,
|
||
EffectCoverflow,
|
||
EffectCube,
|
||
Pagination,
|
||
} from "swiper/modules";
|
||
import LazyLoad from "react-lazyload";
|
||
|
||
|
||
import { fetchSubscriptionPlans, SubscriptionPlan } from "@/lib/stripe";
|
||
import VideoCoverflow from "@/components/ui/VideoCoverflow";
|
||
import { fetchTabsByCode, HomeTabItem } from "@/api/serversetting";
|
||
import { useCallbackModal } from "@/app/layout";
|
||
import { useDeviceType } from "@/hooks/useDeviceType";
|
||
|
||
/** 视频预加载系统 - 后台静默运行 */
|
||
function useVideoPreloader() {
|
||
/** 预加载单个视频 */
|
||
const preloadVideo = (src: string): Promise<void> => {
|
||
return new Promise((resolve) => {
|
||
const video = document.createElement("video");
|
||
video.muted = true;
|
||
video.preload = "auto";
|
||
|
||
// 设置超时,避免某个视频卡住整个预加载过程
|
||
const timeout = setTimeout(() => {
|
||
console.warn(`视频预加载超时: ${src}`);
|
||
resolve();
|
||
}, 10000); // 10秒超时
|
||
|
||
video.onloadeddata = () => {
|
||
clearTimeout(timeout);
|
||
resolve();
|
||
};
|
||
|
||
video.onerror = () => {
|
||
clearTimeout(timeout);
|
||
console.warn(`视频预加载失败: ${src}`);
|
||
resolve(); // 即使失败也继续,不影响其他视频
|
||
};
|
||
|
||
video.src = src;
|
||
video.load();
|
||
});
|
||
};
|
||
|
||
/** 预加载所有视频 */
|
||
const preloadAllVideos = async () => {
|
||
const allVideos = [
|
||
// HomeModule1 - 首屏视频(最高优先级)
|
||
"https://cdn.qikongjian.com/videos/home.mp4",
|
||
// HomeModule2 - 核心价值视频(高优先级)
|
||
"https://cdn.qikongjian.com/videos/module2 (1).mp4",
|
||
"https://cdn.qikongjian.com/videos/module2 (2).mp4",
|
||
"https://cdn.qikongjian.com/videos/module2 (3).mp4",
|
||
// HomeModule4 - 制作工序视频(中优先级)
|
||
"https://cdn.qikongjian.com/videos/module4 (3).mp4",
|
||
"https://cdn.qikongjian.com/videos/module4 (1).mp4",
|
||
"https://cdn.qikongjian.com/videos/module4 (4).mp4",
|
||
"https://cdn.qikongjian.com/videos/module4 (2).mp4",
|
||
// HomeModule3 - 案例展示视频(低优先级,数量多)
|
||
"https://cdn.qikongjian.com/videos/show (1).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (2).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (3).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (4).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (5).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (6).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (7).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (8).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (9).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (10).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (11).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (12).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (13).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (14).mp4",
|
||
"https://cdn.qikongjian.com/videos/show (15).mp4",
|
||
];
|
||
|
||
try {
|
||
// 分阶段预加载:先加载关键视频,再加载其他视频
|
||
const criticalVideos = allVideos.slice(0, 8); // 前8个是关键视频
|
||
const otherVideos = allVideos.slice(8);
|
||
|
||
// 第一阶段:预加载关键视频
|
||
for (let i = 0; i < criticalVideos.length; i += 2) {
|
||
const batch = criticalVideos.slice(i, i + 2);
|
||
await Promise.all(batch.map(preloadVideo));
|
||
}
|
||
|
||
// 第二阶段:后台预加载其他视频
|
||
if (otherVideos.length > 0) {
|
||
// 使用 requestIdleCallback 在浏览器空闲时预加载
|
||
const preloadRemaining = () => {
|
||
let index = 0;
|
||
const processBatch = () => {
|
||
if (index >= otherVideos.length) return;
|
||
|
||
const batch = otherVideos.slice(
|
||
index,
|
||
Math.min(index + 3, otherVideos.length)
|
||
);
|
||
batch.forEach(preloadVideo);
|
||
index += batch.length;
|
||
|
||
if (index < otherVideos.length) {
|
||
requestIdleCallback(processBatch, { timeout: 1000 });
|
||
}
|
||
};
|
||
|
||
requestIdleCallback(processBatch, { timeout: 1000 });
|
||
};
|
||
|
||
// 如果浏览器不支持 requestIdleCallback,使用 setTimeout
|
||
if (typeof requestIdleCallback !== "undefined") {
|
||
preloadRemaining();
|
||
} else {
|
||
setTimeout(() => {
|
||
for (let i = 0; i < otherVideos.length; i += 3) {
|
||
const batch = otherVideos.slice(i, i + 3);
|
||
batch.forEach(preloadVideo);
|
||
}
|
||
}, 100);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("视频预加载过程中出现错误:", error);
|
||
}
|
||
};
|
||
|
||
// useEffect(() => {
|
||
// // 使用 requestIdleCallback 在浏览器空闲时开始预加载
|
||
// // 如果浏览器不支持,则使用 setTimeout 延迟执行
|
||
// if (typeof requestIdleCallback !== "undefined") {
|
||
// requestIdleCallback(() => preloadAllVideos(), { timeout: 2000 });
|
||
// } else {
|
||
// setTimeout(() => preloadAllVideos(), 100);
|
||
// }
|
||
// }, []);
|
||
|
||
// 这个 hook 不需要返回任何值,它只是后台静默运行
|
||
return;
|
||
}
|
||
|
||
export function HomePage2() {
|
||
// 后台静默预加载视频,不显示任何加载界面
|
||
useVideoPreloader();
|
||
|
||
/** 导航滚动容器与各锚点 */
|
||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||
const sectionRefs = useRef<Record<string, HTMLDivElement | null>>({
|
||
home: null,
|
||
core: null,
|
||
cases: null,
|
||
showreel: null,
|
||
process: null,
|
||
pricing: null,
|
||
footer: null,
|
||
});
|
||
|
||
const [menuOpen, setMenuOpen] = useState(false);
|
||
const [homeTabsPC, setHomeTabsPC] = useState<HomeTabItem[]>([]);
|
||
const [homeTabsH5, setHomeTabsH5] = useState<HomeTabItem[]>([]);
|
||
const { isMobile } = useDeviceType();
|
||
|
||
// 旧锚点兼容已移除,完全使用接口 homeTab 渲染
|
||
|
||
useEffect(() => {
|
||
let isMounted = true;
|
||
async function loadTabsBoth() {
|
||
try {
|
||
const [pc, h5] = await Promise.all([
|
||
fetchTabsByCode('homeTab'),
|
||
fetchTabsByCode('homeTabH5'),
|
||
]);
|
||
if (!isMounted) return;
|
||
if (Array.isArray(pc)) setHomeTabsPC(pc);
|
||
if (Array.isArray(h5)) setHomeTabsH5(h5);
|
||
} catch {
|
||
// 忽略错误,保持空态
|
||
}
|
||
}
|
||
|
||
loadTabsBoth();
|
||
|
||
return () => {
|
||
isMounted = false;
|
||
};
|
||
}, []);
|
||
|
||
const tabsToRender = isMobile ? homeTabsH5 : homeTabsPC;
|
||
|
||
/** 在容器内平滑滚动到锚点,兼容有固定导航时的微调 */
|
||
const scrollToSection = (key: keyof typeof sectionRefs.current) => {
|
||
try {
|
||
const container = containerRef.current;
|
||
const target = sectionRefs.current[key];
|
||
if (!container || !target) return;
|
||
const containerTop = container.getBoundingClientRect().top;
|
||
const targetTop = target.getBoundingClientRect().top;
|
||
const currentScrollTop = container.scrollTop;
|
||
const NAV_OFFSET = 64; // 顶部导航高度微调
|
||
const delta = targetTop - containerTop + currentScrollTop - NAV_OFFSET;
|
||
container.scrollTo({ top: Math.max(delta, 0), behavior: 'smooth' });
|
||
setMenuOpen(false);
|
||
} catch {
|
||
// 忽略滚动异常
|
||
}
|
||
};
|
||
|
||
const NavBar = () => {
|
||
if (tabsToRender.length === 0) return null;
|
||
useEffect(() => {
|
||
const handler = () => setMenuOpen((v) => !v);
|
||
window.addEventListener('home-menu-toggle' as any, handler as any);
|
||
return () => window.removeEventListener('home-menu-toggle' as any, handler as any);
|
||
}, []);
|
||
return (
|
||
<div data-alt="home-navbar" className="fixed h-16 top-0 left-0 right-0 z-[19]">
|
||
<div className="mx-auto h-full">
|
||
<div className="flex h-full items-center justify-center px-4 sm:px-6 py-3 bg-black/60 backdrop-blur-md border-b border-white/10">
|
||
{/* 桌面端菜单(居中,仅三个项) */}
|
||
<div data-alt="desktop-menu" className="hidden md:flex items-center gap-6 text-white/90 text-sm h-full">
|
||
{tabsToRender.map((tab) => (
|
||
<button
|
||
key={tab.title}
|
||
data-alt={`nav-${tab.title.toLowerCase()}`}
|
||
className="hover:text-white"
|
||
onClick={() => scrollToSection(tab.title.toLowerCase() as any)}
|
||
>
|
||
{tab.title}
|
||
</button>
|
||
))}
|
||
</div>
|
||
{/* 移动端开关移至 TopBar,保留占位对齐 */}
|
||
<span className="md:hidden" data-alt="mobile-menu-toggle-placeholder"></span>
|
||
</div>
|
||
{/* 移动端下拉(仅三个项) */}
|
||
{menuOpen && (
|
||
<div data-alt="mobile-menu" className="md:hidden bg-black/80 backdrop-blur-md border-b border-white/10 px-4 py-2 text-white/90 text-sm">
|
||
<div className="grid grid-cols-1 gap-1">
|
||
{tabsToRender.map((tab) => (
|
||
<button
|
||
key={tab.title}
|
||
data-alt={`m-nav-${tab.title.toLowerCase()}`}
|
||
className="text-center py-2"
|
||
onClick={() => scrollToSection(tab.title.toLowerCase() as any)}
|
||
>
|
||
{tab.title}
|
||
</button>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
return (
|
||
<div className="w-full h-screen overflow-y-auto" id="home-page" ref={containerRef} style={{ paddingBottom: `2rem` }}>
|
||
<NavBar />
|
||
<HomeModule1 />
|
||
<LazyLoad once>
|
||
<HomeModule2 />
|
||
</LazyLoad>
|
||
<LazyLoad once>
|
||
<HomeModule3 />
|
||
</LazyLoad>
|
||
{/* 动态锚点:来源于服务端 homeTab/homeTabH5 配置,title 作为锚点与标题 */}
|
||
{tabsToRender.map((tab) => (
|
||
<div key={tab.title} data-alt={`anchor-${tab.title.toLowerCase()}`} ref={(el) => (sectionRefs.current as any)[tab.title.toLowerCase()] = el}>
|
||
<VideoCoverflow title={tab.title} subtitle={tab.subtitle} videos={tab.videos} />
|
||
</div>
|
||
))}
|
||
<LazyLoad once>
|
||
<HomeModule4 />
|
||
</LazyLoad>
|
||
<HomeModule5 />
|
||
<HomeModule6 />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/** 首屏 */
|
||
function HomeModule1() {
|
||
const router = useRouter();
|
||
return (
|
||
<div
|
||
className="home-module1 relative flex justify-center items-start w-full bg-black snap-start
|
||
/* 移动端适配 */
|
||
h-[100vh] pt-[40vh]
|
||
/* 平板适配 */
|
||
sm:h-[100vh] sm:pt-[35vh]
|
||
/* 小屏笔记本适配 (13-15寸) */
|
||
md:h-[100vh] md:pt-[30vh]
|
||
/* 大屏笔记本适配 (16-17寸) */
|
||
lg:h-[100vh] lg:pt-[28vh]
|
||
/* 桌面端适配 (21-24寸) */
|
||
xl:h-[100vh] xl:pt-[25vh]
|
||
/* 大屏显示器适配 (27寸+) */
|
||
2xl:h-[100vh] 2xl:pt-[22vh]"
|
||
>
|
||
<LazyLoad once>
|
||
<video
|
||
src="https://cdn.qikongjian.com/1756549479451_ltrtoz.mp4"
|
||
poster="https://cdn.qikongjian.com/1756549479451_ltrtoz.mp4?vframe/jpg/offset/1"
|
||
autoPlay
|
||
loop
|
||
muted
|
||
playsInline
|
||
preload="none"
|
||
className="absolute top-0 left-0 z-1 w-full h-full
|
||
/* 移动端优化 */
|
||
object-cover
|
||
/* 平板及以上优化 */
|
||
sm:object-cover
|
||
/* 桌面端优化 */
|
||
md:object-cover lg:object-cover xl:object-cover 2xl:object-cover"
|
||
></video>
|
||
</LazyLoad>
|
||
<div className="center z-10 flex flex-col items-center px-4">
|
||
<h1
|
||
className="text-white font-bold text-center
|
||
/* 移动端字体 */
|
||
text-[2.5rem] leading-[110%] mb-4
|
||
/* 平板字体 */
|
||
sm:text-[3.5rem] sm:leading-[110%] sm:mb-6
|
||
/* 小屏笔记本字体 */
|
||
md:text-[4.5rem] md:leading-[110%] md:mb-8
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[5rem] lg:leading-[110%] lg:mb-8
|
||
/* 桌面端字体 */
|
||
xl:text-[5.5rem] xl:leading-[110%] xl:mb-10
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[6rem] 2xl:leading-[110%] 2xl:mb-12"
|
||
>
|
||
Ideas Spark Movies
|
||
</h1>
|
||
<p
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[1rem] leading-[140%] mb-2
|
||
/* 平板字体 */
|
||
sm:text-[1.25rem] sm:leading-[140%] sm:mb-3
|
||
/* 小屏笔记本字体 */
|
||
md:text-[1.5rem] md:leading-[140%] md:mb-4
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[1.75rem] lg:leading-[140%] lg:mb-4
|
||
/* 桌面端字体 */
|
||
xl:text-[2rem] xl:leading-[140%] xl:mb-5
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[2.25rem] 2xl:leading-[140%] 2xl:mb-6"
|
||
>
|
||
One line, one film—your story, your scene.
|
||
</p>
|
||
<p
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[1rem] leading-[140%] mb-6
|
||
/* 平板字体 */
|
||
sm:text-[1.25rem] sm:leading-[140%] sm:mb-8
|
||
/* 小屏笔记本字体 */
|
||
md:text-[1.5rem] md:leading-[140%] md:mb-10
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[1.75rem] lg:leading-[140%] lg:mb-12
|
||
/* 桌面端字体 */
|
||
xl:text-[2rem] xl:leading-[140%] xl:mb-14
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[2.25rem] 2xl:leading-[140%] 2xl:mb-16"
|
||
>
|
||
Everyone is a movie master.
|
||
</p>
|
||
<div
|
||
className="flex justify-center items-center font-normal border border-white rounded-full bg-white/30 cursor-pointer
|
||
/* 移动端按钮 */
|
||
w-[8rem] h-[2.5rem] text-sm
|
||
/* 平板按钮 */
|
||
sm:w-[9rem] sm:h-[2.75rem] sm:text-base
|
||
/* 小屏笔记本按钮 */
|
||
md:w-[10rem] md:h-[3rem] md:text-base
|
||
/* 大屏笔记本按钮 */
|
||
lg:w-[11rem] lg:h-[3.25rem] lg:text-lg
|
||
/* 桌面端按钮 */
|
||
xl:w-[11.5rem] xl:h-[3.5rem] xl:text-lg
|
||
/* 大屏显示器按钮 */
|
||
2xl:w-[12rem] 2xl:h-[3.75rem] 2xl:text-xl"
|
||
onClick={() => {
|
||
if (localStorage.getItem("token")) {
|
||
router.push("/movies");
|
||
} else {
|
||
router.push("/login");
|
||
}
|
||
}}
|
||
>
|
||
Make a Movie
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
/**核心价值 */
|
||
function HomeModule2() {
|
||
const [isMobile, setIsMobile] = useState(true);
|
||
|
||
// 检测屏幕尺寸并设置状态
|
||
useEffect(() => {
|
||
const checkScreenSize = () => {
|
||
setIsMobile(window.innerWidth < 640);
|
||
};
|
||
|
||
// 初始检查
|
||
checkScreenSize();
|
||
|
||
// 监听窗口大小变化
|
||
}, []);
|
||
|
||
const videoList = [
|
||
{
|
||
title: "Text to Movie",
|
||
video: "https://cdn.qikongjian.com/1756559467841_kn9fr9.mp4",
|
||
},
|
||
{
|
||
title: "Image to Movie",
|
||
video: "https://cdn.qikongjian.com/1756559467840_m4ijd7.mp4",
|
||
},
|
||
{
|
||
title: "Template to Movie",
|
||
video: "https://cdn.qikongjian.com/1756559467836_ij9y54.mp4",
|
||
},
|
||
];
|
||
return (
|
||
<div
|
||
data-alt="core-value-section"
|
||
className="home-module2 relative flex flex-col items-center justify-center w-full bg-black snap-start
|
||
/* 移动端适配 */
|
||
h-[80vh] py-8
|
||
/* 平板适配 */
|
||
sm:h-[100vh] sm:py-12
|
||
/* 小屏笔记本适配 (13-15寸) */
|
||
md:h-[1000px] md:py-16
|
||
/* 大屏笔记本适配 (16-17寸) */
|
||
lg:h-[1000px] lg:py-20
|
||
/* 桌面端适配 (21-24寸) */
|
||
xl:h-[1000px] xl:py-24
|
||
/* 大屏显示器适配 (27寸+) */
|
||
2xl:h-[1000px] 2xl:py-32"
|
||
>
|
||
<div
|
||
data-alt="core-value-content"
|
||
className="center z-10 flex flex-col items-center px-4
|
||
/* 移动端间距 */
|
||
mb-8
|
||
/* 平板间距 */
|
||
sm:mb-8
|
||
/* 小屏笔记本间距 */
|
||
md:mb-10
|
||
/* 大屏笔记本间距 */
|
||
lg:mb-12
|
||
/* 桌面端间距 */
|
||
xl:mb-16
|
||
/* 大屏显示器间距 */
|
||
2xl:mb-20"
|
||
>
|
||
<h2
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[2rem] leading-[110%] mb-4
|
||
/* 平板字体 */
|
||
sm:text-[2.5rem] sm:leading-[110%] sm:mb-6
|
||
/* 小屏笔记本字体 */
|
||
md:text-[3rem] md:leading-[110%] md:mb-8
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[3.25rem] lg:leading-[110%] lg:mb-10
|
||
/* 桌面端字体 */
|
||
xl:text-[3.375rem] xl:leading-[110%] xl:mb-12
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[3.5rem] 2xl:leading-[110%] 2xl:mb-16"
|
||
>
|
||
Just Drop A Thought
|
||
</h2>
|
||
<p
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[1rem] leading-[140%]
|
||
/* 平板字体 */
|
||
sm:text-[1.25rem] sm:leading-[140%]
|
||
/* 小屏笔记本字体 */
|
||
md:text-[1.5rem] md:leading-[140%]
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[1.6rem] lg:leading-[140%]
|
||
/* 桌面端字体 */
|
||
xl:text-[1.7rem] xl:leading-[140%]
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[1.8rem] 2xl:leading-[140%]"
|
||
>
|
||
Say your idea in a single line,and MovieFlow will bring it to life.
|
||
</p>
|
||
</div>
|
||
<div
|
||
data-alt="core-value-videos"
|
||
className="w-full px-4
|
||
/* 移动端容器 */
|
||
h-[20rem]
|
||
/* 平板容器 */
|
||
sm:h-[24rem] sm:px-6
|
||
/* 小屏笔记本容器 */
|
||
md:h-[28rem] md:px-8
|
||
/* 大屏笔记本容器 */
|
||
lg:h-[32rem] lg:px-12
|
||
/* 桌面端容器 */
|
||
xl:h-[36rem] xl:px-16
|
||
/* 大屏显示器容器 */
|
||
2xl:h-[40rem] 2xl:px-20"
|
||
>
|
||
<Swiper
|
||
key={isMobile ? "mobile-swiper" : "desktop-swiper"}
|
||
effect={isMobile ? "cube" : "coverflow"}
|
||
grabCursor={true}
|
||
initialSlide={1}
|
||
cubeEffect={
|
||
isMobile
|
||
? {
|
||
shadow: true,
|
||
slideShadows: true,
|
||
shadowOffset: 20,
|
||
shadowScale: 0.94,
|
||
}
|
||
: undefined
|
||
}
|
||
coverflowEffect={
|
||
!isMobile
|
||
? {
|
||
rotate: 50,
|
||
stretch: 0,
|
||
depth: 100,
|
||
modifier: 1,
|
||
slideShadows: true,
|
||
}
|
||
: undefined
|
||
}
|
||
centeredSlides={!isMobile}
|
||
slidesPerView={isMobile ? 1 : "auto"}
|
||
pagination={
|
||
isMobile
|
||
? true
|
||
: {
|
||
clickable: true,
|
||
dynamicBullets: true,
|
||
}
|
||
}
|
||
modules={
|
||
isMobile ? [EffectCube, Pagination] : [EffectCoverflow, Pagination]
|
||
}
|
||
className="w-full h-full
|
||
/* 分页器样式 - 白色线条 */
|
||
[&_.swiper-pagination-bullet]:!bg-white [&_.swiper-pagination-bullet]:!w-5 [&_.swiper-pagination-bullet]:!h-0.5 [&_.swiper-pagination-bullet]:!rounded-none [&_.swiper-pagination-bullet]:!mx-1
|
||
/* Cube 效果样式 */
|
||
[&_.swiper-wrapper]:!transform-style-preserve-3d [&_.swiper-slide]:!transform-style-preserve-3d"
|
||
onSlideChange={(swiper) => {
|
||
// 只播放当前活跃的视频
|
||
const activeSlide = swiper.slides[swiper.activeIndex];
|
||
const activeVideo = activeSlide?.querySelector("video");
|
||
if (activeVideo) {
|
||
activeVideo.play().catch(() => {
|
||
// 忽略播放错误
|
||
});
|
||
}
|
||
}}
|
||
onSwiper={(swiper) => {
|
||
// 初始化时播放第二个视频(索引为1)
|
||
const secondVideo = swiper.slides[1]?.querySelector("video");
|
||
if (secondVideo) {
|
||
secondVideo.play().catch(() => {
|
||
// 忽略播放错误
|
||
});
|
||
}
|
||
}}
|
||
>
|
||
{videoList.map((item, index) => (
|
||
<SwiperSlide
|
||
key={index}
|
||
className={`flex flex-col items-center justify-center ${
|
||
isMobile
|
||
? "w-full h-full"
|
||
: "w-[320px] h-full md:w-[360px] lg:w-[400px] xl:w-[440px] 2xl:w-[480px]"
|
||
}`}
|
||
>
|
||
<video
|
||
src={item.video}
|
||
poster={`${item.video}?vframe/jpg/offset/1`}
|
||
loop
|
||
muted
|
||
playsInline
|
||
preload="none"
|
||
className={`object-cover border border-white/20 rounded-2xl w-full ${
|
||
isMobile
|
||
? "h-[12rem]"
|
||
: "h-[14rem] md:h-[16rem] lg:h-[18rem] xl:h-[20rem] 2xl:h-[22rem]"
|
||
}`}
|
||
/>
|
||
<h3
|
||
className={`mt-4 text-white font-medium text-center ${
|
||
isMobile
|
||
? "text-[1rem]"
|
||
: "text-[1.125rem] md:text-[1.25rem] lg:text-[1.375rem] xl:text-[1.5rem] 2xl:text-[1.625rem]"
|
||
}`}
|
||
>
|
||
{item.title}
|
||
</h3>
|
||
</SwiperSlide>
|
||
))}
|
||
</Swiper>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
/**案例展示 */
|
||
function HomeModule3() {
|
||
const [isMobile, setIsMobile] = useState(true);
|
||
|
||
// 检测屏幕尺寸并设置状态
|
||
useEffect(() => {
|
||
const checkScreenSize = () => {
|
||
setIsMobile(window.innerWidth < 640);
|
||
};
|
||
|
||
// 初始检查
|
||
checkScreenSize();
|
||
|
||
// // 监听窗口大小变化
|
||
}, []);
|
||
|
||
// PC端二维数组数据
|
||
const pcVideoList = [
|
||
[
|
||
"https://cdn.qikongjian.com/1756474023656_60twk5.mp4",
|
||
"https://cdn.qikongjian.com/1756474023644_14n7is.mp4",
|
||
"https://cdn.qikongjian.com/1756474023648_kocq6z.mp4",
|
||
"https://cdn.qikongjian.com/1756474023657_w10boo.mp4",
|
||
"https://cdn.qikongjian.com/1756474023657_nf8799.mp4",
|
||
"https://cdn.qikongjian.com/1756474230992_vw0ubf.mp4",
|
||
],
|
||
[
|
||
"https://cdn.qikongjian.com/1756474023655_pov4c3.mp4",
|
||
"https://cdn.qikongjian.com/1756474023663_yohi7a.mp4",
|
||
"https://cdn.qikongjian.com/1756474023661_348dx3.mp4",
|
||
"https://cdn.qikongjian.com/1756474023683_xlb34s.mp4",
|
||
"https://cdn.qikongjian.com/1756474230987_63ooji.mp4",
|
||
],
|
||
[
|
||
"https://cdn.qikongjian.com/1756474230997_zysje8.mp4",
|
||
"https://cdn.qikongjian.com/1756474230988_tgqzln.mp4",
|
||
"https://cdn.qikongjian.com/1756474231007_qneeia.mp4",
|
||
"https://cdn.qikongjian.com/1756474231008_qyqtka.mp4",
|
||
"https://cdn.qikongjian.com/1756474231009_vs49d9.mp4",
|
||
"https://cdn.qikongjian.com/1756474231010_2a48p0.mp4",
|
||
],
|
||
];
|
||
|
||
// 移动端一维数组数据(合并所有视频)
|
||
const mobileVideoList = [
|
||
"https://cdn.qikongjian.com/1756474023656_60twk5.mp4",
|
||
"https://cdn.qikongjian.com/1756474023644_14n7is.mp4",
|
||
"https://cdn.qikongjian.com/1756474023648_kocq6z.mp4",
|
||
"https://cdn.qikongjian.com/1756474023657_w10boo.mp4",
|
||
"https://cdn.qikongjian.com/1756474023657_nf8799.mp4",
|
||
"https://cdn.qikongjian.com/1756474230992_vw0ubf.mp4",
|
||
"https://cdn.qikongjian.com/1756474023655_pov4c3.mp4",
|
||
"https://cdn.qikongjian.com/1756474023663_yohi7a.mp4",
|
||
"https://cdn.qikongjian.com/1756474023661_348dx3.mp4",
|
||
"https://cdn.qikongjian.com/1756474023683_xlb34s.mp4",
|
||
"https://cdn.qikongjian.com/1756474230987_63ooji.mp4",
|
||
"https://cdn.qikongjian.com/1756474230997_zysje8.mp4",
|
||
"https://cdn.qikongjian.com/1756474230988_tgqzln.mp4",
|
||
"https://cdn.qikongjian.com/1756474231007_qneeia.mp4",
|
||
"https://cdn.qikongjian.com/1756474231008_qyqtka.mp4",
|
||
"https://cdn.qikongjian.com/1756474231009_vs49d9.mp4",
|
||
"https://cdn.qikongjian.com/1756474231010_2a48p0.mp4",
|
||
];
|
||
|
||
return (
|
||
<div
|
||
className="home-module3 relative flex flex-col items-center justify-center w-full bg-black snap-start
|
||
/* 移动端适配 */
|
||
h-[80vh] py-8
|
||
/* 平板适配 */
|
||
sm:h-[100vh] sm:py-12
|
||
/* 小屏笔记本适配 (13-15寸) */
|
||
md:h-[1300px] md:py-16
|
||
/* 大屏笔记本适配 (16-17寸) */
|
||
lg:h-[1300px] lg:py-20
|
||
/* 桌面端适配 (21-24寸) */
|
||
xl:h-[1300px] xl:py-24
|
||
/* 大屏显示器适配 (27寸+) */
|
||
2xl:h-[1300px] 2xl:py-32"
|
||
>
|
||
<div
|
||
className="center z-10 flex flex-col items-center px-4
|
||
/* 移动端间距 */
|
||
-mb-8
|
||
/* 平板间距 */
|
||
sm:mb-12
|
||
/* 小屏笔记本间距 */
|
||
md:mb-16
|
||
/* 大屏笔记本间距 */
|
||
lg:mb-20
|
||
/* 桌面端间距 */
|
||
xl:mb-24
|
||
/* 大屏显示器间距 */
|
||
2xl:mb-32"
|
||
>
|
||
<h2
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[2rem] leading-[110%] mb-4
|
||
/* 平板字体 */
|
||
sm:text-[2.5rem] sm:leading-[110%] sm:mb-6
|
||
/* 小屏笔记本字体 */
|
||
md:text-[3rem] md:leading-[110%] md:mb-8
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[3.25rem] lg:leading-[110%] lg:mb-10
|
||
/* 桌面端字体 */
|
||
xl:text-[3.375rem] xl:leading-[110%] xl:mb-12
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[3.5rem] 2xl:leading-[110%] 2xl:mb-16"
|
||
>
|
||
Ideas Made Real
|
||
</h2>
|
||
<p
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[1rem] leading-[140%]
|
||
/* 平板字体 */
|
||
sm:text-[1.25rem] sm:leading-[140%]
|
||
/* 小屏笔记本字体 */
|
||
md:text-[1.5rem] md:leading-[140%]
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[1.6rem] lg:leading-[140%]
|
||
/* 桌面端字体 */
|
||
xl:text-[1.7rem] xl:leading-[140%]
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[1.8rem] 2xl:leading-[140%]"
|
||
>
|
||
High-quality films, any style, made with MovieFlow.
|
||
</p>
|
||
</div>
|
||
|
||
{/* 移动端 - 单列布局 */}
|
||
{isMobile ? (
|
||
<div
|
||
data-alt="mobile-single-column"
|
||
className="w-full px-4 h-[40rem] relative"
|
||
>
|
||
{/* 上方阴影遮罩 */}
|
||
<div
|
||
className="absolute -top-[1rem] -left-0 w-full h-[8rem] z-20 pointer-events-none"
|
||
style={{
|
||
backdropFilter: "blur(12px)",
|
||
WebkitBackdropFilter: "blur(12px)",
|
||
backgroundColor: "rgba(0,0,0,0.9)",
|
||
mask: "linear-gradient(to bottom, black 0%, black 30%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
WebkitMask:
|
||
"linear-gradient(to bottom, black 0%, black 30%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
}}
|
||
></div>
|
||
|
||
{/* 下方阴影遮罩 */}
|
||
<div
|
||
className="absolute -bottom-[1rem] -left-0 w-full h-[8rem] z-20 pointer-events-none"
|
||
style={{
|
||
backdropFilter: "blur(12px)",
|
||
WebkitBackdropFilter: "blur(12px)",
|
||
backgroundColor: "rgba(0,0,0,0.9)",
|
||
mask: "linear-gradient(to top, black 0%, black 30%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
WebkitMask:
|
||
"linear-gradient(to top, black 0%, black 20%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
}}
|
||
></div>
|
||
|
||
<Swiper
|
||
modules={[Autoplay]}
|
||
direction="vertical"
|
||
loop={true}
|
||
spaceBetween={0}
|
||
slidesPerView={3}
|
||
autoplay={{
|
||
delay: 0,
|
||
pauseOnMouseEnter: true,
|
||
disableOnInteraction: false,
|
||
}}
|
||
speed={6000}
|
||
grabCursor={true}
|
||
className="w-full h-full"
|
||
cssMode={false}
|
||
freeMode={true}
|
||
watchSlidesProgress={false}
|
||
style={
|
||
{
|
||
"--swiper-wrapper-transition-timing-function": "linear",
|
||
} as React.CSSProperties
|
||
}
|
||
>
|
||
{mobileVideoList.map((video, videoIndex) => (
|
||
<SwiperSlide key={videoIndex} className="w-full !h-[12rem]">
|
||
<div className="w-full h-[11rem] bg-gray-800 rounded-2xl overflow-hidden">
|
||
<video
|
||
src={video}
|
||
poster={`${video}?vframe/jpg/offset/1`}
|
||
autoPlay
|
||
loop
|
||
muted
|
||
playsInline
|
||
preload="none"
|
||
className="w-full h-full object-cover"
|
||
onMouseEnter={(e) => {
|
||
const videoElement = e.currentTarget;
|
||
videoElement.play();
|
||
}}
|
||
/>
|
||
</div>
|
||
</SwiperSlide>
|
||
))}
|
||
</Swiper>
|
||
</div>
|
||
) : (
|
||
/* PC端 - 三列网格布局 */
|
||
<div
|
||
data-alt="pc-grid-layout"
|
||
className="grid grid-cols-3 gap-4 w-full h-[64rem] px-8 relative
|
||
/* 平板及以上 - 有最大宽度限制 */
|
||
sm:max-w-[1200px] sm:mx-auto sm:gap-6
|
||
/* 小屏笔记本 */
|
||
md:max-w-[1400px] md:gap-8
|
||
/* 大屏笔记本 */
|
||
lg:max-w-[1600px] lg:gap-8
|
||
/* 桌面端 */
|
||
xl:max-w-[1800px] xl:gap-8
|
||
/* 大屏显示器 */
|
||
2xl:max-w-[2000px] 2xl:gap-8 "
|
||
>
|
||
{/* 上方阴影遮罩 */}
|
||
<div
|
||
className="absolute -top-[1rem] -left-0 w-full h-[20rem] z-20 pointer-events-none"
|
||
style={{
|
||
backdropFilter: "blur(12px)",
|
||
WebkitBackdropFilter: "blur(12px)",
|
||
backgroundColor: "rgba(0,0,0,0.9)",
|
||
mask: "linear-gradient(to bottom, black 0%, black 30%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
WebkitMask:
|
||
"linear-gradient(to bottom, black 0%, black 30%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
}}
|
||
></div>
|
||
|
||
{/* 下方阴影遮罩 */}
|
||
<div
|
||
className="absolute -bottom-[1rem] -left-0 w-full h-[20rem] z-20 pointer-events-none"
|
||
style={{
|
||
backdropFilter: "blur(12px)",
|
||
WebkitBackdropFilter: "blur(12px)",
|
||
backgroundColor: "rgba(0,0,0,0.9)",
|
||
mask: "linear-gradient(to top, black 0%, black 30%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
WebkitMask:
|
||
"linear-gradient(to top, black 0%, black 20%, rgba(0,0,0,0.9) 50%, rgba(0,0,0,0.6) 75%, transparent 100%)",
|
||
}}
|
||
></div>
|
||
|
||
{pcVideoList.map((column, columnIndex) => (
|
||
<div key={columnIndex} className="w-full h-[64rem] relative z-10">
|
||
<Swiper
|
||
modules={[Autoplay]}
|
||
direction="vertical"
|
||
loop={true}
|
||
spaceBetween={0}
|
||
slidesPerView={3}
|
||
autoplay={{
|
||
delay: 0,
|
||
pauseOnMouseEnter: true,
|
||
disableOnInteraction: false,
|
||
reverseDirection: columnIndex % 2 === 0,
|
||
}}
|
||
speed={6000}
|
||
grabCursor={true}
|
||
className="w-full h-full"
|
||
cssMode={false}
|
||
freeMode={true}
|
||
watchSlidesProgress={false}
|
||
style={
|
||
{
|
||
"--swiper-wrapper-transition-timing-function": "linear",
|
||
} as React.CSSProperties
|
||
}
|
||
>
|
||
{column.map((video, videoIndex) => (
|
||
<SwiperSlide key={videoIndex} className="w-full !h-[20rem]">
|
||
<div className="w-full h-[19rem] bg-gray-800 rounded-2xl overflow-hidden aspect-[1/0.77]">
|
||
<video
|
||
src={video}
|
||
poster={`${video}?vframe/jpg/offset/1`}
|
||
loop
|
||
muted
|
||
playsInline
|
||
preload="none"
|
||
className="w-full h-full object-cover aspect-[1/0.77]"
|
||
onMouseEnter={(e) => {
|
||
const videoElement = e.currentTarget;
|
||
videoElement.play();
|
||
}}
|
||
/>
|
||
</div>
|
||
</SwiperSlide>
|
||
))}
|
||
</Swiper>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
/**电影制作工序介绍 */
|
||
function HomeModule4() {
|
||
const [activeTab, setActiveTab] = useState(0);
|
||
const [isMobile, setIsMobile] = useState(true);
|
||
|
||
// 检测屏幕尺寸并设置状态
|
||
useEffect(() => {
|
||
const checkScreenSize = () => {
|
||
setIsMobile(window.innerWidth < 768);
|
||
};
|
||
|
||
// 初始检查
|
||
checkScreenSize();
|
||
}, []);
|
||
|
||
const processSteps = [
|
||
{
|
||
title: " The Story Agent",
|
||
description:
|
||
" From a single thought, it builds entire worlds and compelling plots.",
|
||
video: "https://cdn.qikongjian.com/videos/module4 (3).mp4",
|
||
},
|
||
{
|
||
title: " AI Character Agent",
|
||
description:
|
||
"Cast your virtual actors. Lock them in once, for the entire story.",
|
||
video: "https://cdn.qikongjian.com/videos/module4 (1).mp4",
|
||
},
|
||
{
|
||
title: " The Shot Agent",
|
||
description:
|
||
"It translates your aesthetic into art, light, and cinematography for every single shot.",
|
||
video: "https://cdn.qikongjian.com/videos/module4 (4).mp4",
|
||
},
|
||
{
|
||
title: " Intelligent Clip Agent",
|
||
description:
|
||
"An editing AI drives the final cut, for a story told seamlessly.",
|
||
video: "https://cdn.qikongjian.com/videos/module4 (2).mp4",
|
||
},
|
||
];
|
||
|
||
const handleTabClick = (index: number) => {
|
||
setActiveTab(index);
|
||
};
|
||
|
||
return (
|
||
<div
|
||
data-alt="core-value-section"
|
||
className="home-module4 relative flex flex-col items-center justify-center w-full bg-black snap-start
|
||
/* 移动端适配 */
|
||
h-[100vh] py-8 px-2
|
||
/* 平板适配 */
|
||
sm:h-[1000px] sm:py-12 sm:px-6
|
||
/* PC端全部使用视口高度,更好地适应不同屏幕 */
|
||
md:min-h-[1000px] md:py-16 md:px-6
|
||
lg:min-h-[1100px] lg:py-20 lg:px-6
|
||
xl:min-h-[1200px] xl:py-24 xl:px-6
|
||
2xl:min-h-[1300px] 2xl:py-32 2xl:px-6"
|
||
>
|
||
<div
|
||
data-alt="core-value-content"
|
||
className="center z-10 flex flex-col items-center
|
||
/* 移动端间距和高度 */
|
||
px-4 h-[14rem]
|
||
/* 平板间距和高度 */
|
||
sm:px-6 sm:h-[16rem]
|
||
/* 小屏笔记本间距和高度 */
|
||
md:px-8 md:h-[8rem]
|
||
/* 大屏笔记本间距和高度 */
|
||
lg:px-12 lg:h-[12rem]
|
||
/* 桌面端间距和高度 */
|
||
xl:px-16 xl:h-[14rem]
|
||
/* 大屏显示器间距和高度 */
|
||
2xl:px-20 2xl:h-[16rem]"
|
||
>
|
||
<h2
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[1.5rem] leading-[110%]
|
||
/* 平板字体 */
|
||
sm:text-[2rem] sm:leading-[110%]
|
||
/* 小屏笔记本字体 */
|
||
md:text-[2.5rem] md:leading-[110%]
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[3rem] lg:leading-[110%]
|
||
/* 桌面端字体 */
|
||
xl:text-[3.375rem] xl:leading-[110%]
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[3.5rem] 2xl:leading-[110%]"
|
||
>
|
||
Create Your Way
|
||
</h2>
|
||
</div>
|
||
|
||
{isMobile ? (
|
||
/* 移动端 - 垂直布局 */
|
||
<div className="w-full px-4 space-y-6">
|
||
{/* 视频播放区域 */}
|
||
<div className="w-full">
|
||
<div className="w-full h-[12rem] bg-gray-800 rounded-2xl overflow-hidden border border-white/20">
|
||
<video
|
||
key={activeTab}
|
||
src={processSteps[activeTab].video}
|
||
poster={`${processSteps[activeTab].video}?vframe/jpg/offset/1`}
|
||
autoPlay
|
||
loop
|
||
muted
|
||
playsInline
|
||
preload="none"
|
||
className="w-full h-full object-cover"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 切换tabs */}
|
||
<div className="grid grid-cols-2 gap-3">
|
||
{processSteps.map((step, index) => (
|
||
<div
|
||
key={index}
|
||
onClick={() => handleTabClick(index)}
|
||
className={`p-3 rounded-xl cursor-pointer transition-all duration-300 border ${
|
||
activeTab === index
|
||
? "bg-[#262626] border-white/20"
|
||
: "bg-black border-white/10"
|
||
}`}
|
||
>
|
||
<h3
|
||
className={`text-sm font-normal mb-2 ${
|
||
activeTab === index ? "text-white" : "text-white/70"
|
||
}`}
|
||
>
|
||
{step.title}
|
||
</h3>
|
||
<p
|
||
className={`text-xs leading-[140%] ${
|
||
activeTab === index ? "text-white/80" : "text-white/50"
|
||
}`}
|
||
>
|
||
{step.description}
|
||
</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
) : (
|
||
/* PC端 - 水平布局 */
|
||
<div
|
||
className="flex w-full gap-[1rem]
|
||
/* 平板及以上 - 间距和内边距 */
|
||
sm:px-4 sm:gap-2
|
||
/* 小屏笔记本 */
|
||
md:px-[3rem] md:gap-[1.5rem]
|
||
/* 大屏笔记本 */
|
||
lg:px-[4rem] lg:gap-[2rem]
|
||
/* 桌面端 */
|
||
xl:px-[5rem] xl:gap-[2.5rem]
|
||
/* 大屏显示器 */
|
||
2xl:px-[6rem] 2xl:gap-[3rem]
|
||
/* 设置具体高度,覆盖flex-1的自动填充 */
|
||
sm:h-[60%]
|
||
md:h-[65%]
|
||
lg:h-[70%]
|
||
xl:h-[75%]
|
||
2xl:h-[65%]
|
||
/* 超宽屏幕适配 */
|
||
min-[1920px]:h-[70%]
|
||
min-[2560px]:h-[80%]"
|
||
>
|
||
{/* 左侧四个切换tab */}
|
||
<div className="flex flex-col gap-[1rem] h-full justify-center">
|
||
{processSteps.map((step, index) => (
|
||
<div
|
||
key={index}
|
||
onClick={() => handleTabClick(index)}
|
||
className={`rounded-2xl cursor-pointer transition-all flex-1 duration-300 border ${
|
||
activeTab === index
|
||
? "bg-[#262626] border-white/20 hover:border-white/40"
|
||
: "bg-black border-white/10 hover:border-white/40"
|
||
}
|
||
/* 平板及以上 - 使用固定高度,让外层盒子撑起来 */
|
||
sm:w-[12rem]
|
||
/* 小屏笔记本 - 13-15寸适配 */
|
||
md:w-[14rem]
|
||
/* 大屏笔记本 - 16-17寸适配 */
|
||
lg:w-[16rem]
|
||
/* 桌面端 - 21-24寸 */
|
||
xl:w-[22rem]
|
||
/* 大屏显示器 - 27寸+ */
|
||
2xl:w-[32rem]`}
|
||
>
|
||
<div
|
||
className="h-full flex flex-col justify-center
|
||
/* 平板及以上 - 内边距适配 */
|
||
sm:p-[0.75rem]
|
||
/* 小屏笔记本 - 13-15寸 */
|
||
md:p-[0.875rem]
|
||
/* 大屏笔记本 - 16-17寸 */
|
||
lg:p-[1rem]
|
||
/* 桌面端 - 21-24寸 */
|
||
xl:p-[1.25rem]
|
||
/* 大屏显示器 - 27寸+ */
|
||
2xl:p-[1.5rem]"
|
||
>
|
||
<h3
|
||
className={`font-normal ${
|
||
activeTab === index ? "text-white" : "text-white/70"
|
||
}
|
||
/* 平板及以上 - 字体适配 */
|
||
sm:text-[1rem] sm:mb-[0.5rem]
|
||
/* 小屏笔记本 - 13-15寸 */
|
||
md:text-[1.125rem] md:mb-[0.625rem]
|
||
/* 大屏笔记本 - 16-17寸 */
|
||
lg:text-[1.25rem] lg:mb-[0.75rem]
|
||
/* 桌面端 - 21-24寸 */
|
||
xl:text-[1.5rem] xl:mb-[1rem]
|
||
/* 大屏显示器 - 27寸+ */
|
||
2xl:text-[1.75rem] 2xl:mb-[1.25rem]`}
|
||
>
|
||
{step.title}
|
||
</h3>
|
||
<p
|
||
className={`leading-[140%] ${
|
||
activeTab === index ? "text-white" : "text-white/70"
|
||
}
|
||
/* 平板及以上 - 字体适配 */
|
||
sm:text-[0.7rem]
|
||
/* 小屏笔记本 - 13-15寸 */
|
||
md:text-[0.75rem]
|
||
/* 大屏笔记本 - 16-17寸 */
|
||
lg:text-[0.75rem]
|
||
/* 桌面端 - 21-24寸 */
|
||
xl:text-[1rem]
|
||
/* 大屏显示器 - 27寸+ */
|
||
2xl:text-[1rem]`}
|
||
>
|
||
{step.description}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* 右侧视频播放区域 */}
|
||
<div className="flex-1 flex justify-center items-center">
|
||
<div
|
||
className="bg-gray-800 rounded-2xl overflow-hidden border border-white/20
|
||
/* 平板及以上 - 使用100%高度,让外层盒子撑起来 */
|
||
sm:w-[85%] sm:h-full
|
||
/* 小屏笔记本 - 13-15寸适配 */
|
||
md:w-[100%] md:h-full
|
||
/* 大屏笔记本 - 16-17寸适配 */
|
||
lg:w-[100%] lg:h-full
|
||
/* 桌面端 - 21-24寸 */
|
||
xl:w-[100%] xl:h-full
|
||
/* 大屏显示器 - 27寸+ */
|
||
2xl:w-[100%] 2xl:h-full"
|
||
>
|
||
<video
|
||
key={activeTab}
|
||
src={processSteps[activeTab].video}
|
||
poster={`${processSteps[activeTab].video}?vframe/jpg/offset/1`}
|
||
autoPlay
|
||
loop
|
||
muted
|
||
playsInline
|
||
preload="none"
|
||
className="w-full h-full object-cover"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
/**价格方案 */
|
||
function HomeModule5() {
|
||
const [billingType, setBillingType] = useState<"month" | "year">("year");
|
||
const [plans, setPlans] = useState<SubscriptionPlan[]>([]);
|
||
const { setShowCallbackModal } = useCallbackModal();
|
||
const pathname = usePathname();
|
||
// 从后端获取订阅计划数据
|
||
useEffect(() => {
|
||
const loadPlans = async () => {
|
||
try {
|
||
const plansData = await fetchSubscriptionPlans();
|
||
setPlans(plansData);
|
||
} catch (err) {
|
||
console.error("加载订阅计划失败:", err);
|
||
}
|
||
};
|
||
|
||
loadPlans();
|
||
}, []);
|
||
|
||
const pricingPlans = useMemo<
|
||
{
|
||
title: string;
|
||
price: number;
|
||
originalPrice: number;
|
||
monthlyPrice: number;
|
||
discountMsg: string;
|
||
credits: string;
|
||
buttonText: string;
|
||
features: string[];
|
||
issubscribed: boolean;
|
||
}[]
|
||
>(() => {
|
||
return plans.map((plan) => {
|
||
return {
|
||
title: plan.display_name || plan.name,
|
||
price:
|
||
billingType === "month"
|
||
? plan.price_month / 100
|
||
: plan.price_year / 100,
|
||
originalPrice: plan.price_month / 100,
|
||
monthlyPrice: billingType === "month" ? 0 : Math.round(plan.price_year / 12) / 100,
|
||
discountMsg: `Saves $${(plan.price_month * 12 - plan.price_year) / 100} by billing yearly!`,
|
||
credits: plan.description,
|
||
buttonText: plan.is_free ? "Try For Free" : "Subscribe Now",
|
||
issubscribed: plan.is_subscribed,
|
||
features: plan.features || [],
|
||
};
|
||
});
|
||
}, [plans, billingType]);
|
||
|
||
const handleSubscribe = async (planName: string) => {
|
||
localStorage.setItem("callBackUrl", pathname);
|
||
// 改为直接携带参数打开 pay-redirect,由其内部完成创建与跳转
|
||
const url = `/pay-redirect?type=subscription&plan=${encodeURIComponent(planName)}&billing=${encodeURIComponent(billingType)}`;
|
||
const win = window.open(url, "_blank");
|
||
// 通知当前窗口等待支付(显示loading模态框)
|
||
window.postMessage({
|
||
type: 'waiting-payment',
|
||
paymentType: 'subscription',
|
||
}, '*');
|
||
if (!win) {
|
||
throw new Error("Unable to open redirect window, please check popup settings");
|
||
}
|
||
};
|
||
return (
|
||
<div
|
||
data-alt="core-value-section"
|
||
className="home-module5 relative flex flex-col items-center justify-center w-full bg-black snap-start
|
||
/* 移动端适配 */
|
||
min-h-[100vh] py-8
|
||
/* 平板适配 */
|
||
sm:min-h-[100vh] sm:py-12
|
||
/* 小屏笔记本适配 (13-15寸) */
|
||
md:h-[1000px] md:py-16
|
||
/* 大屏笔记本适配 (16-17寸) */
|
||
lg:h-[1100px] lg:py-20
|
||
/* 桌面端适配 (21-24寸) */
|
||
xl:h-[1200px] xl:py-24
|
||
/* 大屏显示器适配 (27寸+) */
|
||
2xl:h-[1500px] 2xl:py-32"
|
||
>
|
||
<div
|
||
data-alt="core-value-content"
|
||
className="center z-10 flex flex-col items-center
|
||
/* 移动端间距 */
|
||
mb-8 px-4
|
||
/* 平板间距 */
|
||
sm:mb-12 sm:px-6
|
||
/* 小屏笔记本间距 */
|
||
md:mb-[4rem] md:px-8
|
||
/* 大屏笔记本间距 */
|
||
lg:mb-[4rem] lg:px-12
|
||
/* 桌面端间距 */
|
||
xl:mb-[4rem] xl:px-16
|
||
/* 大屏显示器间距 */
|
||
2xl:mb-[4rem] 2xl:px-20"
|
||
>
|
||
<h2
|
||
className="text-white font-normal text-center
|
||
/* 移动端字体 */
|
||
text-[1.5rem] leading-[110%] mb-4
|
||
/* 平板字体 */
|
||
sm:text-[2rem] sm:leading-[110%] sm:mb-6
|
||
/* 小屏笔记本字体 */
|
||
md:text-[2.5rem] md:leading-[110%] md:mb-[1.5rem]
|
||
/* 大屏笔记本字体 */
|
||
lg:text-[3rem] lg:leading-[110%] lg:mb-[1.5rem]
|
||
/* 桌面端字体 */
|
||
xl:text-[3.375rem] xl:leading-[110%] xl:mb-[1.5rem]
|
||
/* 大屏显示器字体 */
|
||
2xl:text-[3.5rem] 2xl:leading-[110%] 2xl:mb-[1.5rem]"
|
||
>
|
||
Pick a plan and make it yours
|
||
</h2>
|
||
|
||
{/* 计费切换 */}
|
||
<div
|
||
className="flex bg-black rounded-full border border-white/20
|
||
/* 移动端尺寸 */
|
||
h-[2.5rem] p-[0.0625rem] mt-4
|
||
/* 平板尺寸 */
|
||
sm:h-[3rem] sm:mt-6
|
||
/* 小屏笔记本尺寸 */
|
||
md:h-[3.375rem] md:mt-[1.5rem]
|
||
/* 大屏笔记本尺寸 */
|
||
lg:h-[3.375rem] lg:mt-[1.5rem]
|
||
/* 桌面端尺寸 */
|
||
xl:h-[3.375rem] xl:mt-[1.5rem]
|
||
/* 大屏显示器尺寸 */
|
||
2xl:h-[3.375rem] 2xl:mt-[1.5rem]"
|
||
>
|
||
<button
|
||
onClick={() => setBillingType("month")}
|
||
className={`box-border flex justify-center items-center rounded-full transition-all duration-300 ${
|
||
billingType === "month"
|
||
? "bg-white text-black"
|
||
: "text-white hover:text-gray-300"
|
||
}
|
||
/* 移动端按钮尺寸 */
|
||
w-[4.5rem] text-sm
|
||
/* 平板按钮尺寸 */
|
||
sm:w-[5rem] sm:text-sm
|
||
/* 小屏笔记本按钮尺寸 */
|
||
md:w-[6rem] md:text-base
|
||
/* 大屏笔记本按钮尺寸 */
|
||
lg:w-[6rem] lg:text-base
|
||
/* 桌面端按钮尺寸 */
|
||
xl:w-[6rem] xl:text-base
|
||
/* 大屏显示器按钮尺寸 */
|
||
2xl:w-[6rem] 2xl:text-base`}
|
||
>
|
||
Monthly
|
||
</button>
|
||
<button
|
||
onClick={() => setBillingType("year")}
|
||
className={`box-border flex justify-center items-center rounded-full transition-all duration-300 ${
|
||
billingType === "year"
|
||
? "bg-white text-black"
|
||
: "text-white hover:text-gray-300"
|
||
}
|
||
/* 移动端按钮尺寸 */
|
||
w-[5.5rem] text-sm
|
||
/* 平板按钮尺寸 */
|
||
sm:w-[6rem] sm:text-sm
|
||
/* 小屏笔记本按钮尺寸 */
|
||
md:w-[7.125rem] md:text-base
|
||
/* 大屏笔记本按钮尺寸 */
|
||
lg:w-[7.125rem] lg:text-base
|
||
/* 桌面端按钮尺寸 */
|
||
xl:w-[7.125rem] xl:text-base
|
||
/* 大屏显示器按钮尺寸 */
|
||
2xl:w-[7.125rem] 2xl:text-base`}
|
||
>
|
||
Yearly
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 主要价格卡片 */}
|
||
<div
|
||
className="w-full max-w-[88%] mx-auto px-4
|
||
/* 移动端 - 单列布局 */
|
||
grid grid-cols-1 gap-2
|
||
/* 平板 - 双列布局 */
|
||
sm:grid-cols-2 sm:gap-6 sm:px-6
|
||
/* 桌面端 - 三列布局 */
|
||
md:grid-cols-3 md:gap-8 md:px-8
|
||
/* 大屏 - 保持三列但增加间距 */
|
||
lg:gap-10 lg:px-12
|
||
xl:gap-12 xl:px-16
|
||
2xl:gap-16 2xl:px-20"
|
||
>
|
||
{pricingPlans.map((plan, index) => (
|
||
<div
|
||
key={index}
|
||
className="bg-black rounded-2xl border border-white/20
|
||
/* 移动端卡片尺寸 */
|
||
p-4 min-h-[28rem]
|
||
/* 平板卡片尺寸 */
|
||
sm:p-5 sm:min-h-[32rem]
|
||
/* 小屏笔记本卡片尺寸 */
|
||
md:p-6 md:min-h-[36rem]
|
||
/* 大屏笔记本卡片尺寸 */
|
||
lg:p-[1.375rem] lg:min-h-[37rem]
|
||
/* 桌面端卡片尺寸 */
|
||
xl:p-[1.5rem] xl:min-h-[38.125rem]
|
||
/* 大屏显示器卡片尺寸 */
|
||
2xl:p-[1.75rem] 2xl:min-h-[40rem]"
|
||
>
|
||
<h3
|
||
className="text-white font-normal
|
||
/* 移动端标题 */
|
||
text-lg mb-3
|
||
/* 平板标题 */
|
||
sm:text-xl sm:mb-4
|
||
/* 小屏笔记本标题 */
|
||
md:text-xl md:mb-4
|
||
/* 大屏笔记本标题 */
|
||
lg:text-2xl lg:mb-[1rem]
|
||
/* 桌面端标题 */
|
||
xl:text-2xl xl:mb-[1rem]
|
||
/* 大屏显示器标题 */
|
||
2xl:text-3xl 2xl:mb-[1.25rem]"
|
||
>
|
||
{plan.title}
|
||
</h3>
|
||
<div
|
||
className="mb-3
|
||
/* 平板间距 */
|
||
sm:mb-4
|
||
/* 小屏笔记本间距 */
|
||
md:mb-4
|
||
/* 大屏笔记本间距 */
|
||
lg:mb-[1rem]
|
||
/* 桌面端间距 */
|
||
xl:mb-[1rem]
|
||
/* 大屏显示器间距 */
|
||
2xl:mb-[1.25rem]"
|
||
>
|
||
<div className="flex items-baseline">
|
||
<span
|
||
className="text-white font-bold
|
||
/* 移动端价格字体 */
|
||
text-2xl
|
||
/* 平板价格字体 */
|
||
sm:text-3xl
|
||
/* 小屏笔记本价格字体 */
|
||
md:text-[2.5rem]
|
||
/* 大屏笔记本价格字体 */
|
||
lg:text-[3rem]
|
||
/* 桌面端价格字体 */
|
||
xl:text-[3.375rem]
|
||
/* 大屏显示器价格字体 */
|
||
2xl:text-[3.75rem]"
|
||
>
|
||
${plan.monthlyPrice || plan.price}
|
||
</span>
|
||
<span
|
||
className="text-white ml-2 whitespace-nowrap
|
||
/* 移动端单位字体 */
|
||
text-xs
|
||
/* 平板单位字体 */
|
||
sm:text-xs
|
||
/* 小屏笔记本单位字体 */
|
||
md:text-xs
|
||
/* 大屏笔记本单位字体 */
|
||
lg:text-xs
|
||
/* 桌面端单位字体 */
|
||
xl:text-xs
|
||
/* 大屏显示器单位字体 */
|
||
2xl:text-sm"
|
||
>
|
||
/ month
|
||
</span>
|
||
</div>
|
||
{plan.originalPrice !== plan.price ? (
|
||
<div className="pt-2 text-white text-sm line-through">
|
||
${plan.originalPrice}
|
||
</div>
|
||
) : null}
|
||
</div>
|
||
<p
|
||
className="text-white mb-4
|
||
/* 移动端描述字体 */
|
||
text-sm
|
||
/* 平板描述字体 */
|
||
sm:text-sm
|
||
/* 小屏笔记本描述字体 */
|
||
md:text-[0.875rem]
|
||
/* 大屏笔记本描述字体 */
|
||
lg:text-[0.875rem]
|
||
/* 桌面端描述字体 */
|
||
xl:text-[0.875rem]
|
||
/* 大屏显示器描述字体 */
|
||
2xl:text-base"
|
||
>
|
||
{plan.credits}
|
||
</p>
|
||
{plan.issubscribed ? (
|
||
<button
|
||
disabled
|
||
className="w-full bg-gray-400 text-gray-600 rounded-full cursor-not-allowed border border-gray-300
|
||
/* 移动端按钮 */
|
||
py-2 mb-4 text-sm
|
||
/* 平板按钮 */
|
||
sm:py-3 sm:mb-4 sm:text-base
|
||
/* 小屏笔记本按钮 */
|
||
md:py-[0.75rem] md:mb-[1rem] md:text-base
|
||
/* 大屏笔记本按钮 */
|
||
lg:py-[0.75rem] lg:mb-[1rem] lg:text-base
|
||
/* 桌面端按钮 */
|
||
xl:py-[0.75rem] xl:mb-[1rem] xl:text-base
|
||
/* 大屏显示器按钮 */
|
||
2xl:py-[0.875rem] 2xl:mb-[1.25rem] 2xl:text-lg"
|
||
>
|
||
Already Owned
|
||
</button>
|
||
) : (
|
||
<button
|
||
onClick={() => handleSubscribe(plan.title)}
|
||
className="w-full bg-white text-black rounded-full hover:bg-black hover:text-white transition-colors border border-white/20
|
||
/* 移动端按钮 */
|
||
py-2 mb-4 text-sm
|
||
/* 平板按钮 */
|
||
sm:py-3 sm:mb-4 sm:text-base
|
||
/* 小屏笔记本按钮 */
|
||
md:py-[0.75rem] md:mb-[1rem] md:text-base
|
||
/* 大屏笔记本按钮 */
|
||
lg:py-[0.75rem] lg:mb-[1rem] lg:text-base
|
||
/* 桌面端按钮 */
|
||
xl:py-[0.75rem] xl:mb-[1rem] xl:text-base
|
||
/* 大屏显示器按钮 */
|
||
2xl:py-[0.875rem] 2xl:mb-[1.25rem] 2xl:text-lg"
|
||
>
|
||
{plan.buttonText}
|
||
</button>
|
||
)}
|
||
<p
|
||
className="w-full text-center text-white/60 mb-4
|
||
/* 移动端提示文字 */
|
||
text-xs
|
||
/* 平板提示文字 */
|
||
sm:text-xs
|
||
/* 小屏笔记本提示文字 */
|
||
md:text-[0.75rem] md:mb-[2rem]
|
||
/* 大屏笔记本提示文字 */
|
||
lg:text-[0.75rem] lg:mb-[2rem]
|
||
/* 桌面端提示文字 */
|
||
xl:text-[0.75rem] xl:mb-[2rem]
|
||
/* 大屏显示器提示文字 */
|
||
2xl:text-sm 2xl:mb-[2.5rem]"
|
||
>
|
||
* {plan.discountMsg}
|
||
</p>
|
||
<ul
|
||
className="space-y-2
|
||
/* 平板特性列表间距 */
|
||
sm:space-y-3
|
||
/* 小屏笔记本特性列表间距 */
|
||
md:space-y-[1rem]
|
||
/* 大屏笔记本特性列表间距 */
|
||
lg:space-y-[1rem]
|
||
/* 桌面端特性列表间距 */
|
||
xl:space-y-[1rem]
|
||
/* 大屏显示器特性列表间距 */
|
||
2xl:space-y-[1.25rem]"
|
||
>
|
||
{plan.features.map((feature, featureIndex) => (
|
||
<li
|
||
key={featureIndex}
|
||
className="flex items-center text-white
|
||
/* 移动端特性文字 */
|
||
text-sm
|
||
/* 平板特性文字 */
|
||
sm:text-sm
|
||
/* 小屏笔记本特性文字 */
|
||
md:text-[0.875rem]
|
||
/* 大屏笔记本特性文字 */
|
||
lg:text-[0.875rem]
|
||
/* 桌面端特性文字 */
|
||
xl:text-[0.875rem]
|
||
/* 大屏显示器特性文字 */
|
||
2xl:text-base"
|
||
>
|
||
<span
|
||
className="text-[#C73BFF] mr-2
|
||
/* 移动端勾号间距 */
|
||
text-sm
|
||
/* 平板勾号间距 */
|
||
sm:mr-2 sm:text-base
|
||
/* 小屏笔记本勾号间距 */
|
||
md:mr-[0.5rem] md:text-base
|
||
/* 大屏笔记本勾号间距 */
|
||
lg:mr-[0.5rem] lg:text-base
|
||
/* 桌面端勾号间距 */
|
||
xl:mr-[0.5rem] xl:text-base
|
||
/* 大屏显示器勾号间距 */
|
||
2xl:mr-[0.625rem] 2xl:text-lg"
|
||
>
|
||
✓
|
||
</span>
|
||
{feature}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function HomeModule6() {
|
||
return (
|
||
<div className="home-module6 flex justify-center items-center w-full h-min text-white/50 text-lg bg-black snap-start">
|
||
© 2025 MovieFlow. All rights reserved.
|
||
</div>
|
||
);
|
||
}
|