Skip to content

Commit 58269b9

Browse files
committed
feat: improve hero video playback reliability and update banner copy
1 parent 43649b6 commit 58269b9

4 files changed

Lines changed: 211 additions & 97 deletions

File tree

site/components/home/Banner.tsx

Lines changed: 181 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,191 @@
1+
import { useEffect, useRef } from "react";
12
import logo from "../../pages/public/images/logo.svg";
23
import GitHubButton from "./GitHubButton";
34

45
interface BannerProps {
5-
isMobile?: boolean;
6+
isMobile?: boolean;
67
}
78

89
function Banner({ isMobile }: BannerProps) {
9-
return (
10-
<div className="pushy-hero relative overflow-hidden min-h-[680px] lg:min-h-[760px] flex items-center bg-[#eee7dc]">
11-
<img
12-
src="/hero-loop/pushy-hero-loop-poster.png"
13-
className="pushy-hero__poster absolute inset-0 h-full w-full object-cover"
14-
alt=""
15-
aria-hidden="true"
16-
/>
17-
<video
18-
className="pushy-hero__video absolute inset-0 h-full w-full object-cover"
19-
autoPlay
20-
muted
21-
loop
22-
playsInline
23-
preload="metadata"
24-
poster="/hero-loop/pushy-hero-loop-poster.png"
25-
aria-hidden="true"
26-
>
27-
<source src="/hero-loop/pushy-hero-loop.webm" type="video/webm" />
28-
<source src="/hero-loop/pushy-hero-loop.mp4" type="video/mp4" />
29-
</video>
30-
31-
<div className="absolute inset-0 z-[1] pointer-events-none bg-[linear-gradient(90deg,rgba(249,246,240,0.96)_0%,rgba(248,244,236,0.88)_34%,rgba(248,244,236,0.42)_55%,rgba(25,24,22,0.06)_100%)]" />
32-
<div className="absolute inset-0 z-[2] pointer-events-none bg-[linear-gradient(180deg,rgba(255,255,255,0.24)_0%,rgba(255,255,255,0)_52%,rgba(45,38,30,0.2)_100%)]" />
33-
34-
<div className="max-w-7xl mx-auto px-5 sm:px-6 lg:px-8 relative w-full z-10">
35-
<div className="max-w-2xl text-left pt-32 pb-20 lg:pt-40 lg:pb-28">
36-
<div className="flex justify-start mb-8 animate-fade-in-down">
37-
<img
38-
src={logo}
39-
className="w-20 h-20 sm:w-24 sm:h-24 hover:scale-105 transition-transform duration-500 drop-shadow-[0_10px_28px_rgba(68,131,237,0.24)]"
40-
alt="Pushy Logo"
41-
/>
42-
</div>
43-
44-
<h1 className="text-5xl sm:text-6xl md:text-7xl font-extrabold tracking-tight mb-6 animate-fade-in-up text-[#182235]">
45-
Pushy
46-
</h1>
47-
48-
<p className="text-xl sm:text-2xl md:text-3xl font-semibold text-[#233248] mb-8 tracking-wide animate-fade-in-up animation-delay-100">
49-
让应用体验持续变好,不打断用户
50-
</p>
51-
52-
<div className="max-w-xl mb-12 animate-fade-in-up animation-delay-200">
53-
<p className="text-lg sm:text-xl text-[#4a5768] leading-relaxed font-normal">
54-
<span className="block mb-4 text-base sm:text-lg font-semibold text-[#1f3f6d]">
55-
官方 Skill 已支持 AI 自动完成接入、配置检查与常见问题排查。
56-
</span>
57-
<span className="inline-block mr-5 mt-2 relative font-medium text-[#334256]">高速节点勤分发</span>
58-
<span className="inline-block mr-5 mt-2 relative font-medium text-[#334256]">山河浩广若比邻</span>
59-
<br className="hidden sm:block" />
60-
<span className="inline-block mr-5 mt-3 relative font-medium text-[#334256]">增量算法尽优化</span>
61-
<span className="inline-block mr-5 mt-3 relative font-medium text-[#334256]">字节四两拨千斤</span>
62-
</p>
63-
</div>
64-
65-
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-start gap-5 animate-fade-in-up animation-delay-300">
66-
<a href="/docs/skills" className="w-full sm:w-auto">
67-
<button className="w-full sm:w-auto px-8 py-4 bg-[#286be8] hover:bg-[#1f5ed4] text-white font-bold rounded-2xl text-lg transition-all duration-300 shadow-lg shadow-blue-600/25 hover:shadow-blue-500/35 hover:-translate-y-1">
68-
优先:安装 Skill
69-
</button>
70-
</a>
71-
<a href="/docs/getting-started" className="w-full sm:w-auto">
72-
<button className="w-full sm:w-auto px-8 py-4 border border-[#27384f]/20 bg-white/56 hover:bg-white/74 text-[#233248] font-bold rounded-2xl text-lg transition-all duration-300 hover:-translate-y-1 backdrop-blur-sm">
73-
查看手动接入
74-
</button>
75-
</a>
76-
<div className="scale-125 transform origin-left hover:scale-150 transition-transform duration-300">
77-
<GitHubButton
78-
type="stargazers"
79-
namespace="reactnativecn"
80-
repo="react-native-update"
81-
/>
82-
</div>
83-
</div>
84-
</div>
85-
</div>
86-
</div>
87-
);
10+
const videoRef = useRef<HTMLVideoElement>(null);
11+
12+
useEffect(() => {
13+
const video = videoRef.current;
14+
if (!video) {
15+
return;
16+
}
17+
18+
const reducedMotionQuery = window.matchMedia(
19+
"(prefers-reduced-motion: reduce)",
20+
);
21+
22+
if (reducedMotionQuery.matches) {
23+
video.pause();
24+
video.dataset.playback = "reduced-motion";
25+
return;
26+
}
27+
28+
video.muted = true;
29+
video.defaultMuted = true;
30+
video.loop = true;
31+
video.playsInline = true;
32+
33+
let playRequest: Promise<void> | null = null;
34+
let retryId: number | undefined;
35+
36+
const markPlaying = () => {
37+
video.dataset.playback = "playing";
38+
};
39+
const markPaused = () => {
40+
video.dataset.playback = "paused";
41+
};
42+
const start = () => {
43+
if (reducedMotionQuery.matches) {
44+
video.pause();
45+
video.dataset.playback = "reduced-motion";
46+
return;
47+
}
48+
49+
video.muted = true;
50+
video.defaultMuted = true;
51+
if (!video.paused && !video.ended) {
52+
markPlaying();
53+
return;
54+
}
55+
if (playRequest) {
56+
return;
57+
}
58+
59+
playRequest = video
60+
.play()
61+
.then(markPlaying)
62+
.catch((error: unknown) => {
63+
const name = error instanceof DOMException ? error.name : "blocked";
64+
if (name === "AbortError") {
65+
retryId = window.setTimeout(start, 180);
66+
return;
67+
}
68+
69+
video.dataset.playback = name;
70+
})
71+
.finally(() => {
72+
playRequest = null;
73+
});
74+
};
75+
const handleVisibilityChange = () => {
76+
if (!document.hidden) {
77+
start();
78+
}
79+
};
80+
81+
video.addEventListener("playing", markPlaying);
82+
video.addEventListener("pause", markPaused);
83+
video.addEventListener("loadeddata", start);
84+
video.addEventListener("canplay", start);
85+
document.addEventListener("visibilitychange", handleVisibilityChange);
86+
start();
87+
88+
return () => {
89+
if (retryId) {
90+
window.clearTimeout(retryId);
91+
}
92+
video.removeEventListener("playing", markPlaying);
93+
video.removeEventListener("pause", markPaused);
94+
video.removeEventListener("loadeddata", start);
95+
video.removeEventListener("canplay", start);
96+
document.removeEventListener("visibilitychange", handleVisibilityChange);
97+
};
98+
}, []);
99+
100+
return (
101+
<div className="pushy-hero relative overflow-hidden min-h-[680px] lg:min-h-[760px] flex items-center bg-[#eee7dc]">
102+
<img
103+
src="/hero-loop/pushy-hero-loop-poster.png"
104+
className="pushy-hero__poster absolute inset-0 h-full w-full object-cover"
105+
alt=""
106+
aria-hidden="true"
107+
/>
108+
<video
109+
ref={videoRef}
110+
className="pushy-hero__video absolute inset-0 h-full w-full object-cover"
111+
autoPlay
112+
defaultMuted
113+
muted
114+
loop
115+
playsInline
116+
preload="auto"
117+
poster="/hero-loop/pushy-hero-loop-poster.png"
118+
aria-hidden="true"
119+
>
120+
<source src="/hero-loop/pushy-hero-loop.mp4" type="video/mp4" />
121+
<source src="/hero-loop/pushy-hero-loop.webm" type="video/webm" />
122+
</video>
123+
124+
<div className="absolute inset-0 z-[1] pointer-events-none bg-[linear-gradient(90deg,rgba(249,246,240,0.96)_0%,rgba(248,244,236,0.88)_34%,rgba(248,244,236,0.42)_55%,rgba(25,24,22,0.06)_100%)]" />
125+
<div className="absolute inset-0 z-[2] pointer-events-none bg-[linear-gradient(180deg,rgba(255,255,255,0.24)_0%,rgba(255,255,255,0)_52%,rgba(45,38,30,0.2)_100%)]" />
126+
127+
<div className="max-w-7xl mx-auto px-5 sm:px-6 lg:px-8 relative w-full z-10">
128+
<div className="max-w-2xl text-left pt-32 pb-20 lg:pt-40 lg:pb-28">
129+
<div className="flex justify-start mb-8 animate-fade-in-down">
130+
<img
131+
src={logo}
132+
className="w-20 h-20 sm:w-24 sm:h-24 hover:scale-105 transition-transform duration-500 drop-shadow-[0_10px_28px_rgba(68,131,237,0.24)]"
133+
alt="Pushy Logo"
134+
/>
135+
</div>
136+
137+
<h1 className="text-5xl sm:text-6xl md:text-7xl font-extrabold tracking-tight mb-6 animate-fade-in-up text-[#182235]">
138+
Pushy
139+
</h1>
140+
141+
<p className="text-xl sm:text-2xl md:text-3xl font-semibold text-[#233248] mb-8 tracking-wide animate-fade-in-up animation-delay-100">
142+
让 React Native 应用体验持续变好
143+
</p>
144+
145+
<div className="max-w-xl mb-12 animate-fade-in-up animation-delay-200">
146+
<p className="text-lg sm:text-xl text-[#4a5768] leading-relaxed font-normal">
147+
<span className="block mb-4 text-base sm:text-lg font-semibold text-[#1f3f6d]">
148+
官方 Skill 已支持 AI 自动完成接入、配置检查与常见问题排查。
149+
</span>
150+
<span className="inline-block mr-5 mt-2 relative font-medium text-[#334256]">
151+
专为 React Native 设计
152+
</span>
153+
<span className="inline-block mr-5 mt-2 relative font-medium text-[#334256]">
154+
小更新快速送达
155+
</span>
156+
<br className="hidden sm:block" />
157+
<span className="inline-block mr-5 mt-3 relative font-medium text-[#334256]">
158+
用户无需重新安装
159+
</span>
160+
<span className="inline-block mr-5 mt-3 relative font-medium text-[#334256]">
161+
修复和优化更及时
162+
</span>
163+
</p>
164+
</div>
165+
166+
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-start gap-5 animate-fade-in-up animation-delay-300">
167+
<a href="/docs/skills" className="w-full sm:w-auto">
168+
<button className="w-full sm:w-auto px-8 py-4 bg-[#286be8] hover:bg-[#1f5ed4] text-white font-bold rounded-2xl text-lg transition-all duration-300 shadow-lg shadow-blue-600/25 hover:shadow-blue-500/35 hover:-translate-y-1">
169+
AI 自动接入
170+
</button>
171+
</a>
172+
<a href="/docs/getting-started" className="w-full sm:w-auto">
173+
<button className="w-full sm:w-auto px-8 py-4 border border-[#27384f]/20 bg-white/56 hover:bg-white/74 text-[#233248] font-bold rounded-2xl text-lg transition-all duration-300 hover:-translate-y-1 backdrop-blur-sm">
174+
查看手动接入
175+
</button>
176+
</a>
177+
<div className="scale-125 transform origin-left hover:scale-150 transition-transform duration-300">
178+
<GitHubButton
179+
type="stargazers"
180+
namespace="reactnativecn"
181+
repo="react-native-update"
182+
/>
183+
</div>
184+
</div>
185+
</div>
186+
</div>
187+
</div>
188+
);
88189
}
89190

