07月25, 2025

css3磁吸立体卡片

磁吸立体卡片

录屏2025-07-25 09.gif

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>磁吸立体卡片 - @xiaoyierle</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #e8e8e8 0%, #d1d1d1 50%, #c8c8c8 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
            overflow: hidden;
        }

        /* === 磁吸区域 === */
        .magnetic-area {
            width: 600px;
            height: 600px;
            display: flex;
            align-items: center;
            justify-content: center;
            position: relative;
        }

        /* === 卡片容器 === */
        .card {
            width: 320px;
            height: 450px;
            perspective: 1000px;
            cursor: pointer;
            position: relative;
            transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
            filter: drop-shadow(0 25px 50px rgba(0, 0, 0, 0.15));
        }

        .card:hover {
            filter: drop-shadow(0 35px 70px rgba(0, 0, 0, 0.25));
        }

        /* === 扩展控制区域 === */
        .card-control-area {
            position: absolute;
            top: -30px;
            left: -30px;
            right: -30px;
            bottom: -30px;
            pointer-events: all;
            z-index: 50;
        }

        /* === 卡片内容 === */
        .card-content {
            position: relative;
            width: 100%;
            height: 100%;
            border-radius: 24px;
            background: linear-gradient(145deg, #1a1a1a, #000);
            transform-style: preserve-3d;
            transition: transform 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
            overflow: hidden;
            border: 1px solid rgba(255, 255, 255, 0.1);
            box-shadow: 
                inset 0 1px 0 rgba(255, 255, 255, 0.1),
                inset 0 -1px 0 rgba(0, 0, 0, 0.5);
            pointer-events: all;
        }

        /* 卡片纹理 */
        .card-content::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: 
                repeating-linear-gradient(
                    45deg,
                    transparent,
                    transparent 1px,
                    rgba(255, 255, 255, 0.02) 1px,
                    rgba(255, 255, 255, 0.02) 2px
                );
            pointer-events: none;
            z-index: 1;
        }

        /* === 图层样式 === */
        .card-layer {
            position: absolute;
            inset: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: transform 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94);
        }

        /* 背景图片 */
        .card-bg-image {
            width: 100%;
            height: 100%;
            object-fit: cover;
            border-radius: 24px;
            transform: translateZ(-50px) scale(1.1);
            filter: contrast(1.2) brightness(0.9) saturate(0.8);
            transition: transform 0.15s cubic-bezier(0.25, 0.46, 0.45, 0.94);
        }

        /* 文字样式 */
        .card-text {
            color: #fff;
            font-weight: 600;
            text-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
            letter-spacing: 0.5px;
        }

        .title {
            align-items: flex-end;
            justify-content: flex-start;
            padding: 0 0 28px 28px;
            font-size: 20px;
            transform: translateZ(40px);
        }

        .code {
            align-items: flex-end;
            justify-content: flex-end;
            padding: 0 28px 28px 0;
            font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', monospace;
            font-size: 20px; /* 与title保持一致 */
            font-weight: 600; /* 与title保持一致 */
            color: rgba(255, 255, 255, 0.9); /* 稍微提亮 */
            transform: translateZ(40px);
        }

        /* 图标样式 */
        .icon {
            width: 32px;
            height: 32px;
            fill: rgba(255, 255, 255, 0.9);
            filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
            transition: all 0.2s ease;
        }

        .icon:hover {
            fill: #fff; /* 保持白色,不变蓝 */
            transform: scale(1.1);
        }

        .icon-top-right {
            align-items: flex-start;
            justify-content: flex-end;
            padding: 28px;
            transform: translateZ(50px);
            cursor: pointer;
            z-index: 100;
        }

        .icon-top-right:hover .icon {
            fill: #fff; /* 保持白色 */
            transform: scale(1.2);
        }

        /* 二维码样式 */
        .qr-code {
            width: 120px;
            height: 120px;
            border-radius: 12px;
            border: 3px solid rgba(255, 255, 255, 0.9);
            box-shadow: 
                0 8px 25px rgba(0, 0, 0, 0.3),
                inset 0 1px 0 rgba(255, 255, 255, 0.8);
            transform: translateZ(80px);
            filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.4));
            animation: qrCodeFloat 4s ease-in-out infinite;
        }

        @keyframes qrCodeFloat {
            0%, 100% { 
                transform: translateZ(80px) scale(1);
                filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.4));
            }
            50% { 
                transform: translateZ(80px) scale(1.05);
                filter: drop-shadow(0 0 25px rgba(255, 255, 255, 0.6));
            }
        }

        /* === 增强的光泽效果 === */
        .card-glare {
            position: absolute;
            width: 250px; /* 增大光圈 */
            height: 250px;
            background: radial-gradient(
                circle, 
                rgba(255, 255, 255, 0.6) 0%,  /* 增强中心亮度 */
                rgba(255, 255, 255, 0.3) 20%, /* 增强内圈亮度 */
                rgba(255, 255, 255, 0.1) 40%,
                rgba(255, 255, 255, 0.05) 60%,
                transparent 80%
            );
            mix-blend-mode: overlay;
            opacity: 0;
            transform: translate(-50%, -50%) scale(1.5);
            transition: opacity 0.2s ease;
            pointer-events: none;
            z-index: 15;
            border-radius: 50%;
        }

        /* === 磁吸指示器 === */
        .magnetic-indicator {
            position: absolute;
            width: 6px; /* 稍微增大 */
            height: 6px;
            background: rgba(255, 255, 255, 0.9); /* 改为白色 */
            border-radius: 50%;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.2s ease;
            z-index: 20;
        }

        .magnetic-indicator.active {
            opacity: 1;
            animation: magneticPulse 1s ease-in-out infinite;
        }

        @keyframes magneticPulse {
            0%, 100% { 
                transform: scale(1);
                box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7); /* 白色脉动 */
            }
            50% { 
                transform: scale(1.5);
                box-shadow: 0 0 0 12px rgba(255, 255, 255, 0); /* 增强脉动范围 */
            }
        }

        /* === 性能优化 === */
        .card-content,
        .card-layer,
        .card-bg-image {
            will-change: transform;
        }

        /* === 响应式 === */
        @media (max-width: 480px) {
            .magnetic-area {
                width: 400px;
                height: 400px;
            }

            .card {
                width: 280px;
                height: 390px;
            }

            .title {
                font-size: 18px;
                padding: 0 0 24px 24px;
            }

            .code {
                font-size: 18px;
                padding: 0 24px 24px 0;
            }

            .icon-top-right {
                padding: 24px;
            }

            .icon {
                width: 28px;
                height: 28px;
            }

            .qr-code {
                width: 100px;
                height: 100px;
            }
        }
    </style>
