konva实战-摇钱树-翻牌

1、摇钱树

先用千问大模型生成示例代码

https://bailian.console.aliyun.com/cn-beijing/?tab=demohouse#/experience/llm

image

如何不合适可以手动修改代码

image

最终代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Konva.js 摇钱树 - 礼物掉落弹窗版</title>
    <script src="https://unpkg.com/konva@9.2.0/konva.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
            overflow: hidden;
            background-color: #87CEEB;
            font-family: 'Microsoft YaHei', sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        /* 按钮样式 */
        #ui-layer {
            position: absolute;
            top: 20px;
            z-index: 10;
            text-align: center;
        }

        button {
            padding: 15px 40px;
            font-size: 20px;
            background: linear-gradient(to bottom, #ff7675, #d63031);
            color: white;
            border: 2px solid #fff;
            border-radius: 30px;
            cursor: pointer;
            box-shadow: 0 5px 15px rgba(0,0,0,0.3);
            transition: all 0.2s;
            font-weight: bold;
            outline: none;
        }

        button:hover:not(:disabled) {
            transform: translateY(-2px);
            box-shadow: 0 8px 20px rgba(0,0,0,0.4);
        }

        button:active:not(:disabled) {
            transform: translateY(1px);
        }

        button:disabled {
            background: #b2bec3;
            cursor: not-allowed;
            opacity: 0.7;
        }

        /* 画布容器 */
        #container {
            margin-top: 60px;
            background-color: #fff;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.2);
        }

        /* 弹窗样式 */
        .modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.6);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 100;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.3s;
        }

        .modal-overlay.active {
            opacity: 1;
            pointer-events: all;
        }

        .modal-content {
            background: white;
            padding: 30px;
            border-radius: 15px;
            text-align: center;
            width: 300px;
            box-shadow: 0 10px 25px rgba(0,0,0,0.3);
            transform: scale(0.8);
            transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        }

        .modal-overlay.active .modal-content {
            transform: scale(1);
        }

        .gift-icon {
            font-size: 60px;
            margin-bottom: 10px;
            display: block;
        }

        .gift-title {
            font-size: 24px;
            color: #2d3436;
            margin: 10px 0;
        }

        .gift-desc {
            color: #636e72;
            margin-bottom: 20px;
        }

        .close-btn {
            background-color: #0984e3;
            padding: 10px 30px;
            font-size: 16px;
            border-radius: 20px;
            border: none;
            color: white;
            cursor: pointer;
        }
    </style>