90191
export default Banner;

site/components/home/Showcase.tsx

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import wyyx from "../../pages/public/images/showcase/wyyx.png";
2-
import lyl from "../../pages/public/images/showcase/lyl.png";
3-
import hzsfdx from "../../pages/public/images/showcase/hzsfdx.jpg";
1+
import wyyx from "../../pages/public/images/showcase/wyyx.svg";
2+
import lyl from "../../pages/public/images/showcase/lyl.svg";
3+
import hzsfdx from "../../pages/public/images/showcase/hzsfdx-cropped.jpg";
44
import pabdc from "../../pages/public/images/showcase/pabdc.png";
55
import rjwl from "../../pages/public/images/showcase/rjwl.svg";
66
import htxx from "../../pages/public/images/showcase/htxx.png";
@@ -12,20 +12,23 @@ import hqsb from "../../pages/public/images/showcase/hqsb.png";
1212
interface ShowcaseLogo {
1313
src: string;
1414
alt: string;
15+
width: number;
16+
height: number;
17+
scale?: number;
1518
}
1619

1720
function Showcase() {
1821
const logos: ShowcaseLogo[] = [
19-
{ src: zglt, alt: "中国联通" },
20-
{ src: wyyx, alt: "网易游戏" },
21-
{ src: hzsfdx, alt: "华中师范大学" },
22-
{ src: lyl, alt: "蓝月亮" },
23-
{ src: opple, alt: "欧普照明" },
24-
{ src: pabdc, alt: "平安不动产" },
25-
{ src: tjgj, alt: "天津公交" },
26-
{ src: rjwl, alt: "锐捷网络" },
27-
{ src: hqsb, alt: "环球时报" },
28-
{ src: htxx, alt: "航天信息" },
22+
{ src: zglt, alt: "中国联通", width: 152, height: 50 },
23+
{ src: wyyx, alt: "网易游戏", width: 154, height: 62 },
24+
{ src: hzsfdx, alt: "华中师范大学", width: 188, height: 54 },
25+
{ src: lyl, alt: "蓝月亮", width: 146, height: 56 },
26+
{ src: opple, alt: "欧普照明", width: 96, height: 90 },
27+
{ src: pabdc, alt: "平安不动产", width: 158, height: 46 },
28+
{ src: tjgj, alt: "天津公交", width: 150, height: 50 },
29+
{ src: rjwl, alt: "锐捷网络", width: 164, height: 42 },
30+
{ src: hqsb, alt: "环球时报", width: 150, height: 48 },
31+
{ src: htxx, alt: "航天信息", width: 150, height: 54 },
2932
];
3033

3134
return (
@@ -37,18 +40,24 @@ function Showcase() {
3740
<div className="mt-4 w-24 h-1 bg-gradient-to-r from-blue-500 to-indigo-500 mx-auto rounded-full"></div>
3841
</div>
3942

40-
<div className="max-w-5xl mx-auto px-8">
41-
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-8 md:gap-10">
43+
<div className="max-w-6xl mx-auto px-8">
44+
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-8 md:gap-9">
4245
{logos.map((logo, index) => (
4346
<div
4447
key={index}
45-
className="group flex items-center justify-center h-16 sm:h-20 grayscale opacity-50 hover:grayscale-0 hover:opacity-100 transition-all duration-300 ease-in-out cursor-pointer hover:scale-110"
48+
className="group flex items-center justify-center h-24 sm:h-28 grayscale opacity-55 hover:grayscale-0 hover:opacity-100 transition-all duration-300 ease-in-out cursor-pointer hover:scale-105"
4649
>
4750
<img
4851
src={logo.src}
4952
alt={logo.alt}
5053
title={logo.alt}
51-
className="max-h-full max-w-full object-contain"
54+
className="object-contain"
55+
style={{
56+
width: logo.width,
57+
height: logo.height,
58+
maxWidth: "100%",
59+
transform: logo.scale ? `scale(${logo.scale})` : undefined,
60+
}}
5261
/>
5362
</div>
5463
))}
58.6 KB
Loading

0 commit comments

Comments
 (0)