mediapipe实现实时摄像头手指把脸嘴巴拉长拉大橡胶特效互动代码

代码语言:html

所属分类:其他

代码描述:mediapipe实现实时摄像头手指把脸嘴巴拉长拉大橡胶特效互动代码

代码标签: mediapipe 实时 摄像头 手指 把脸 嘴巴 拉长 拉大 橡胶 特效 互动 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>橡胶果实实时拉脸特效</title>
    <!-- 引入 Tailwind CSS 构建现代动漫风面板 -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- 引入 MediaPipe 核心库 -->
    <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/@mediapipe/face_mesh/face_mesh.js" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>

    <style>
        body {
            background-color: #1a1a2e;
            font-family: 'PingFang SC', system-ui, -apple-system, sans-serif;
        }
        /* 动漫黑框描边风格 */
        .anime-border {
            border: 4px solid #000;
            box-shadow: 6px 6px 0px #000;
        }
        .anime-btn {
            border: 3px solid #000;
            box-shadow: 3px 3px 0px #000;
            transition: all 0.1s ease;
        }
        .anime-btn:active {
            transform: translate(2px, 2px);
            box-shadow: 1px 1px 0px #000;
        }
    </style>
</head>
<body class="min-h-screen flex flex-col items-center justify-center p-4 md:p-8 text-white">

    <!-- 顶部标题 -->
    <div class="text-center mb-6 max-w-lg">
        <h1 class="text-3xl md:text-4xl font-extrabold text-[#FFD369] tracking-wider uppercase" style="text-shadow: 3px 3px 0px #000, -1px -1px 0px #000;">
            GOMU GOMU NO...
        </h1>
        <p class="text-xs md:text-sm text-gray-400 mt-2 font-medium">
            实时动作捕捉:用单手食指和拇指“捏住”嘴角或脸颊往外拉,松开瞬间触发橡胶回弹!
        </p>
    </div>

    <!-- 核心区域 -->
    <div class="grid grid-cols-1 lg:grid-cols-12 gap-8 max-w-6xl w-full items-center">
        
        <!-- 左侧:画布与摄像头容器 -->
        <div class="lg:col-span-8 flex flex-col items-center">
            <div class="relative anime-border bg-black rounded-lg overflow-hidden w-[640px] max-w-full aspect-[4/3]">
                <!-- 主渲染画面 -->
                <canvas id="outputCanvas" width="640" height="480" class="w-full h-full block transform scale-x-1"></canvas>
                
                <!-- 隐藏的原始视频流(用于 MediaPipe 帧捕获) -->
                <video id="webcam" autoplay playsinline class="absolute top-0 left-0 w-0 h-0 opacity-0"></video>
                
                <!-- 初始化遮罩/加载提示 -->
                <div id="loader" class="absolute inset-0 bg-[#16213e]/95 flex flex-col items-center justify-center z-10 transition-opacity duration-500">
                    <div class="w-12 h-12 border-4 border-[#FF9F1C] border-t-transparent rounded-full animate-spin mb-4"></div>
                    <p class="text-sm font-semibold tracking-wider text-yellow-400">正在载入 MediaPipe 视觉模型与初始化相机...</p>
                    <p class="text-xs text-gray-400 mt-2">首次加载可能需要10-20秒,请在浏览器弹窗中允许相机权限</p>
                </div>
            </div>
        </div>

        <!-- 右侧:交互参数与指示说明 -->
        <div class="lg:col-span-4 space-y-6">
            <!-- 指导卡片 -->
            <div class="bg-[#16213e] anime-border p-5 rounded-lg space-y-4">
                <h3 class="text-lg font-bold text-[#FF9F1C] border-b-2 border-black pb-2">💡 动作秘籍</h3>
                <ul class="text-xs space-y-3 text-gray-300 leading-relaxed">
                    <li class="flex items-start gap-2">
                        <span class="bg-[#FF9F1C] text-black font-extrabold text-[10px] px-1.5 py-0.5 rounded">步骤 1</span>
                        <span>正对摄像头,伸手靠近自己的<b class="text-[#FF9F1C]">左/右嘴角</b>或<b class="text-[#FF9F1C]">脸颊</b>。</span>
                    </li>
                    <li class="flex items-start gap-2">
                        <span class="bg-[#FF9F1C] text-black font-extrabold text-[10px] px-1.5 py-0.5 rounded">步骤 2</span>
                        <span>将拇指与食指捏合(Pinch 动作),此时画面会锁定锚点。</span>
                    </li>
                    <li class="flex items-start gap-2">
                        <span class="bg-[#FF9F1C] text-black font-extrabold text-[10px] px-1.5 py-0.5 rounded">步骤 3</span>
                        <span>按住不放往外拉,脸部像素会跟随手平滑变形。</span>
                    </li>
                    <li class="flex items-start gap-2">
                        <span class="bg-[#FF9F1C] text-black font-extrabold text-[10px] px-1.5 py-0.5 rounded">步骤 4</span>
                        <span>松开手指,体验弹性物理抖动回弹!</span>
                    </li>
                </ul>
            </div>

            <!-- 参数微调 -->
            <div class="bg-[#16213e] anime-border p-5 rounded-lg space-y-4">
                <h3 class="text-md font-bold text-white">⚙️ 橡胶参数调节</h3>
                
                <div class="space-y-1">
                    <div class="flex justify-between text-xs text-gray-400">
                        <span>橡胶回弹硬度 (Stiffness)</span>
                        <span id="stiffness-val">0.18</span>
                    </div>
                    <input type="range" id="stiffness-slider" min="0.05" max="0.4" step="0.01" value="0.18" class="w-full accent-yellow-400" />
                </div>

                <div class="space-y-1">
                    <div class="flex justify-between text-xs text-gray-400">
                        <span>回弹空气阻尼 (Damping)</span>
                        <span id="damping-val">0.72</span>
                    </div>
                    <input type="range" id="damping-slider" min="0.5" max="0.95" step="0.01" value="0.72" class="w-full accent-yellow-400" />
                </div>

                <div class="space-y-1">
                    <div class="flex justify-between text-xs text-gray-400">
                        <span>影响半径 (Influence Radius)</span>
                        <span id="radius-val">120px</span>
                    </div>
                    <input type="range" id="radius-slider" min="60" max="200" step="5" value="120" class="w-full accent-yellow-400" />
                </div>
            </div>

            <div class="text-center">
                <button onclick="toggleAudio()" id="audio-btn" class="anime-btn w-full bg-[#FF9F1C] text-black font-bold py-2.5 rounded-lg text-sm">
                    🔊 卡通音效: 已开启
                </button>
            </div>
        </div>

    </div>