</head>
<body>

    <div id="ui-layer">
        <button id="shakeBtn">🎄 摇动摇钱树</button>
    </div>

    <div id="container"></div>

    <!-- 弹窗结构 -->
    <div class="modal-overlay" id="giftModal">
        <div class="modal-content">
            <span class="gift-icon" id="modalIcon">🎁</span>
            <h2 class="gift-title" id="modalTitle">获得礼物</h2>
            <p class="gift-desc" id="modalDesc">恭喜获得神秘大奖!</p>
            <button class="close-btn" id="closeModalBtn">收下礼物</button>
        </div>
    </div>

    <script>
        // --- 1. 初始化 Konva ---
        const width = 800;
        const height = 600;
        const groundY = height - 50;

        const stage = new Konva.Stage({
            container: 'container',
            width: width,
            height: height,
        });

        const layer = new Konva.Layer();
        stage.add(layer);

        // 地面
        const ground = new Konva.Rect({
            x: 0, y: groundY, width: width, height: 50,
            fill: '#55efc4', listening: false
        });
        layer.add(ground);

        // --- 2. 创建树 ---
        const treeGroup = new Konva.Group({
            x: width / 2,
            y: groundY,
            offset: { x: 0, y: 0 } // 旋转轴在底部
        });

        // 树干
        const trunk = new Konva.Rect({
            x: -30, y: -300, width: 60, height: 300,
            fill: '#8B4513', cornerRadius: 5
        });

        // 树冠
        const foliageGroup = new Konva.Group();
        for(let i=0; i<50; i++) {
            const angle = Math.random() * Math.PI;
            const r = Math.random() * 120 + 40;
            const fx = Math.cos(angle) * r * (Math.random() > 0.5 ? 1 : -1);
            const fy = -Math.sin(angle) * r - 210;
            
            foliageGroup.add(new Konva.Circle({
                x: fx, y: fy,
                radius: Math.random() * 25 + 15,
                fill: i % 2 === 0 ? '#00b894' : '#55efc4',
                opacity: 0.9
            }));
        }

        treeGroup.add(trunk);
        treeGroup.add(foliageGroup);
        layer.add(treeGroup);

        // --- 3. 礼物系统 ---
        const giftTypes = [
            { icon: '💰', name: '金币袋', desc: '财富滚滚来!' },
            { icon: '💎', name: '大钻石', desc: '闪耀夺目!' },
            { icon: '📱', name: '新手机', desc: '最新款旗舰机!' },
            { icon: '🚗', name: '跑车模型', desc: '速度与激情!' },
            { icon: '🧧', name: '大红包', desc: '恭喜发财!' }
        ];

        let treeGifts = []; // 存储在树上的礼物对象
        let currentFallingGift = null; // 当前正在掉落的礼物
        let isShaking = false;
        let isModalOpen = false;

        // 初始化树上的礼物
        function initTreeGifts() {
            // 清除旧礼物
            treeGifts.forEach(g => g.group.destroy());
            treeGifts = [];

            // 生成 5-8 个新礼物
            const count = Math.floor(Math.random() * 4) + 5;
            for (let i = 0; i < count; i++) {
                createHangingGift();
            }
        }

        function createHangingGift() {
            // 随机位置 (树冠范围内)
            const angle = Math.random() * Math.PI;
            const r = Math.random() * 100 + 60;
            const gx = Math.cos(angle) * r * (Math.random() > 0.5 ? 1 : -1);
            const gy = -Math.sin(angle) * r - 210;

            const giftGroup = new Konva.Group({
                x: gx,
                y: gy,
                rotation: Math.random() * 20 - 10 // 随机轻微旋转
            });

            // 礼物盒主体
            const box = new Konva.Rect({
                x: -15, y: -15, width: 30, height: 30,
                fill: '#ff7675', stroke: '#d63031', strokeWidth: 2,
                cornerRadius: 3
            });
            // 丝带
            const ribbon = new Konva.Rect({ x: -3, y: -15, width: 6, height: 30, fill: '#ffeaa7' });
            
            giftGroup.add(box);
            giftGroup.add(ribbon);
            
            // 将礼物添加到树组中
            foliageGroup.add(giftGroup);

            // 记录数据
            treeGifts.push({
                group: giftGroup,
                type: giftTypes[Math.floor(Math.random() * giftTypes.length)],
                isOnTree: true
            });
        }

        // --- 4. 动画循环 (处理掉落物理) ---
        function animate() {
            if (currentFallingGift) {
                const gift = currentFallingGift;
                
                // 重力加速
                gift.vy += 0.8; 
                gift.group.y(gift.group.y() + gift.vy);
                
                // 旋转下落
                gift.group.rotation(gift.group.rotation() + 5);

                // 落地检测
                // 注意:gift.group 现在在 layer 上,y 是绝对坐标
                if (gift.group.y() >= groundY - 20) {
                    gift.group.y(groundY - 20);
                    onGiftLand(gift);
                    currentFallingGift = null;
                    return;
                }
            }
            // layer.batchDraw();
            requestAnimationFrame(animate);
        }

        // --- 5. 交互逻辑 ---

        // 礼物落地处理
        function onGiftLand(giftObj) {
            // 简单的弹跳效果
            giftObj.group.to({
                scaleY: 0.8,
                scaleX: 1.2,
                duration: 0.1,
                onFinish: () => {
                    giftObj.group.to({
                        scaleY: 1,
                        scaleX: 1,
                        duration: 0.1
                    });
                    // 落地后显示弹窗
                    showGiftModal(giftObj.type);
                }
            });
        }

        // 显示弹窗
        function showGiftModal(typeData) {
            isModalOpen = true;
            document.getElementById('modalIcon').innerText = typeData.icon;
            document.getElementById('modalTitle').innerText = typeData.name;
            document.getElementById('modalDesc').innerText = typeData.desc;
            
            const modal = document.getElementById('giftModal');
            modal.classList.add('active');
        }

        // 关闭弹窗
        document.getElementById('closeModalBtn').addEventListener('click', () => {
            const modal = document.getElementById('giftModal');
            modal.classList.remove('active');
            isModalOpen = false;
            
            // 重置按钮状态
            const btn = document.getElementById('shakeBtn');
            btn.disabled = false;
            btn.innerText = "🎄 继续摇树";
            
            // 可选:落地后过一会让礼物消失,或者保留在地上
            // 这里我们让地上的礼物停留 2 秒后消失,保持画面整洁
            if(currentFallingGift === null && treeGifts.length > 0) { 
               // 这里的逻辑稍微复杂,因为 currentFallingGift 已经置空了
               // 我们简单处理:不自动消失,或者你可以添加逻辑让它淡出
            }
        });

        // 摇树按钮点击
        document.getElementById('shakeBtn').addEventListener('click', () => {
            if (treeGifts.length === 0) {
                finishShake();
                return;
            }
            if (isShaking || isModalOpen || treeGifts.length === 0) return;

            isShaking = true;
            const btn = document.getElementById('shakeBtn');
            btn.disabled = true;
            btn.innerText = "摇晃中...";

            // 1. 执行摇晃动画 (使用 Tween 链式调用)
            // 左 -> 右 -> 左 -> 右 -> 回正
            const shakeSequence = [
                { rot: -20, dur: 0.1 },
                { rot: 20, dur: 0.1 },
                { rot: -20, dur: 0.1 },
                { rot: 20, dur: 0.1 },
                { rot: 0, dur: 0.2 }
            ];

            let currentIndex = 0;

            function playNextShake() {
                if (currentIndex >= shakeSequence.length) {
                    animate();
                    finishShake();
                    return;
                }

                const step = shakeSequence[currentIndex];
                new Konva.Tween({
                    node: treeGroup,
                    rotation: step.rot,
                    duration: step.dur,
                    easing: Konva.Easings.EaseInOut,
                    onFinish: () => {
                        currentIndex++;
                        playNextShake();
                    }
                }).play();
            }

            playNextShake();
        });

        // 摇晃结束后的逻辑
        function finishShake() {
            isShaking = false;
            
            // 2. 选择一个礼物掉落
            if (treeGifts.length > 0) {
                // 随机选一个
                const randomIndex = Math.floor(Math.random() * treeGifts.length);
                const selectedGiftData = treeGifts[randomIndex];
                
                // 从数组移除
                treeGifts.splice(randomIndex, 1);
                selectedGiftData.isOnTree = false;

                // 关键:将礼物从树组 (treeGroup) 移动到主图层 (layer)
                // 这样它就不再跟随树旋转,而是独立掉落
                const globalPos = selectedGiftData.group.getAbsolutePosition();
                
                // 从父节点移除
                selectedGiftData.group.moveTo(layer);
                // 设置绝对位置
                selectedGiftData.group.position(globalPos);
                // 重置旋转偏移,确保视觉连续
                selectedGiftData.group.rotation(selectedGiftData.group.rotation() + treeGroup.rotation());

                // 设置物理属性并开始掉落
                currentFallingGift = {
                    group: selectedGiftData.group,
                    type: selectedGiftData.type,
                    vy: 2 // 初始向下速度
                };
            } else {
                // 如果没有礼物了
                const btn = document.getElementById('shakeBtn');
                btn.innerText = "🎄 树上没礼物了 (刷新重置)";
                // 这里可以添加逻辑重新长礼物,或者提示刷新
                setTimeout(() => {
                     btn.innerText = "🎄 摇动摇钱树";
                     btn.disabled = false;
                     initTreeGifts(); // 重新长满
                    // alert("树上长满了新礼物!");
                }, 1000);
            }
        }

        // 初始化
        initTreeGifts();

    </script>
