刚刚在别人博客看到的js

V 阿呆 屌  (UID: 1) 站长 [复制链接]
帖子链接已复制到剪贴板
帖子已经有人评论啦,不支持删除!

1068 0
function initKunShake(options = {}) {
    const config = {
        imgUrl: 'https://web.wang618.cn/kun/kun2026.webp',
        width: 150,
        left: 20,
        bottom: 20,
        shakeDuration: 0.8,
        onShakeEnd: null,
        audioUrls: [
            'https://web.wang618.cn/kun/你干嘛.mp3',
            'https://web.wang618.cn/kun/练习生.mp3',
            'https://web.wang618.cn/kun/鸡你太美.mp3'
        ],
        emojiCount: 40,
        emojiSizeRange: [24, 56],
        fallSpeedRange: [2, 7],
        swingRange: [10, 35],
        emojis: ['🏀', '🎵', '🐔', '🎤', '🥚'],
        emojiShadow: '0 2px 10px rgba(0,0,0,0.35)',
        moveRange: { x: [0, 100], y: [0, 100] },
        moveSpeed: 500
    };

    Object.assign(config, options);

    let audioElements = [];
    let currentPlayingAudio = null;
    let emojiRain = null;
    let moveInterval = null;
    const initialPosition = { left: config.left, bottom: config.bottom };
    let unplayedAudioIndices = [];
    let playedAudioIndices = [];

    class EmojiRain {
        constructor(config) {
            this.config = config;
            this.emojis = [];
            this.container = null;
            this.animationFrame = null;
        }

        createContainer() {
            const container = document.createElement('div');
            container.style.position = 'fixed';
            container.style.top = '0';
            container.style.left = '0';
            container.style.width = '100vw';
            container.style.height = '100vh';
            container.style.pointerEvents = 'none';
            container.style.zIndex = '99998';
            container.style.overflow = 'hidden';
            document.body.appendChild(container);
            this.container = container;
            return container;
        }

        createEmoji() {
            const size = this.getRandom(...this.config.emojiSizeRange);
            const emoji = document.createElement('div');
            
            emoji.style.position = 'absolute';
            emoji.style.width = `${size}px`;
            emoji.style.height = `${size}px`;
            emoji.style.fontSize = `${size}px`;
            emoji.style.lineHeight = '1';
            emoji.style.textAlign = 'center';
            emoji.style.textShadow = this.config.emojiShadow;
            emoji.style.opacity = '0.95';
            emoji.style.transform = 'translateZ(0)';
            emoji.style.transition = 'transform 0.1s ease';
            
            const randomEmoji = this.config.emojis[Math.floor(Math.random() * this.config.emojis.length)];
            emoji.textContent = randomEmoji;
            
            emoji.style.left = `${this.getRandom(0, window.innerWidth - size)}px`;
            emoji.style.top = `-${size}px`;
            
            const emojiState = {
                element: emoji,
                x: parseFloat(emoji.style.left),
                y: -size,
                size,
                fallSpeed: this.getRandom(...this.config.fallSpeedRange),
                swingAmplitude: this.getRandom(...this.config.swingRange),
                swingFrequency: this.getRandom(0.01, 0.04),
                swingPhase: this.getRandom(0, Math.PI * 2),
                rotateSpeed: this.getRandom(-1.2, 1.2),
                bounce: this.getRandom(0.8, 1.2)
            };
            
            this.container.appendChild(emoji);
            return emojiState;
        }

        getRandom(min, max) {
            return Math.random() * (max - min) + min;
        }

        init() {
            this.destroy();
            this.createContainer();
            
            for (let i = 0; i < this.config.emojiCount; i++) {
                this.emojis.push(this.createEmoji());
            }
            
            this.animate();
        }

        animate() {
            this.emojis.forEach((emoji) => {
                emoji.y += emoji.fallSpeed * emoji.bounce;
                
                emoji.x += Math.sin(emoji.swingPhase) * emoji.swingAmplitude * 0.12;
                emoji.swingPhase += emoji.swingFrequency;
                
                const rotateAngle = emoji.rotateSpeed * (emoji.y / 10);
                
                const scale = 1 + Math.sin(emoji.y * 0.02) * 0.1;
                
                emoji.element.style.transform = `translateX(${emoji.x - parseFloat(emoji.element.style.left)}px) rotate(${rotateAngle}deg) scale(${scale})`;
                emoji.element.style.top = `${emoji.y}px`;
                
                if (emoji.y > window.innerHeight + emoji.size) {
                    emoji.y = -emoji.size;
                    emoji.x = this.getRandom(0, window.innerWidth - emoji.size);
                    emoji.element.style.left = `${emoji.x}px`;
                    emoji.element.style.top = `${emoji.y}px`;
                    const randomEmoji = this.config.emojis[Math.floor(Math.random() * this.config.emojis.length)];
                    emoji.element.textContent = randomEmoji;
                    emoji.bounce = this.getRandom(0.8, 1.2);
                    emoji.rotateSpeed = this.getRandom(-1.2, 1.2);
                }
            });
            
            this.animationFrame = requestAnimationFrame(() => this.animate());
        }

        destroy() {
            if (this.animationFrame) {
                cancelAnimationFrame(this.animationFrame);
            }
            if (this.container && this.container.parentNode) {
                this.container.parentNode.removeChild(this.container);
            }
            this.emojis = [];
            this.container = null;
            this.animationFrame = null;
        }
    }

    function initAudios() {
        config.audioUrls.forEach(url => {
            const audio = new Audio(url);
            audio.preload = 'auto';
            audio.volume = 1;
            audioElements.push(audio);
        });
        unplayedAudioIndices = Array.from({length: audioElements.length}, (_, i) => i);
        playedAudioIndices = [];
    }

    function playRandomAudio() {
        if (audioElements.length === 0) return null;
        
        if (currentPlayingAudio) {
            currentPlayingAudio.pause();
            currentPlayingAudio.currentTime = 0;
            if (emojiRain) {
                emojiRain.destroy();
            }
        }

        if (unplayedAudioIndices.length === 0) {
            unplayedAudioIndices = [...playedAudioIndices];
            playedAudioIndices = [];
            console.log('所有音频已播放一遍,重新开始循环~');
        }

        const randomIndexInUnplayed = Math.floor(Math.random() * unplayedAudioIndices.length);
        const audioIndex = unplayedAudioIndices[randomIndexInUnplayed];
        
        unplayedAudioIndices.splice(randomIndexInUnplayed, 1);
        playedAudioIndices.push(audioIndex);

        currentPlayingAudio = audioElements[audioIndex];
        currentPlayingAudio.currentTime = 0;
        
        currentPlayingAudio.play().catch(error => {
            console.log('音频播放失败:', error);
            currentPlayingAudio = null;
            playedAudioIndices.pop();
            unplayedAudioIndices.push(audioIndex);
        });

        return currentPlayingAudio;
    }

    function getRandomPosition(element) {
        const elementWidth = element.offsetWidth || config.width;
        const elementHeight = element.offsetHeight || config.width;
        
        const maxX = window.innerWidth - elementWidth;
        const maxY = window.innerHeight - elementHeight;
        
        const minX = (config.moveRange.x[0] / 100) * maxX;
        const maxXLimited = (config.moveRange.x[1] / 100) * maxX;
        const minY = (config.moveRange.y[0] / 100) * maxY;
        const maxYLimited = (config.moveRange.y[1] / 100) * maxY;
        
        const x = Math.random() * (maxXLimited - minX) + minX;
        const y = Math.random() * (maxYLimited - minY) + minY;
        
        return { x, y };
    }

    function startRandomMove(element) {
        if (moveInterval) {
            clearInterval(moveInterval);
        }
        
        element.style.transition = `left ${config.moveSpeed / 1000}s cubic-bezier(0.34, 1.56, 0.64, 1), bottom ${config.moveSpeed / 1000}s cubic-bezier(0.34, 1.56, 0.64, 1)`;
        
        element.classList.add('moving');
        
        moveInterval = setInterval(() => {
            const { x, y } = getRandomPosition(element);
            element.style.left = `${x}px`;
            element.style.bottom = `${y}px`;
        }, config.moveSpeed);
    }

    function stopRandomMove(element) {
        if (moveInterval) {
            clearInterval(moveInterval);
            moveInterval = null;
        }
        
        element.style.transition = `left 0.8s ease-out, bottom 0.8s ease-out`;
        element.style.left = `${initialPosition.left}px`;
        element.style.bottom = `${initialPosition.bottom}px`;
        
        element.classList.remove('moving');
    }

    function injectStyles() {
        const style = document.createElement('style');
        style.textContent = `
            .kun {
                position: fixed;
                left: ${config.left}px;
                bottom: ${config.bottom}px;
                width: ${config.width}px;
                height: auto;
                cursor: pointer;
                z-index: 99999;
                filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.2));
                transition: transform 0.03s ease;
                transform-origin: center center;
                user-select: none;
            }
            .kun img {
                width: 100%;
                height: auto;
                display: block;
                border-radius: 8px;
                object-fit: cover;
                pointer-events: none;
            }
            /* 新增:💤 表情样式 */
            .kun .sleep-emoji {
                position: absolute;
                top: -20px;
                right: -10px;
                font-size: 32px;
                z-index: 100000;
                pointer-events: none;
                transition: opacity 0.3s ease;
                animation: float 2s ease-in-out infinite;
            }
            /* 运动/抖动状态隐藏 💤 表情 */
            .kun.shaking .sleep-emoji,
            .kun.moving .sleep-emoji {
                opacity: 0;
            }
            /* 静止状态显示 💤 表情 */
            .kun:not(.shaking):not(.moving) .sleep-emoji {
                opacity: 1;
            }
            .kun:active {
                transform: scale(0.9);
                filter: drop-shadow(0 3px 6px rgba(0, 0, 0, 0.25));
            }
            @keyframes strongShake {
                0%, 100% { transform: translate(0, 0) rotate(0deg) scale(1); }
                10% { transform: translate(45px, -35px) rotate(-10deg) scale(1.2); }
                20% { transform: translate(-40px, 30px) rotate(8deg) scale(0.85); }
                30% { transform: translate(38px, -28px) rotate(-7deg) scale(1.15); }
                40% { transform: translate(-35px, 25px) rotate(6deg) scale(0.9); }
                50% { transform: translate(32px, -22px) rotate(-5deg) scale(1.2); }
                60% { transform: translate(-28px, 20px) rotate(4deg) scale(0.85); }
                70% { transform: translate(25px, -18px) rotate(-6deg) scale(1.15); }
                80% { transform: translate(-22px, 16px) rotate(7deg) scale(0.9); }
                90% { transform: translate(20px, -15px) rotate(-8deg) scale(1.18); }
            }
            .kun.shaking {
                animation: strongShake ${config.shakeDuration}s ease-in-out infinite;
                filter: drop-shadow(0 12px 24px rgba(107, 125, 226, 0.6));
            }
            @keyframes float {
                0%, 100% { transform: translateY(0); }
                50% { transform: translateY(-8px); }
            }
            .kun:not(.shaking) {
                animation: float 3s ease-in-out infinite;
            }
            @keyframes lightningTrail {
                0% { filter: drop-shadow(0 0 10px rgba(66, 133, 244, 0.8)) 
                              drop-shadow(0 0 20px rgba(66, 133, 244, 0.6)) 
                              drop-shadow(0 0 30px rgba(66, 133, 244, 0.4)) 
                              blur(2px); }
                50% { filter: drop-shadow(0 0 15px rgba(66, 133, 244, 0.9)) 
                              drop-shadow(0 0 25px rgba(66, 133, 244, 0.7)) 
                              drop-shadow(0 0 35px rgba(66, 133, 244, 0.5)) 
                              blur(3px); }
                100% { filter: drop-shadow(0 0 10px rgba(66, 133, 244, 0.8)) 
                               drop-shadow(0 0 20px rgba(66, 133, 244, 0.6)) 
                               drop-shadow(0 0 30px rgba(66, 133, 244, 0.4)) 
                               blur(2px); }
            }
            .kun.moving {
                animation: lightningTrail 0.5s ease-in-out infinite;
                transform: translateZ(0);
                will-change: left, bottom, filter;
            }
            .kun.shaking.moving {
                animation: strongShake ${config.shakeDuration}s ease-in-out infinite,
                           lightningTrail 0.5s ease-in-out infinite;
                animation-composition: add;
            }
        `;
        document.head.appendChild(style);
    }

    function createElement() {
        const kunDiv = document.createElement('div');
        kunDiv.className = 'kun';
        kunDiv.id = 'shakable';

        const img = document.createElement('img');
        img.src = config.imgUrl;
        img.alt = '点击抖动图片';
        img.draggable = false;

        // 新增:创建 💤 表情元素
        const sleepEmoji = document.createElement('div');
        sleepEmoji.className = 'sleep-emoji';
        sleepEmoji.textContent = '💤';

        kunDiv.appendChild(img);
        kunDiv.appendChild(sleepEmoji); // 添加到容器中
        document.body.appendChild(kunDiv);

        return kunDiv;
    }

    function bindEvents(element) {
        let isAnimating = false;

        const handleTrigger = () => {
            if (isAnimating) return;

            const audio = playRandomAudio();
            if (!audio) return;

            isAnimating = true;
            element.classList.add('shaking');
            
            startRandomMove(element);
            
            if (!emojiRain) {
                emojiRain = new EmojiRain(config);
            }
            emojiRain.init();

            const onAudioEnd = () => {
                element.classList.remove('shaking');
                stopRandomMove(element);
                
                audio.removeEventListener('ended', onAudioEnd);
                isAnimating = false;
                currentPlayingAudio = null;
                
                if (emojiRain) {
                    emojiRain.destroy();
                }
                
                if (typeof config.onShakeEnd === 'function') {
                    config.onShakeEnd();
                }
            };

            audio.addEventListener('ended', onAudioEnd);
        };

        element.addEventListener('click', handleTrigger);

        element.addEventListener('contextmenu', (e) => {
            e.preventDefault();
        });

        element.addEventListener('dragstart', (e) => {
            e.preventDefault();
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            initAudios();
            injectStyles();
            const shakableElement = createElement();
            bindEvents(shakableElement);
        });
    } else {
        initAudios();
        injectStyles();
        const shakableElement = createElement();
        bindEvents(shakableElement);
    }

    return shakableElement;
}

initKunShake({
    width: 150,
    left: 20,
    bottom: 20,
    shakeDuration: 0.6,
    emojiCount: 50,
    emojiSizeRange: [30, 58],
    fallSpeedRange: [3, 8],
    swingRange: [15, 40],
    emojis: ['🏀', '🎵', '🐔', '🎤', '🥚'],
    moveSpeed: 300,
    moveRange: { x: [0, 100], y: [0, 100] },
    onShakeEnd: () => {
        console.log('抖动结束啦!回归原位~');
    }
});

https://wang618.cn/

左下角哥哥

本站服务器由 DangYun 友情提供
已有评论 ( 0 )
提示:您必须 登录 才能查看此内容。
域名市场
   域名载入中...
创建新帖
自助推广 (点击空位或 这里 添加)
确认删除
确定要删除这篇帖子吗?删除后将无法恢复。
删除成功
帖子已成功删除,页面将自动刷新。
删除失败
删除帖子时发生错误,请稍后再试。