diff --git a/assets/css/front-page.css b/assets/css/front-page.css index 865bb52..7c964fb 100644 --- a/assets/css/front-page.css +++ b/assets/css/front-page.css @@ -3,12 +3,116 @@ ========================================= */ /* --- Hero 区域 --- */ +.site-main { + position: relative; +} + .hero-section { - padding: 120px 0 80px; + padding: 40px 0; background-color: var(--bg-body); position: relative; - overflow: hidden; + overflow: visible; background-image: none; + min-height: calc(100vh - var(--header-height)); + display: flex; + align-items: center; + isolation: isolate; + z-index: 1; +} + +.hero-scroll-indicator { + position: absolute; + left: 50%; + bottom: 28px; + transform: translateX(-50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + z-index: 4; + color: var(--text-secondary); + font-family: var(--font-mono); + font-size: 0.9rem; + letter-spacing: 0.12em; + text-transform: uppercase; + opacity: 0; + pointer-events: none; + transition: opacity 0.45s var(--ease-in-out), transform 0.45s var(--ease-in-out); +} + +.hero-scroll-indicator .scroll-arrow { + width: 18px; + height: 18px; + border-right: 2px solid currentColor; + border-bottom: 2px solid currentColor; + transform: rotate(45deg); + animation: hero-scroll-bounce 1.6s ease-in-out infinite; +} + +.hero-scroll-indicator .scroll-text { + font-size: 0.7rem; + letter-spacing: 0.3em; +} + +@keyframes hero-scroll-bounce { + 0%, 100% { transform: translateY(0) rotate(45deg); opacity: 0.5; } + 50% { transform: translateY(6px) rotate(45deg); opacity: 1; } +} + +@media (min-width: 900px) { + .home-hero-initial .site-header { + transform: translateY(-100%); + } + + .home-hero-initial .site-header, + .home-hero-scrolled .site-header { + position: fixed; + left: 0; + right: 0; + width: 100%; + } + + .home-hero-initial .hero-scroll-indicator { + opacity: 1; + transform: translateX(-50%) translateY(0); + } + + .home-hero-scrolled .hero-scroll-indicator { + opacity: 0; + transform: translateX(-50%) translateY(10px); + } +} + +.hero-waves { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 140vh; + opacity: 0.5; + pointer-events: none; + z-index: 2; + mix-blend-mode: screen; + filter: saturate(1.08); + display: block; + -webkit-mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0.35) 75%, rgba(0, 0, 0, 0) 100%); + mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0.35) 75%, rgba(0, 0, 0, 0) 100%); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-size: 100% 100%; + mask-size: 100% 100%; +} + +html:not([data-theme="dark"]) .hero-waves { + opacity: 0.55; + mix-blend-mode: multiply; + filter: saturate(1.2); +} + +[data-theme="dark"] .hero-waves { + opacity: 0.5; + mix-blend-mode: screen; + filter: saturate(1.08); } /* 底部渐变遮罩 */ @@ -22,18 +126,82 @@ text-align: center; position: relative; z-index: 1; + display: flex; + flex-direction: column; + align-items: center; } /* 大标题 */ -.hero-content h1 { - font-size: 4rem; /* 增大 Hero 标题 (原3.5rem) */ - font-weight: 800; - letter-spacing: -0.02em; - margin-bottom: 1.5rem; - background: linear-gradient(135deg, var(--text-primary) 0%, var(--color-primary) 100%); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - line-height: 1.2; +.hero-title { + display: flex; + justify-content: center; + margin: 0 0 1.2rem; + overflow: visible; + --hero-title-gradient: linear-gradient(135deg, var(--text-primary) 0%, var(--color-primary) 100%); + width: 100%; +} + +.hero-title-svg { + display: block; + width: min(88vw, 900px); + margin: 0 auto; + aspect-ratio: 3 / 2; + background-image: var(--hero-title-gradient); + -webkit-mask: url("../../resources/title.svg") center / contain no-repeat; + mask: url("../../resources/title.svg") center / contain no-repeat; + filter: + drop-shadow(0 10px 24px rgba(12, 20, 32, 0.25)) + drop-shadow(0 0 18px rgba(120, 190, 255, 0.28)); + position: relative; + transform: translateY(-8px) scale(1.06); + transform-origin: center; +} + +.hero-title-svg::before { + content: ""; + position: absolute; + inset: -6% -4%; + background: + radial-gradient(circle at 30% 35%, rgba(255, 255, 255, 0.35), transparent 55%), + radial-gradient(circle at 70% 45%, rgba(140, 205, 255, 0.28), transparent 60%), + linear-gradient(180deg, rgba(255, 255, 255, 0.25), rgba(255, 255, 255, 0)); + -webkit-mask: url("../../resources/title.svg") center / contain no-repeat; + mask: url("../../resources/title.svg") center / contain no-repeat; + opacity: 0.22; + filter: blur(6px); + mix-blend-mode: screen; + pointer-events: none; +} + +html:not([data-theme="dark"]) .hero-title-svg::before { + opacity: 0; +} + +.hero-title-svg::after { + content: ""; + position: absolute; + left: 0; + right: 0; + top: 100%; + height: 55%; + background-image: var(--hero-title-gradient); + -webkit-mask: url("../../resources/title.svg") center / contain no-repeat; + mask: url("../../resources/title.svg") center / contain no-repeat; + transform: scaleY(-1); + opacity: 0.18; + filter: blur(2px); +} + +.hero-title-text { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; } /* 描述文本 (代码风格) */ @@ -52,11 +220,18 @@ /* --- 服务展示区 (Services) --- */ .services-provided { - margin-top: 5rem; + margin-top: 0; position: relative; z-index: 2; } +.services-section { + padding: 80px 0; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, var(--bg-body) 55%); + position: relative; + z-index: 3; +} + /* 标题带有命令提示符风格 */ .services-provided h2 { font-size: 1.8rem; /* 增大服务标题 (原1.5rem) */ @@ -85,8 +260,8 @@ } .service-item { - background: var(--bg-card); - border: 1px solid var(--border-default); + background: transparent; + border: none; border-radius: var(--radius-md); padding: 32px 24px; display: flex; @@ -99,8 +274,7 @@ .service-item:hover { transform: translateY(-4px); - border-color: var(--color-primary-light); - box-shadow: var(--shadow-lg); + box-shadow: none; } .service-icon { @@ -129,17 +303,66 @@ } @media (max-width: 768px) { - .hero-content h1 { - font-size: 2.25rem; + .hero-title { + margin-bottom: 1rem; + } + + .hero-title-svg { + width: min(92vw, 560px); + transform: translateY(-6px) scale(1.03); } .hero-description { font-size: 1rem; } + + .services-section { + padding: 28px 0; + } + + .hero-section { + min-height: auto; + padding: 16px 0 16px; + } + + .hero-waves { + display: none; + } + + .hero-scroll-indicator { + display: none; + } } @media (max-width: 600px) { .services-grid-box { - grid-template-columns: 1fr; /* 手机端单列 */ + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + } + + .service-item { + padding: 12px 8px; + border: none; + background: transparent; + box-shadow: none; + } + + .service-item:hover { + transform: none; + border-color: transparent; + box-shadow: none; + } + + .service-icon { + margin-bottom: 0.75rem; + } + + .service-icon svg { + width: 64px; + height: 64px; + } + + .service-item span { + font-size: 1rem; } } diff --git a/assets/css/header.css b/assets/css/header.css index 1fa9794..b1f2f45 100644 --- a/assets/css/header.css +++ b/assets/css/header.css @@ -13,7 +13,8 @@ -webkit-backdrop-filter: blur(12px); /* 使用半透明背景以增强毛玻璃效果 */ background-color: rgba(255, 255, 255, 0.85); - transition: background-color var(--duration-normal), border-color var(--duration-normal); + transition: background-color var(--duration-normal), border-color var(--duration-normal), transform 0.45s var(--ease-in-out); + will-change: transform; flex-shrink: 0; /* 防止被父容器 Flex 布局压缩 */ } diff --git a/assets/css/intro.css b/assets/css/intro.css index fa3f2d0..f1f09ae 100644 --- a/assets/css/intro.css +++ b/assets/css/intro.css @@ -292,6 +292,11 @@ html:not([data-theme="dark"]) .intro-about .site-header { grid-template-columns: repeat(3, 1fr); } +/* Stream text characters */ +.stream-char { + transition: opacity 0.4s ease; +} + /* Animations */ .intro-animate { transition: opacity 0.8s var(--ease-in-out), transform 0.8s var(--ease-in-out); diff --git a/assets/js/hero-waves.js b/assets/js/hero-waves.js new file mode 100644 index 0000000..83e37af --- /dev/null +++ b/assets/js/hero-waves.js @@ -0,0 +1,140 @@ +(() => { + const main = document.querySelector('.site-main'); + const heroSection = document.querySelector('.hero-section'); + const servicesSection = document.querySelector('.services-section'); + const canvas = document.querySelector('.hero-waves'); + if (!main || !canvas) return; + + const ctx = canvas.getContext('2d'); + if (!ctx) return; + + const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)'); + let width = 0; + let height = 0; + let dpr = 1; + let rafId = null; + + const waveDefs = [ + { yRatio: 0.3, amplitude: 22, wavelength: 940, speed: 0.5, alpha: 0.98 }, + { yRatio: 0.45, amplitude: 16, wavelength: 1020, speed: 0.58, alpha: 0.92 }, + { yRatio: 0.65, amplitude: 14, wavelength: 1160, speed: 0.6, alpha: 0.9 }, + ]; + + const palettes = { + light: { + gradient: ['#e8f3ff', '#cfe6fb', '#558ec1', '#2476af', '#2b6fb3'], + waves: ['rgba(170, 215, 245, 0.91)', 'rgba(120, 185, 230, 0.93)', 'rgba(80, 155, 210, 0.95)'], + }, + dark: { + gradient: ['#0d1117', '#111d2b', '#14334d', '#17507a', '#1b6aa6'], + waves: ['rgba(90, 165, 225, 0.9)', 'rgba(65, 140, 210, 0.88)', 'rgba(50, 120, 190, 0.86)'], + }, + }; + + const getTheme = () => (document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'); + + const getCoverageHeight = () => { + if (servicesSection) { + const coverage = servicesSection.offsetTop + servicesSection.offsetHeight * 0.5; + return Math.min(coverage, main.scrollHeight); + } + if (heroSection) { + return heroSection.offsetHeight; + } + return main.scrollHeight; + }; + + const resize = () => { + dpr = window.devicePixelRatio || 1; + width = main.clientWidth; + height = Math.max(1, Math.round(getCoverageHeight())); + canvas.width = Math.floor(width * dpr); + canvas.height = Math.floor(height * dpr); + canvas.style.width = `${width}px`; + canvas.style.height = `${height}px`; + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + }; + + const drawBackground = (palette) => { + const gradient = ctx.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, palette.gradient[0]); + gradient.addColorStop(0.35, palette.gradient[1]); + gradient.addColorStop(0.55, palette.gradient[2]); + gradient.addColorStop(0.75, palette.gradient[3]); + gradient.addColorStop(1, palette.gradient[4]); + ctx.fillStyle = gradient; + ctx.fillRect(0, 0, width, height); + }; + + const drawWave = (palette, wave, time, index) => { + const yBase = height * wave.yRatio; + const phase = time * wave.speed; + const prevAlpha = ctx.globalAlpha; + ctx.globalAlpha = wave.alpha ?? 1; + ctx.beginPath(); + ctx.moveTo(0, height); + for (let x = 0; x <= width; x += 18) { + const theta = (x / wave.wavelength) * Math.PI * 2 + phase; + const y = yBase + Math.sin(theta) * wave.amplitude; + ctx.lineTo(x, y); + } + ctx.lineTo(width, height); + ctx.closePath(); + ctx.fillStyle = palette.waves[index % palette.waves.length]; + ctx.fill(); + ctx.globalAlpha = prevAlpha; + }; + + const render = (timestamp) => { + const palette = palettes[getTheme()]; + const time = timestamp * 0.001; + ctx.clearRect(0, 0, width, height); + drawBackground(palette); + waveDefs.forEach((wave, index) => drawWave(palette, wave, time, index)); + rafId = requestAnimationFrame(render); + }; + + const stop = () => { + if (rafId) { + cancelAnimationFrame(rafId); + rafId = null; + } + }; + + const drawStatic = () => { + const palette = palettes[getTheme()]; + ctx.clearRect(0, 0, width, height); + drawBackground(palette); + waveDefs.forEach((wave, index) => drawWave(palette, wave, 0, index)); + }; + + const start = () => { + stop(); + resize(); + if (prefersReduced.matches) { + drawStatic(); + return; + } + rafId = requestAnimationFrame(render); + }; + + const onResize = () => { + resize(); + if (prefersReduced.matches) { + drawStatic(); + } + }; + + const themeObserver = new MutationObserver(() => { + if (prefersReduced.matches) { + drawStatic(); + } + }); + + themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); + prefersReduced.addEventListener('change', start); + window.addEventListener('resize', onResize); + window.addEventListener('load', onResize); + + start(); +})(); diff --git a/assets/js/home-hero.js b/assets/js/home-hero.js new file mode 100644 index 0000000..71ed5d2 --- /dev/null +++ b/assets/js/home-hero.js @@ -0,0 +1,40 @@ +(() => { + const hero = document.querySelector('.hero-section'); + if (!hero) return; + + const body = document.body; + const desktopQuery = window.matchMedia('(min-width: 900px)'); + let hasScrolled = false; + + const applyState = () => { + if (!desktopQuery.matches) { + body.classList.remove('home-hero-initial', 'home-hero-scrolled'); + return; + } + + if (hasScrolled || window.scrollY > 6) { + body.classList.remove('home-hero-initial'); + body.classList.add('home-hero-scrolled'); + } else { + body.classList.add('home-hero-initial'); + body.classList.remove('home-hero-scrolled'); + } + }; + + const onScroll = () => { + hasScrolled = window.scrollY > 6; + applyState(); + }; + + const onResize = () => { + if (!desktopQuery.matches) { + hasScrolled = false; + } + applyState(); + }; + + applyState(); + window.addEventListener('scroll', onScroll, { passive: true }); + window.addEventListener('resize', onResize); + desktopQuery.addEventListener('change', onResize); +})(); diff --git a/assets/js/stream.js b/assets/js/stream.js index bd23e40..6d7aba9 100644 --- a/assets/js/stream.js +++ b/assets/js/stream.js @@ -1,5 +1,5 @@ document.addEventListener('DOMContentLoaded', function() { - const container = document.querySelector('.hero-description'); + const container = document.querySelector('.stream-text') || document.querySelector('.hero-description'); if (!container) return; const cnText = container.getAttribute('data-cn') || ''; diff --git a/functions.php b/functions.php index cc3b91c..f7584b4 100644 --- a/functions.php +++ b/functions.php @@ -31,17 +31,19 @@ function itstudio_enqueue_scripts() { // 仅在首页加载 Hero 样式 if (is_front_page() || is_home()) { wp_enqueue_style('itstudio-front-page', get_template_directory_uri() . '/assets/css/front-page.css', array('itstudio-style'), '2.1.2'); + wp_enqueue_script('itstudio-hero-waves', get_template_directory_uri() . '/assets/js/hero-waves.js', array(), '1.0.0', true); + wp_enqueue_script('itstudio-home-hero', get_template_directory_uri() . '/assets/js/home-hero.js', array(), '1.0.0', true); } // 仅在工作室介绍页加载(包含 /about fallback) - $is_about_fallback = false; - if (is_404()) { + $is_about = is_page('about') || is_page_template('page-about.php'); + if (!$is_about && is_404()) { global $wp; $request = isset($wp->request) ? trim($wp->request, '/') : ''; - $is_about_fallback = ($request === 'about'); + $is_about = ($request === 'about'); } - if (is_page('about') || is_page_template('page-about.php') || $is_about_fallback) { + if ($is_about) { wp_enqueue_style('itstudio-intro', get_template_directory_uri() . '/assets/css/intro.css', array('itstudio-content'), '2.1.2'); wp_enqueue_script('itstudio-intro-scroll', get_template_directory_uri() . '/assets/js/intro-scroll.js', array(), '1.0.0', true); } @@ -49,8 +51,8 @@ function itstudio_enqueue_scripts() { // Scripts wp_enqueue_script('itstudio-theme-toggle', get_template_directory_uri() . '/assets/js/theme-toggle.js', array(), '1.0.0', true); wp_enqueue_script('itstudio-lang-toggle', get_template_directory_uri() . '/assets/js/lang-toggle.js', array(), '1.0.0', true); - // 注册并加载打字机效果脚本 - 仅在首页 - if (is_front_page() || is_home()) { + // 注册并加载打字机效果脚本 - 仅在工作室介绍页 + if ($is_about) { wp_enqueue_script('itstudio-stream', get_template_directory_uri() . '/assets/js/stream.js', array(), '1.0.0', true); } wp_enqueue_script('itstudio-main', get_template_directory_uri() . '/assets/js/main.js', array(), '1.0.0', true); diff --git a/index.php b/index.php index 3842ff9..17aecab 100644 --- a/index.php +++ b/index.php @@ -1,13 +1,24 @@
+
-

-

+

+ + +

+
+ +
+
+

diff --git a/page-about.php b/page-about.php index 465e4b6..8e780de 100644 --- a/page-about.php +++ b/page-about.php @@ -8,7 +8,7 @@ get_header();

-

+

diff --git a/resources/title.svg b/resources/title.svg new file mode 100644 index 0000000..0fe1297 --- /dev/null +++ b/resources/title.svg @@ -0,0 +1,14 @@ + + + + + + + +