<script>
    // --- 页面配置与交互参数 ---
    const width = 640;
    const height = 480;

    let stiffness = 0.18;
    let damping = 0.72;
    let influenceRadius = 120;
    let audioEnabled = true;

    // 滑块数据联动
    document.getElementById('stiffness-slider').addEventListener('input', (e) => {
        stiffness = parseFloat(e.target.value);
        document.getElementById('stiffness-val').innerText = stiffness;
    });
    document.getElementById('damping-slider').addEventListener('input', (e) => {
        damping = parseFloat(e.target.value);
        document.getElementById('damping-val').innerText = damping;
    });
    document.getElementById('radius-slider').addEventListener('input', (e) => {
        influenceRadius = parseInt(e.target.value);
        document.getElementById('radius-val').innerText = influenceRadius + 'px';
    });

    function toggleAudio() {
        audioEnabled = !audioEnabled;
        document.getElementById('audio-btn').innerText = audioEnabled ? "🔊 卡通音效: 已开启" : "🔇 卡通音效: 已关闭";
    }

    // --- 仿射仿生网格(Grid Warp) 物理引擎设置 ---
    const GRID_COLS = 16;
    const GRID_ROWS = 12;

    let srcGrid = [];
    let dstGrid = [];

    // 初始化坐标网格
    for (let r = 0; r <= GRID_ROWS; r++) {
        srcGrid[r] = [];
        dstGrid[r] = [];
        for (let c = 0; c <= GRID_COLS; c++) {
            const gx = c * (width / GRID_COLS);
            const gy = r * (height / GRID_ROWS);
            srcGrid[r][c] = { x: gx, y: gy };
            dstGrid[r][c] = { x: gx, y: gy };
        }
    }

    // 重置变形目标网格
    function resetDstGrid() {
        for (let r = 0; r <= GRID_ROWS; r++) {
            for (let c = 0; c <= GRID_COLS; c++) {
                dstGrid[r][c].x = srcGrid[r][c].x;
                dstGrid[r][c].y = srcGrid[r][c].y;
            }
        }
    }

    // --- 2D仿射三角形渲染贴图算法 ---
    function drawTriangle(ctx, img, sx0, sy0, sx1, sy1, sx2, sy2, dx0, dy0, dx1, dy1, dx2, dy2) {
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(dx0, dy0);
        ctx.lineTo(dx1, dy1);
        ctx.lineTo(dx2, dy2);
        ctx.closePath();
        ctx.clip(); // 限制渲染输出为三角形闭合区间

        // 求解单应/仿射变换矩阵系数 (基于三角形两组顶点的解析解)
        const denom = sx0 * (sy1 - sy2) - sy0 * (sx1 - sx2) + sx1 * sy2 - sx2 * sy1;
        if (Math.abs(denom) < 0.001) {
            ctx.restore();
            return;
        }

        const a = (dx0 * (sy1 - sy2) - sy0 * (dx1 - dx2) + dx1 * sy2 - dx2 * sy1) / denom;
        const c = (sx0 * (dx1 - dx2) - dx0 * (sx1 - sx2) + sx1 * dx2 - sx2 * dx1) / denom;
        const e = (sx0 * (sy1 * dx2 - sy2 * dx1) - sy0 * (sx1 * dx2 - sx2 * dx1) + (sx1 * sy2 - sx2 * sy1) * dx0) / denom;

        const b = (dy0 * (sy1 - sy2) - sy0 * (dy1 - dy2) + dy1 * sy2 - dy2 * sy1) / denom;
        const d = (sx0 * (dy1 - dy2) - dy0 * (sx1 - sx2) + sx1 * dy2 - sx2 * dy1) / denom;
        const f = (sx0 * (sy1 * dy2 - sy2 * dy1) - sy0 * (sx1 * dy2 - sx2 * dy1) + (sx1 * sy2 - sx2 * sy1) * dy0) / denom;

        ctx.transform(a, b, c, d, e, f);
        ctx.drawImage(img, 0, 0);
        ctx.restore();
    }

    // --- 卡通物理回弹音效 (Web Audio API 纯程序合成) ---
    let audioCtx = null;
    function playBoingSound() {
        if (!audioEnabled) return;
        try {
            if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            if (audioCtx.state === 'suspended') audioCtx.resume();

            const now = audioCtx.currentTime;
            const osc = audioCtx.createOscillator();
            con.........完整代码请登录后点击上方下载按钮下载查看

网友评论0