simba-index/components/robots-showcase.tsx

360 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useRef, useState } from "react"
import Image from "next/image"
import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { ChevronLeft, ChevronRight, Info } from "lucide-react"
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
export default function RobotsShowcase() {
const scrollContainerRef = useRef<HTMLDivElement>(null)
const [showLeftArrow, setShowLeftArrow] = useState(false)
const [showRightArrow, setShowRightArrow] = useState(true)
const [activeIndex, setActiveIndex] = useState(0)
const robots = [
{
id: 1,
name: "初号步兵",
image: "/placeholder.svg?height=400&width=600",
category: "INFANTRY",
description:
"Our flagship defense robot, equipped with advanced armor plating and a 360° rotating turret system.",
specs: ["Weight: 15kg", "Max Speed: 3m/s", "Battery Life: 30min", "Weapon: Launcher"],
},
{
id: 2,
name: "初号英雄",
image: "/placeholder.svg?height=400&width=600",
category: "HERO",
description: "High-mobility attack unit designed for rapid engagement and strategic positioning.",
specs: ["Weight: 12kg", "Max Speed: 4.5m/s", "Battery Life: 25min", "Weapon: Dual Launcher System"],
},
{
id: 3,
name: "零号哨兵",
image: "/placeholder.svg?height=400&width=600",
category: "SENTRY",
description:
"Lightweight reconnaissance drone providing battlefield intelligence and limited attack capabilities.",
specs: ["Weight: 2kg", "Max Speed: 10m/s", "Flight Time: 15min", "Sensors: 4K Camera, Infrared"],
},
{
id: 4,
name: "零号工程",
image: "/placeholder.svg?height=400&width=600",
category: "ENGINEER",
description: "Specialized support unit capable of on-field repairs and resource management during matches.",
specs: ["Weight: 18kg", "Max Speed: 2m/s", "Battery Life: 40min", "Tools: Mechanical Arm, Repair Module"],
},
]
const scroll = (direction: "left" | "right") => {
if (scrollContainerRef.current) {
const { current: container } = scrollContainerRef
const scrollAmount = container.clientWidth * 0.8
const cardWidth = container.querySelector(".card-hover-effect")?.clientWidth || 0
const gap = 24 // 6 * 4px (gap-6)
if (direction === "left") {
container.scrollBy({ left: -scrollAmount, behavior: "smooth" })
setActiveIndex((prev) => (prev === 0 ? robots.length - 1 : prev - 1))
} else {
container.scrollBy({ left: scrollAmount, behavior: "smooth" })
setActiveIndex((prev) => (prev === robots.length - 1 ? 0 : prev + 1))
}
// 检查是否需要无限滚动
setTimeout(() => {
if (container) {
// 如果滚动到最左边(克隆的最后一个元素)
if (container.scrollLeft < cardWidth / 2) {
// 无动画地跳到真正的最后一组元素
container.scrollTo({
left: container.scrollWidth - cardWidth * 2,
behavior: "auto",
})
}
// 如果滚动到最右边(克隆的第一个元素)
else if (container.scrollLeft > container.scrollWidth - cardWidth * 1.5) {
// 无动画地跳到真正的第一组元素
container.scrollTo({
left: cardWidth,
behavior: "auto",
})
}
setShowLeftArrow(container.scrollLeft > 0)
setShowRightArrow(container.scrollLeft < container.scrollWidth - container.clientWidth - 10)
}
}, 500)
}
}
const handleScroll = () => {
if (scrollContainerRef.current) {
const { current: container } = scrollContainerRef
const cardWidth = container.querySelector(".card-hover-effect")?.clientWidth || 0
const gap = 24 // 6 * 4px (gap-6)
// 计算当前查看的卡片索引
const scrollPosition = container.scrollLeft
const itemWidth = cardWidth + gap
const index = Math.round(scrollPosition / itemWidth) - 1 // -1 因为有一个克隆元素在开头
if (index >= 0 && index < robots.length) {
setActiveIndex(index)
}
setShowLeftArrow(container.scrollLeft > 0)
setShowRightArrow(container.scrollLeft < container.scrollWidth - container.clientWidth - 10)
}
}
return (
<section id="robots" className="py-20 relative overflow-hidden">
<div className="absolute right-0 top-0 bottom-0 red-section z-0" />
<div className="container mx-auto px-4 relative z-10">
<div className="flex flex-col md:flex-row gap-8 mb-12">
<div className="md:w-1/3">
<div className="red-accent pl-4">
<h2 className="text-3xl font-bold mb-2 uppercase"></h2>
<p className="text-xs text-muted-foreground uppercase mb-4 font-mono">ROBOTS</p>
</div>
<div className="diagonal-line mb-8"></div>
<div className="content-overlay">
<p className="text-muted-foreground leading-relaxed">
RoboMaster
</p>
</div>
</div>
</div>
<div className="relative">
{showLeftArrow && (
<Button
variant="outline"
size="icon"
className="absolute left-0 top-1/2 -translate-y-1/2 z-10 bg-background/80 backdrop-blur-sm border-white/10 button-hover-effect"
onClick={() => scroll("left")}
>
<ChevronLeft className="h-6 w-6" />
</Button>
)}
<div
ref={scrollContainerRef}
className="flex gap-6 overflow-x-auto scrollbar-hide py-4 px-2 snap-x snap-mandatory"
onScroll={handleScroll}
>
{/* 添加末尾项到开头 */}
<Card
key={`clone-end-${robots[robots.length - 1].id}`}
className="min-w-[300px] md:min-w-[350px] border-white/10 bg-background/60 backdrop-blur-sm flex-shrink-0 card-hover-effect snap-center"
>
<CardContent className="p-0">
<div className="relative">
<Image
src={robots[robots.length - 1].image || "/placeholder.svg"}
alt={robots[robots.length - 1].name}
width={350}
height={200}
className="w-full h-48 object-cover"
/>
<div className="absolute top-0 right-0 bg-primary text-primary-foreground text-xs px-3 py-1">
{robots[robots.length - 1].category}
</div>
<div className="absolute bottom-0 left-0 p-2">
<p className="text-xs text-primary font-mono">{`0${robots.length}/0${robots.length}`}</p>
</div>
</div>
<div className="p-4">
<div className="flex justify-between items-start mb-2">
<h3 className="text-xl font-bold">{robots[robots.length - 1].name}</h3>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8 button-hover-effect">
<Info className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-[250px]">
<div className="space-y-2">
<p className="font-bold">Technical Specifications:</p>
<ul className="text-xs space-y-1">
{robots[robots.length - 1].specs.map((spec, index) => (
<li key={index}>{spec}</li>
))}
</ul>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<p className="text-sm text-muted-foreground mb-4 leading-relaxed">
{robots[robots.length - 1].description}
</p>
<Button variant="outline" size="sm" className="w-full border-white/10 button-hover-effect">
View Details
</Button>
</div>
</CardContent>
</Card>
{robots.map((robot, index) => (
<Card
key={robot.id}
className="min-w-[300px] md:min-w-[350px] border-white/10 bg-background/60 backdrop-blur-sm flex-shrink-0 card-hover-effect snap-center"
onMouseEnter={() => setActiveIndex(index)}
>
<CardContent className="p-0">
<div className="relative">
<Image
src={robot.image || "/placeholder.svg"}
alt={robot.name}
width={350}
height={200}
className="w-full h-48 object-cover"
/>
<div className="absolute top-0 right-0 bg-primary text-primary-foreground text-xs px-3 py-1">
{robot.category}
</div>
<div className="absolute bottom-0 left-0 p-2">
<p className="text-xs text-primary font-mono">{`0${index + 1}/0${robots.length}`}</p>
</div>
</div>
<div className="p-4">
<div className="flex justify-between items-start mb-2">
<h3 className="text-xl font-bold">{robot.name}</h3>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8 button-hover-effect">
<Info className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-[250px]">
<div className="space-y-2">
<p className="font-bold">Technical Specifications:</p>
<ul className="text-xs space-y-1">
{robot.specs.map((spec, index) => (
<li key={index}>{spec}</li>
))}
</ul>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<p className="text-sm text-muted-foreground mb-4 leading-relaxed">{robot.description}</p>
<Button variant="outline" size="sm" className="w-full border-white/10 button-hover-effect">
View Details
</Button>
</div>
</CardContent>
</Card>
))}
{/* 添加开头项到末尾 */}
<Card
key={`clone-start-${robots[0].id}`}
className="min-w-[300px] md:min-w-[350px] border-white/10 bg-background/60 backdrop-blur-sm flex-shrink-0 card-hover-effect snap-center"
>
<CardContent className="p-0">
<div className="relative">
<Image
src={robots[0].image || "/placeholder.svg"}
alt={robots[0].name}
width={350}
height={200}
className="w-full h-48 object-cover"
/>
<div className="absolute top-0 right-0 bg-primary text-primary-foreground text-xs px-3 py-1">
{robots[0].category}
</div>
<div className="absolute bottom-0 left-0 p-2">
<p className="text-xs text-primary font-mono">{`01/0${robots.length}`}</p>
</div>
</div>
<div className="p-4">
<div className="flex justify-between items-start mb-2">
<h3 className="text-xl font-bold">{robots[0].name}</h3>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className="h-8 w-8 button-hover-effect">
<Info className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-[250px]">
<div className="space-y-2">
<p className="font-bold">:</p>
<ul className="text-xs space-y-1">
{robots[0].specs.map((spec, index) => (
<li key={index}>{spec}</li>
))}
</ul>
</div>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<p className="text-sm text-muted-foreground mb-4 leading-relaxed">{robots[0].description}</p>
<Button variant="outline" size="sm" className="w-full border-white/10 button-hover-effect">
View Details
</Button>
</div>
</CardContent>
</Card>
</div>
{showRightArrow && (
<Button
variant="outline"
size="icon"
className="absolute right-0 top-1/2 -translate-y-1/2 z-10 bg-background/80 backdrop-blur-sm border-white/10 button-hover-effect"
onClick={() => scroll("right")}
>
<ChevronRight className="h-6 w-6" />
</Button>
)}
{/* 添加索引指示器 */}
<div className="flex justify-center mt-6 gap-2">
{robots.map((_, index) => (
<button
key={index}
className={`w-2 h-2 rounded-full transition-all ${
activeIndex === index ? "bg-primary w-4" : "bg-gray-500"
}`}
onClick={() => {
if (scrollContainerRef.current) {
const cardWidth = scrollContainerRef.current.querySelector(".card-hover-effect")?.clientWidth || 0
const gap = 24 // 6 * 4px (gap-6)
scrollContainerRef.current.scrollTo({
left: (index + 1) * (cardWidth + gap), // +1 for the cloned element at start
behavior: "smooth",
})
setActiveIndex(index)
}
}}
/>
))}
</div>
</div>
</div>
</section>
)
}