</body>
</html>

  

2、翻牌

image

最终代码

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Konva 3D翻牌匹配游戏</title>
    <script src="https://unpkg.com/konva@8.4.0/konva.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 20px;
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #4b6cb7 0%, #182848 100%);
            min-height: 100vh;
            display: flex;
            flex-direction: column;
            align-items: center;
            color: white;
            
        }

        .game-header {
            text-align: center;
            margin-bottom: 20px;
        }

        .game-title {
            font-size: 2.2rem;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
        }

        .score-container {
            background: rgba(255,255,255,0.1);
            padding: 12px 25px;
            border-radius: 30px;
            font-size: 1.3rem;
            backdrop-filter: blur(10px);
            border: 1px solid rgba(255,255,255,0.2);
        }

        #container {
            border-radius: 15px;
            overflow: hidden;
            box-shadow: 0 20px 50px rgba(0,0,0,0.4);
            background: rgba(255,255,255,0.05);
        }

        .instructions {
            text-align: center;
            margin-top: 20px;
            max-width: 800px;
            line-height: 1.6;
            background: rgba(255,255,255,0.1);
            padding: 15px;
            border-radius: 15px;
            backdrop-filter: blur(5px);
        }
    </style>
</head>
<body>
    <div class="game-header">
        <h1 class="game-title">✨ 3D翻牌匹配游戏 ✨</h1>
        <div class="score-container">得分: <span id="score">0</span></div>
    </div>

    <div id="container"></div>

    <div class="instructions">
        <p>点击卡牌进行3D翻转!找到相同的卡牌匹配成功时,它们会飞向中心碰撞并获得奖励 🎯</p>
    </div>

    <script>
        // 游戏配置
        const CONFIG = {
            cardWidth: 90,
            cardHeight: 130,
            cardSpacing: 25,
            columns: 4,
            rows: 4,
            symbols: ['🌟', '⭐', '💎', '🌈', '🔥', '⚡', '🎯', '🎮']
        };

        // 计算画布尺寸
        const canvasWidth = CONFIG.columns * (CONFIG.cardWidth + CONFIG.cardSpacing) + CONFIG.cardSpacing;
        const canvasHeight = CONFIG.rows * (CONFIG.cardHeight + CONFIG.cardSpacing) + CONFIG.cardSpacing;

        // 初始化Konva舞台
        const stage = new Konva.Stage({
            container: 'container',
            width: canvasWidth,
            height: canvasHeight
        });

        // 主图层
        const mainLayer = new Konva.Layer();
        stage.add(mainLayer);

        // 动画图层(用于飞行卡牌)
        const animationLayer = new Konva.Layer();
        stage.add(animationLayer);

        // 游戏状态
        let cards = [];
        let flippedCards = [];
        let matchedPairs = 0;
        let score = 0;
        let canFlip = true;

        // 卡牌类
        class Card {
            constructor(symbol, x, y, index) {
                this.symbol = symbol;
                this.x = x;
                this.y = y;
                this.index = index;
                this.isFlipped = false;
                this.isMatched = false;
                this.group = null;
                
                this.createCard();
            }

            createCard() {
                // 创建卡牌组
                this.group = new Konva.Group({
                    x: this.x,
                    y: this.y,
                    offsetX: CONFIG.cardWidth / 2,
                    offsetY: CONFIG.cardHeight / 2,
                    listening: true
                });

                // 背面矩形
                this.backRect = new Konva.Rect({
                    width: CONFIG.cardWidth,
                    height: CONFIG.cardHeight,
                    fill: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
                    cornerRadius: 12,
                    stroke: '#fff',
                    strokeWidth: 2,
                    shadowColor: 'rgba(0,0,0,0.3)',
                    shadowBlur: 10,
                    shadowOffset: { x: 0, y: 4 }
                });

                // 背面图标
                this.backIcon = new Konva.Text({
                    text: '❓',
                    fontSize: 45,
                    fontFamily: 'Arial',
                    fill: 'white',
                    width: CONFIG.cardWidth,
                    height: CONFIG.cardHeight,
                    align: 'center',
                    verticalAlign: 'middle',
                    fontWeight: 'bold'
                });

                // 正面矩形
                this.frontRect = new Konva.Rect({
                    width: CONFIG.cardWidth,
                    height: CONFIG.cardHeight,
                    fill: 'white',
                    cornerRadius: 12,
                    stroke: '#333',
                    strokeWidth: 2,
                    shadowColor: 'rgba(0,0,0,0.3)',
                    shadowBlur: 10,
                    shadowOffset: { x: 0, y: 4 }
                });

                // 正面符号
                this.frontSymbol = new Konva.Text({
                    text: this.symbol,
                    fontSize: 45,
                    fontFamily: 'Arial',
                    fill: '#333',
                    width: CONFIG.cardWidth,
                    height: CONFIG.cardHeight,
                    align: 'center',
                    verticalAlign: 'middle'
                });

                // 添加到组
                this.group.add(this.backRect);
                this.group.add(this.backIcon);
                this.group.add(this.frontRect);
                this.group.add(this.frontSymbol);

                // 初始状态:显示背面,隐藏正面
                this.frontRect.visible(false);
                this.frontSymbol.visible(false);

                // 添加点击事件
                this.group.on('click tap', () => {
                    this.flipCard();
                });

                mainLayer.add(this.group);
            }

            flipCard() {
                // this.showWinMessage();
                // return;
                if (!canFlip || this.isFlipped || this.isMatched) {
                    return;
                }

                // 3D翻转动画
                this.isFlipped = true;
                console.log("this.group-scaleX", this.group.scaleX());
                
                // 第一阶段:旋转到90度(消失)
                this.group.to({
                    scaleX: 0,
                    duration: 0.2,
                    easing: Konva.Easings.EaseInOut,
                    onFinish: () => {
                        // 切换显示内容
                        this.backRect.visible(false);
                        this.backIcon.visible(false);
                        this.frontRect.visible(true);
                        this.frontSymbol.visible(true);
                        
                        // 第二阶段:从90度旋转到0度(显示正面)
                        this.group.to({
                            scaleX: 1,
                            duration: 0.2,
                            easing: Konva.Easings.EaseInOut
                        });
                    }
                });

                flippedCards.push(this);
                
                if (flippedCards.length === 2) {
                    canFlip = false;
                    setTimeout(() => {
                        this.checkMatch();
                    }, 500);
                }
            }

            checkMatch() {
                const [card1, card2] = flippedCards;
                
                if (card1.symbol === card2.symbol) {
                    // 匹配成功 - 执行飞行动画
                    this.flyToCenter(card1, card2);
                } else {
                    // 匹配失败 - 翻回背面
                    setTimeout(() => {
                        card1.flipBack();
                        card2.flipBack();
                        flippedCards = [];
                        canFlip = true;
                    }, 1000);
                }
            }
            randomNeonColor() {
                return ['#00FFE0', '#0066FF', '#CC00FF', '#FF0099', '#FF6600', '#FFFF00'][Math.floor(Math.random() * 6)];
            }
            flyToCenter(card1, card2) {
                const centerX = canvasWidth / 2;
                const centerY = canvasHeight / 2;

                // 将卡牌移动到动画图层以便独立控制
                // card1.group.getLayer().remove(card1.group);
                // card2.group.getLayer().remove(card2.group);
                animationLayer.add(card1.group);
                animationLayer.add(card2.group);

                // 飞行动画
                card1.group.to({
                    x: centerX,
                    y: centerY,
                    duration: 0.8,
                    easing: Konva.Easings.EaseOut,
                    onFinish: () => {
                        // 碰撞效果
                        this.createCollisionEffect(centerX, centerY);
                        this.showReward(centerX, centerY);
                        
                        // 淡出卡牌
                        card1.group.to({
                            opacity: 0,
                            duration: 0.3
                        });
                        card2.group.to({
                            opacity: 0,
                            duration: 0.3,
                            onFinish: () => {
                                // 更新游戏状态
                                card1.isMatched = true;
                                card2.isMatched = true;
                                matchedPairs++;
                                score += 10;
                                document.getElementById('score').textContent = score;
                                
                                flippedCards = [];
                                canFlip = true;
                                
                                // 检查游戏完成
                                if (matchedPairs === CONFIG.symbols.length) {
                                    setTimeout(() => {
                                        this.showWinMessage();
                                    }, 1000);
                                }
                            }
                        });
                    }
                });

                card2.group.to({
                    x: centerX,
                    y: centerY,
                    duration: 0.8,
                    easing: Konva.Easings.EaseOut
                });
            }

            createCollisionEffect(x, y) {
                // 创建碰撞粒子效果
                this.flyoffEffect(x, y);
                // 粒子爆炸
                // this.bombEffect(x, y);
                // 波纹扩散
                // this.waveEffect(x, y);
                // 星光闪烁
                // this.twinkleEffect(x, y);
                // 中心闪光
                this.centerScaleEffect(x, y);
               
            }
            flyoffEffect(x, y) {
                for (let i = 0; i < 8; i++) {
                    const angle = (i / 8) * Math.PI * 2;
                    const particle = new Konva.Circle({
                        x: x,
                        y: y,
                        radius: 3,
                        fill: '#FFD700',
                        opacity: 1
                    });
                    
                    animationLayer.add(particle);
                    // 粒子飞散动画
                    particle.to({
                        x: x + Math.cos(angle) * 50,
                        y: y + Math.sin(angle) * 50,
                        radius: 0,
                        opacity: 0,
                        duration: 0.6,
                        onFinish: () => {
                            particle.destroy();
                        }
                    });
                }
            }
            bombEffect(x, y) {
                 const colors = ['#FFD700', '#FFA500', '#FF4500', '#FF1493', '#00BFFF'];
                const particles = [];
                
                // 创建粒子
                for (let i = 0; i < 150; i++) {
                    const size = Math.random() * 2;
                    const angle = Math.random() * Math.PI * 2;
                    const speed = Math.random() * 5 + 2;
                    
                    const particle = new Konva.Circle({
                        x: x,
                        y: y,
                        radius: size,
                        fill: colors[Math.floor(Math.random() * colors.length)],
                        opacity: 0.8,
                        shadowColor: 'white',
                        shadowBlur: 5
                    });
                    
                    animationLayer.add(particle);
                    particles.push(particle);
                    
                    // 动画
                    const tween = new Konva.Tween({
                        node: particle,
                        x: x + Math.cos(angle) * speed * 30,
                        y: y + Math.sin(angle) * speed * 30,
                        opacity: 0,
                        duration: 1.2,
                        easing: Konva.Easings.EaseOut,
                        onFinish: function() {
                            particle.destroy();
                        }
                    });
                    
                    // 延迟开始
                    setTimeout(() => {
                        tween.play();
                    }, i * 1.5);
                }
                
            }
            waveEffect(x, y) {
                for (let i = 0; i < 6; i++) {
                    const ripple = new Konva.Circle({
                        x: x,
                        y: y,
                        radius: 0,
                        stroke: "white",
                        strokeWidth: 6 - i * 0.8,
                        opacity: 0.7 - i * 0.12,
                    });

                    animationLayer.add(ripple);

                    ripple.to({
                        radius: 140 + i * 35,
                        opacity: 0,
                        duration: 0.9 + i * 0.15,
                        easing: Konva.Easings.EaseOut,
                        onFinish: () => {
                        ripple.destroy();
                        }
                    });
                }

            }
            twinkleEffect(x, y) {
                // 星光闪烁
                for (let i = 0; i < 60; i++) {
                    setTimeout(() => {
                        const star = new Konva.Star({
                            x: x + (Math.random() - 0.5) * 400,
                            y: y + (Math.random() - 0.6) * 400,
                            numPoints: 5,
                            innerRadius: 3,
                            outerRadius: 10,
                            fill: "#fff100",
                            opacity: 0.9,
                        });
                        animationLayer.add(star);
                        star.to({ opacity: 0.6, duration: 0.1, onFinish: () => {
                            star.to({ opacity: 0, duration: 0.7, delay: 0.5, onFinish: () => star.destroy() });
                        } });
                    }, i * 2);
                }
            }
            centerScaleEffect(x, y) {
                // 中心闪光
                 // 中心闪光
                const flash = new Konva.Circle({
                    x: x,
                    y: y,
                    radius: 10,
                    fill: 'white',
                    opacity: 0.8,
                    shadowColor: '#FFD700',
                    shadowBlur: 30
                });
                
                animationLayer.add(flash);
                
                const flashTween = new Konva.Tween({
                    node: flash,
                    radius: 100,
                    opacity: 0,
                    duration: 0.8,
                    easing: Konva.Easings.EaseOut,
                    onFinish: function() {
                        flash.destroy();
                    }
                });
                
                flashTween.play();
                
                animationLayer.draw();
            }
            showReward(x, y) {
                // 奖励文本
                const rewardText = new Konva.Text({
                    x: x,
                    y: y - 60,
                    text: '+10 分!',
                    fontSize: 32,
                    fontFamily: 'Arial',
                    fill: '#FFD700',
                    fontWeight: 'bold',
                    align: 'center',
                    width: 200,
                    shadowColor: 'rgba(255,215,0,0.5)',
                    shadowBlur: 15,
                    shadowOffset: { x: 0, y: 0 },
                    opacity: 0
                });

                animationLayer.add(rewardText);

                // 弹跳动画
                rewardText.to({
                    y: y - 80,
                    opacity: 1,
                    scale: { x: 1.2, y: 1.2 },
                    duration: 0.3,
                    easing: Konva.Easings.ElasticEaseOut,
                    onFinish: () => {
                        rewardText.to({
                            scale: { x: 1, y: 1 },
                            duration: 0.2
                        });
                        
                        // 淡出
                        setTimeout(() => {
                            rewardText.to({
                                opacity: 0,
                                y: y - 120,
                                duration: 1,
                                onFinish: () => {
                                    rewardText.destroy();
                                }
                            });
                        }, 1000);
                    }
                });
            }

            showWinMessage() {
                const winGroup = new Konva.Group({
                    x: canvasWidth / 2,
                    y: canvasHeight / 2,
                    offsetX: 0,
                    offsetY: 0
                });

                // 背景遮罩
                const overlay = new Konva.Rect({
                    x: 0,
                    y: 0,
                    offsetX: canvasWidth/2,
                    offsetY: canvasHeight/2,
                    width: canvasWidth,
                    height: canvasHeight,
                    fill: 'rgba(0,0,0,0.8)',
                     cornerRadius: 12,
                    listening: false
                });

                // 胜利文本
                const winText = new Konva.Text({
                    x: -190,
                    y: -140,
                    text: '🎉 恭喜通关! 🎉',
                    fontSize: 36,
                    fontFamily: 'Arial',
                    fill: '#FFD700',
                    align: 'center',
                    width: 400,
                    fontWeight: 'bold'
                });

                // 最终得分
                const scoreText = new Konva.Text({
                    x: -200,
                    y: -60,
                    text: `最终得分: ${score}`,
                    fontSize: 24,
                    fontFamily: 'Arial',
                    fill: 'white',
                    align: 'center',
                    width: 400
                });

                // 重新开始按钮
                const restartBtn = new Konva.Rect({
                    x: -80,
                    y: 0,
                    width: 160,
                    height: 50,
                    fill: 'linear-gradient(45deg, #ff6b6b, #4ecdc4)',
                    cornerRadius: 25,
                    stroke: 'white',
                    strokeWidth: 2,
                });

                const restartText = new Konva.Text({
                    x: -80,
                    y: 18,
                    text: '重新开始',
                    fontSize: 20,
                    fontFamily: 'Arial',
                    fill: 'white',
                    align: 'center',
                    width: 160,
                    listening: false
                });

                winGroup.add(overlay, winText, scoreText, restartBtn, restartText);
                animationLayer.add(winGroup);
                // restartText.on('click tap', () => {
                //     winGroup.destroy();
                //     initGame();
                // });
                restartBtn.on('click tap', () => {
                    winGroup.destroy();
                    initGame();
                });
                overlay.on('click tap', (event) => {
                    event.evt.stopPropagation(); // 阻止事件冒泡
                });
            }

            flipBack() {
                this.isFlipped = false;
                
                // 翻回背面的3D动画
                this.group.to({
                    scaleX: 0,
                    duration: 0.2,
                    easing: Konva.Easings.EaseInOut,
                    onFinish: () => {
                        // 切换回背面
                        this.backRect.visible(true);
                        this.backIcon.visible(true);
                        this.frontRect.visible(false);
                        this.frontSymbol.visible(false);
                        
                        this.group.to({
                            scaleX: 1,
                            duration: 0.2,
                            easing: Konva.Easings.EaseInOut
                        });
                    }
                });
            }
        }

        function initGame() {
            // 重置游戏状态
            cards = [];
            flippedCards = [];
            matchedPairs = 0;
            score = 0;
            canFlip = true;
            document.getElementById('score').textContent = score;

            // 清空所有图层
            mainLayer.destroyChildren();
            animationLayer.destroyChildren();

            // 创建卡牌数据(每种符号两个)
            let cardData = [...CONFIG.symbols, ...CONFIG.symbols];
            
            // 随机打乱数组
            // for (let i = cardData.length - 1; i > 0; i--) {
            //     const j = Math.floor(Math.random() * (i + 1));
            //     [cardData[i], cardData[j]] = [cardData[j], cardData[i]];
            // }

            // 创建卡牌
            let index = 0;
            for (let row = 0; row < CONFIG.rows; row++) {
                for (let col = 0; col < CONFIG.columns; col++) {
                    if (index >= cardData.length) break;
                    
                    const x = CONFIG.cardSpacing + col * (CONFIG.cardWidth + CONFIG.cardSpacing) + CONFIG.cardWidth / 2;
                    const y = CONFIG.cardSpacing + row * (CONFIG.cardHeight + CONFIG.cardSpacing) + CONFIG.cardHeight / 2;
                    
                    const card = new Card(cardData[index], x, y, index);
                    cards.push(card);
                    index++;
                }
            }

            mainLayer.draw();
            animationLayer.draw();
        }

        // 初始化游戏
        initGame();
    </script>
</body>
</html>

  

 

posted @ 2026-02-28 10:46  六只小猫  阅读(0)  评论(0)    收藏  举报