<meta name="code-type" content="html">
</head>
<body>
    <div class="magnetic-area">
        <!-- 磁吸指示器 -->
        <div class="magnetic-indicator"></div>

        <div class="card">
            <!-- 扩展控制区域 -->
            <div class="card-control-area"></div>

            <div class="card-content">
                <!-- 背景图片层 -->
                <div class="card-layer">
                    <img class="card-bg-image" src="https://personaltailor.oss-cn-beijing.aliyuncs.com/blog/20250725/upload_5d3f1e6e40a8b8d13a12564118a74207" alt="xiaoyierle Background">
                </div>

                <!-- 光泽效果层 -->
                <div class="card-glare"></div>

                <!-- 文字层 -->
                <div class="card-layer title">
                    <span class="card-text">@xiaoyierle</span>
                </div>
                <div class="card-layer code">
                    <span class="card-text"></span>
                </div>

                <!-- Home图标层 - 移到右上角 -->
                <div class="card-layer icon-top-right">
                    <svg class="icon" viewBox="0 0 24 24">
                        <path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"/>
                    </svg>
                </div>

                <!-- 二维码层 -->
                <div class="card-layer">
                    <img class="qr-code" src="https://personaltailor.oss-cn-beijing.aliyuncs.com/blog/20250725/upload_a7c8b6e5463f25daf54e35aebf1e7a5d" alt="xiaoyierle QR Code">
                </div>
            </div>
        </div>
    </div>

    <script>
        const magneticArea = document.querySelector('.magnetic-area');
        const card = document.querySelector('.card');
        const content = document.querySelector('.card-content');
        const controlArea = document.querySelector('.card-control-area');
        const indicator = document.querySelector('.magnetic-indicator');
        const homeIcon = document.querySelector('.icon-top-right');

        // 获取所有图层
        const layers = {
            title: document.querySelector('.title'),
            code: document.querySelector('.code'),
            iconTopRight: document.querySelector('.icon-top-right'),
            qrCode: document.querySelector('.qr-code'),
            bgImage: document.querySelector('.card-bg-image'),
            glare: document.querySelector('.card-glare')
        };

        // 配置参数
        const config = {
            // 磁吸配置
            magneticStrength: 0.3,
            magneticRadius: 200,

            // 旋转配置
            maxRotate: 25,
            rotateMultiplier: 1.5,

            // 视差配置
            sensitivity: {
                text: 12,
                iconSmall: 16,
                qrCode: 20,
                background: -8
            }
        };

        let isInMagneticField = false;
        let isHovering = false;
        let animationFrame;

        // 计算距离
        function getDistance(x1, y1, x2, y2) {
            return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
        }

        // 磁吸效果
        function applyMagneticEffect(mouseX, mouseY) {
            const areaRect = magneticArea.getBoundingClientRect();
            const cardRect = card.getBoundingClientRect();

            const areaCenterX = areaRect.left + areaRect.width / 2;
            const areaCenterY = areaRect.top + areaRect.height / 2;

            const cardCenterX = cardRect.left + cardRect.width / 2;
            const cardCenterY = cardRect.top + cardRect.height / 2;

            const distance = getDistance(mouseX, mouseY, areaCenterX, areaCenterY);

            if (distance < config.magneticRadius) {
                isInMagneticField = true;

                const magneticForce = (config.magneticRadius - distance) / config.magneticRadius;
                const pullStrength = magneticForce * config.magneticStrength;

                const deltaX = (mouseX - cardCenterX) * pullStrength;
                const deltaY = (mouseY - cardCenterY) * pullStrength;

                card.style.transform = `translate(${deltaX}px, ${deltaY}px)`;

                indicator.style.left = `${mouseX - areaRect.left}px`;
                indicator.style.top = `${mouseY - areaRect.top}px`;
                indicator.classList.add('active');

            } else {
                isInMagneticField = false;
                card.style.transform = 'translate(0px, 0px)';
                indicator.classList.remove('active');
            }
        }

        // 更新卡片变换
        function updateCardTransforms(mouseX, mouseY) {
            if (!isHovering) return;

            const rotateY = mouseX * config.maxRotate * config.rotateMultiplier;
            const rotateX = -mouseY * config.maxRotate * config.rotateMultiplier;

            const scale = 1 + (Math.abs(mouseX) + Math.abs(mouseY)) * 0.02;

            content.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(${scale})`;

            const { sensitivity } = config;
            layers.title.style.transform = `translateZ(40px) translateX(${mouseX * sensitivity.text}px) translateY(${mouseY * sensitivity.text}px)`;
            layers.code.style.transform = `translateZ(40px) translateX(${mouseX * sensitivity.text}px) translateY(${mouseY * sensitivity.text}px)`;
            layers.iconTopRight.style.transform = `translateZ(50px) translateX(${mouseX * sensitivity.iconSmall}px) translateY(${mouseY * sensitivity.iconSmall}px)`;
            layers.qrCode.style.transform = `translateZ(80px) translateX(${mouseX * sensitivity.qrCode}px) translateY(${mouseY * sensitivity.qrCode}px)`;
            layers.bgImage.style.transform = `translateZ(-50px) scale(1.1) translateX(${mouseX * sensitivity.background}px) translateY(${mouseY * sensitivity.background}px)`;
        }

        // 全局鼠标移动事件
        document.addEventListener('mousemove', (e) => {
            if (animationFrame) {
                cancelAnimationFrame(animationFrame);
            }

            animationFrame = requestAnimationFrame(() => {
                applyMagneticEffect(e.clientX, e.clientY);

                if (isHovering) {
                    const cardRect = card.getBoundingClientRect();
                    const x = e.clientX - cardRect.left;
                    const y = e.clientY - cardRect.top;

                    const { width, height } = cardRect;
                    const mouseX = (x / width) - 0.5;
                    const mouseY = (y / height) - 0.5;

                    updateCardTransforms(mouseX, mouseY);

                    layers.glare.style.left = `${x}px`;
                    layers.glare.style.top = `${y}px`;
                    layers.glare.style.opacity = '1';
                }
            });
        });

        // 扩展控制区域鼠标事件
        controlArea.addEventListener('mouseenter', () => {
            isHovering = true;
        });

        controlArea.addEventListener('mouseleave', () => {
            isHovering = false;

            if (animationFrame) {
                cancelAnimationFrame(animationFrame);
            }

            content.style.transform = 'rotateX(0deg) rotateY(0deg) scale(1)';
            layers.title.style.transform = 'translateZ(40px)';
            layers.code.style.transform = 'translateZ(40px)';
            layers.iconTopRight.style.transform = 'translateZ(50px)';
            layers.qrCode.style.transform = 'translateZ(80px)';
            layers.bgImage.style.transform = 'translateZ(-50px) scale(1.1)';
            layers.glare.style.opacity = '0';
        });

        // 卡片点击事件
        card.addEventListener('click', (e) => {
            // 如果点击的是Home图标,不执行卡片点击
            if (e.target.closest('.icon-top-right')) {
                return;
            }

            // 点击反馈效果
            content.style.transform = 'rotateX(0deg) rotateY(0deg) scale(0.95)';
            setTimeout(() => {
                content.style.transform = 'rotateX(0deg) rotateY(0deg) scale(1)';
                console.log('点击卡片');
            }, 150);
        });

        // Home图标点击事件
        homeIcon.addEventListener('click', (e) => {
            e.stopPropagation(); // 阻止事件冒泡

            // 图标点击效果
            const icon = homeIcon.querySelector('.icon');
            icon.style.transform = 'scale(0.9)';
            setTimeout(() => {
                icon.style.transform = 'scale(1.1)';
                // 打开Twitter页面
                console.log('点击卡片');
            }, 100);
        });

        // 防止选中文字
        card.addEventListener('selectstart', (e) => {
            e.preventDefault();
        });

        // 离开磁吸区域时重置
        magneticArea.addEventListener('mouseleave', () => {
            isInMagneticField = false;
            card.style.transform = 'translate(0px, 0px)';
            indicator.classList.remove('active');
        });
    </script>
</body>
</html>

本文链接:https://587v5.com/post/css3-ci-xi-li-ti-ka-pian.html

Comments