React 实战选择互动特效小功能

javascript 复制代码
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { setSwitchPage, updateSelectedEffect } from '../store/selectionSlice';
import { ArrowLeftOutlined } from '@ant-design/icons';
import { Button, Typography, Image, Row, Col, Card } from 'antd';

export interface MainProps {}

const { Title } = Typography;

const InteractEffectPannel: React.FC<MainProps> = () => {
    const dispatch = useDispatch();
    const effects = [
        { id: 1, src: require('../assets/pika/pika1.jpg'), label: '皮卡1' },
        { id: 2, src: require('../assets/pika/pika2.jpg'), label: '皮卡2' },
        { id: 3, src: require('../assets/pika/pika3.jpg'), label: '皮卡3' },
        { id: 4, src: require('../assets/pika/pika4.jpg'), label: '皮卡4' },
        { id: 5, src: require('../assets/pika/pika5.jpg'), label: '皮卡5' },
        { id: 6, src: require('../assets/pika/pika6.jpg'), label: '皮卡6' },
        { id: 7, src: require('../assets/pika/pika7.jpg'), label: '皮卡7' },
        { id: 8, src: require('../assets/pika/pika8.jpg'), label: '皮卡8' },
    ];
    const effectsGif = [
        { id: 1, src: require('../assets/pika/pika1.jpg'), label: '皮卡1' },
        { id: 2, src: require('../assets/pika/pika2.jpg'), label: '皮卡2' },
        { id: 3, src: require('../assets/pika/pika3.jpg'), label: '皮卡3' },
        { id: 4, src: require('../assets/pika/pika4.jpg'), label: '皮卡4' },
        { id: 5, src: require('../assets/pika/pika5.jpg'), label: '皮卡5' },
        { id: 6, src: require('../assets/pika/pika6.jpg'), label: '皮卡6' },
        { id: 7, src: require('../assets/pika/pika7.jpg'), label: '皮卡7' },
        { id: 8, src: require('../assets/pika/pika8.jpg'), label: '皮卡8' },
    ];

    const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
    const [hoverPosition, setHoverPosition] = useState<{ top: number; left: number } | null>(null);
    const [selectedEffect, setSelectedEffect] = useState<{src: string, label: string} | null>(null);
    const [selectedIndex, setSelectedIndex] = useState<number | null>(null);

    const handleMouseEnter = (event: any, index: number) => {
        const rect = event.target.getBoundingClientRect();
        let left = rect.left - 25;
        const viewWidth = 700;
        console.log('left:',left)
        if (left < 0) {
            left = 10;
        }

        setHoveredIndex(index);
        setHoverPosition({ top: rect.top + rect.height + 10, left: left });
    };

    const handleEffectSelect = (effect: {src: string, label: string}, index: number) => {
        setSelectedEffect({src: effect.src, label: effect.label});
        setSelectedIndex(index);
    };

    const handleConfirm = () => {
        if (selectedEffect) {
            dispatch(updateSelectedEffect(selectedEffect));
        }
    };

    return (
        <Card style={{ width: 720, height: 700, backgroundColor: '#2E3137', borderRadius: 12 }}>
            <Row align="middle" justify="space-between" style={{ padding: '10px 10px', borderBottom: '1px solid rgba(255, 255, 255, 0.1)' }}>
                <Button
                    type="link"
                    icon={<ArrowLeftOutlined style={{ color: '#ffffff' }} />}
                    onClick={() => dispatch(setSwitchPage(false))}
                />
                <Title level={4} style={{ color: '#FFFFFF' }}>选择互动特效</Title>
                <Button type="link" style={{ color: '#FFD736' }} onClick={handleConfirm}>确定</Button>
            </Row>
            <Row gutter={[24, 24]} style={{ padding: '20px', overflowY: 'auto', maxHeight: '700px' }}>
                {effects.map((effect, index) => (
                    <Col key={effect.id} xs={12} sm={6} md={6}>
                        <Card
                            hoverable
                            bordered={false}
                            onMouseEnter={(event) => handleMouseEnter(event, index)}
                            onMouseLeave={() => setHoveredIndex(null)}
                            onClick={() => handleEffectSelect(effect, index)}
                            style={{
                                display: 'flex',
                                flexDirection: 'column',
                                justifyContent: 'center',
                                alignItems: 'center',
                                width: 140,
                                height: 140,
                                position: 'relative',
                                boxSizing: 'border-box',
                                textAlign: 'center',
                                backgroundColor: 'rgba(0,0,0,0.5)',
                                padding: 0 // 确保没有额外的内边距
                            }}
                        >
                            <div style={{
                                display: 'flex',
                                justifyContent: 'center',
                                alignItems: 'center',
                                height: '100%'
                            }}>
                                <Image preview={false} src={effect.src} width={80} height={80} />
                            </div>
                            <Title level={5} style={{ color: 'rgba(255, 255, 255, 0.7)', fontSize: '12px', marginTop: '-7px' }}>{effect.label}</Title>
                            {(hoveredIndex === index || selectedIndex === index) && (
                                <div style={{
                                    position: 'absolute',
                                    top: '-5px',
                                    left: '-5px',
                                    right: '-5px',
                                    bottom: '-5px',
                                    border: '2px solid yellow',
                                    borderRadius: '10px',
                                    boxSizing: 'border-box'
                                }} />
                            )}
                        </Card>
                    </Col>
                ))}
            </Row>
            {hoveredIndex !== null && hoverPosition && (
                <div
                    style={{
                        position: 'absolute',
                        top: hoverPosition.top,
                        left: hoverPosition.left,
                        width: '180px',
                        height: '180px',
                        zIndex: 1000,
                        borderRadius: '10px',
                        boxShadow: '0 0 20px rgba(0, 0, 0, 0.4)',
                        backgroundColor: '#2E3137',
                        overflow: 'hidden',
                        paddingTop:5,
                    }}
                >
                    <Image preview={false} src={effectsGif[hoveredIndex].src} width={170} height={170} />
                </div>
            )}
        </Card>
    );
};

export default InteractEffectPannel;
相关推荐
Setsuna_F_Seiei3 分钟前
CocosCreator 游戏开发 - 多维度状态机架构设计与实现
前端·cocos creator·游戏开发
Bigger28 分钟前
CodeWalkers:让 AI 助手化身桌面宠物,陪你敲代码的赛博伙伴!
前端·app·ai编程
cyclv2 小时前
无网络地图展示轨迹,地图瓦片下载,绘制管线
前端·javascript
土豆12502 小时前
Tauri 入门与实践:用 Rust 构建你的下一个桌面应用
前端·rust
小陈工3 小时前
2026年4月2日技术资讯洞察:数据库融合革命、端侧AI突破与脑机接口产业化
开发语言·前端·数据库·人工智能·python·安全
IT_陈寒4 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
HIT_Weston4 小时前
41、【Agent】【OpenCode】本地代理分析(五)
javascript·人工智能·opencode
C澒4 小时前
AI 生码:A 类生码方案架构升级
前端·ai编程
前端Hardy4 小时前
前端必看!LocalStorage这么用,再也不踩坑(多框架通用,直接复制)
前端·javascript·面试
前端Hardy4 小时前
前端必看!前端路由守卫这么写,再也不担心权限混乱(Vue/React通用)
前端·javascript·面试