import { View, Text, Image, ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { useState, useEffect, useCallback, useMemo } from 'react'
import './index.scss'
import { PRODUCTS } from '../../../utils/productConfig'
import { ProductCategory } from '../../../types'
import { useSafeArea } from '../../../hooks/useSafeArea'
import { useStatusBar } from '../../../hooks/useStatusBar'
import { useThemeContext } from '../../../context/ThemeContext'
import ThemedPageMeta from '../../../components/ThemedPageMeta'
const clamp = (v: number, min: number, max: number) => Math.min(Math.max(v, min), max)
const lerp = (a: number, b: number, t: number) => a + (b - a) * t
const easeOut = (t: number) => 1 - Math.pow(1 - t, 3)
export default function ShopDetailPage() {
const { resolvedTheme } = useThemeContext()
const [winHeight, setWinHeight] = useState(667)
const [progress, setProgress] = useState(0)
const safe = useSafeArea()
// 顶部为沉浸式商品图,状态栏始终使用白字。
useStatusBar(resolvedTheme, { mode: 'light' })
const routerParams = Taro.getCurrentInstance().router
const routerParamsValues = routerParams ? routerParams.params : undefined
const productId = routerParamsValues ? routerParamsValues.id : ''
const product: ProductCategory | undefined = PRODUCTS.find(p => p.id === productId)
useEffect(() => {
try {
const info = Taro.getSystemInfoSync()
setWinHeight(info.windowHeight)
} catch (e) {
// fallback to safe default
}
}, [])
const handleScroll = useCallback((e: any) => {
const st = (e.detail && e.detail.scrollTop) || 0
const total = winHeight * 1.2
const p = clamp(st / total, 0, 1)
setProgress(p)
}, [winHeight])
/* Scroll-progress → multi-stage hero & content animations */
const heroStyles = useMemo(() => {
const p = progress
// 1. Image crop insets (vh %)
let topCrop = 0, bottomCrop = 0
if (p <= 0.4) {
const t = easeOut(p / 0.4)
topCrop = lerp(0, 32, t)
bottomCrop = lerp(0, 38, t)
} else if (p <= 0.55) {
const t = (p - 0.4) / 0.15
topCrop = lerp(32, 34, t)
bottomCrop = lerp(38, 41, t)
} else {
topCrop = 34; bottomCrop = 41
}
// 2. Hero translateY
let translateY = 0
if (p <= 0.4) translateY = 0
else if (p <= 0.7) translateY = lerp(0, -75, (p - 0.4) / 0.3)
else translateY = -75
// 3. Hero bottom info opacity (always visible on open, fades after scroll)
let heroInfoOpacity = 1
if (p <= 0.35) heroInfoOpacity = 1
else if (p <= 0.48) heroInfoOpacity = clamp(1 - (p - 0.35) / 0.13, 0, 1)
else heroInfoOpacity = 0
// 4. Transition band opacity
let bandOpacity = 0
if (p <= 0.55) bandOpacity = 0
else if (p <= 0.7) bandOpacity = (p - 0.55) / 0.15
else bandOpacity = 1
// 5. Content reveal
let contentOpacity = 1, contentOffset = 0
if (p <= 0.05) { contentOpacity = 0; contentOffset = 60 }
else if (p <= 0.22) {
const t = (p - 0.05) / 0.17
contentOpacity = easeOut(t)
contentOffset = lerp(60, 0, easeOut(t))
}
// 6. Back button (fade out as hero scrolls)
let backBtnOpacity = 1
if (p <= 0.15) backBtnOpacity = clamp(1 - p / 0.15, 0, 1)
return {
topCrop,
bottomCrop,
translateY,
heroInfoOpacity,
bandOpacity,
contentOpacity,
contentOffset,
backBtnOpacity,
visibleHeight: 100 - topCrop - bottomCrop
}
}, [progress])
const tone = useMemo(() => (product ? product.tone : [28, 22, 18]), [product])
if (!product) {
return (
商品不存在
)
}
return (
{/* Fixed Hero */}
{/* Bottom gradient overlay driven by product tone */}
{product.name}
{product.subtitle || product.desc}
¥{product.price}
{product.originalPrice && product.originalPrice > product.price && (
¥{product.originalPrice}
)}
{/* Scrollable content sits above the fixed hero */}
{/* Transparent spacer pushes content below hero */}
{/* Transition band — appears as hero scrolls away */}
{product.name}
{product.subtitle || product.desc}
¥{product.price}
{product.originalPrice && product.originalPrice > product.price && (
¥{product.originalPrice}
)}
{/* Main copy blocks */}
{product.story && (
设计理念
{product.story}
)}
{product.scene && (
使用场景
{product.scene}
)}
{product.tags && product.tags.length > 0 && (
{product.tags.map(tag => (
{tag}
))}
)}
{product.specs && product.specs.length > 0 && (
规格参数
{product.specs.map(([k, v]) => (
{k}
{v}
))}
)}
{/* Back button — glass morphism circle */}
Taro.navigateBack()}
style={{ top: `${safe.backBtnPaddingTop}px`, opacity: heroStyles.backBtnOpacity }}
>
←
{/* Bottom CTA — fixed, glass, links to existing product page */}
¥{product.price}
{product.originalPrice && product.originalPrice > product.price && (
¥{product.originalPrice}
)}
Taro.navigateTo({ url: `/pages/product/index?id=${product.id}` })}
>
立即定制
)
}