DoniaHub
DoniaHub
python_hideman/index.html
Download ZIP
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>doniahub</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@600;800;900&family=Montserrat:wght@500;700&display=swap" rel="stylesheet">
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/camera_utils/camera_utils.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/selfie_segmentation/selfie_segmentation.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/hands/hands.js" crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/@mediapipe/drawing_utils/drawing_utils.js" crossorigin="anonymous"></script>

  <style>*{box-sizing:border-box;margin:0;padding:0;user-select:none}html,body{width:100vw;height:100vh;overflow:hidden;background:transparent;margin:0;padding:0}video{display:none}canvas#outputCanvas{position:fixed;top:0;left:0;width:100vw;height:100vh;object-fit:cover;transform:scaleX(-1);z-index:1}.watermark{position:fixed;bottom:25px;right:35px;z-index:10;font-family:'Orbitron',sans-serif;font-weight:800;font-size:1.45rem;letter-spacing:3px;text-transform:uppercase;background:linear-gradient(135deg,#ffffff 0%,#00ffcc 60%,#ff007f 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;filter:drop-shadow(0 0 12px rgba(0,255,204,0.6));pointer-events:none;opacity:.88;transition:opacity .3s ease}.watermark span{font-family:'Montserrat',sans-serif;font-weight:500;font-size:.85rem;letter-spacing:4px;display:block;color:rgba(255,255,255,0.8);text-align:right;-webkit-text-fill-color:initial;text-shadow:0 0 10px rgba(0,255,204,0.4)}</style>
</head>
<body>

  <video id="webcam" playsinline autoplay muted></video>
  <canvas id="outputCanvas"></canvas>

  <div class="watermark">
    DoniaHub
  </div>

  <script>
    const video = document.getElementById('webcam');
    const canvas = document.getElementById('outputCanvas');
    const ctx = canvas.getContext('2d');

    const bgCanvas = document.createElement('canvas');
    const bgCtx = bgCanvas.getContext('2d');

    let bgCaptured = false;
    let invisibleMode = false;
    let lastToggleTime = 0;
    const debounceDelay = 750;

    let pinchStartTime = 0;
    let pinchTriggered = false;
    const PINCH_HOLD_DURATION = 120;

    let cloakProgress = 0.0;
    const DISSOLVE_SPEED = 0.085;

    let latestHandResults = null;

    const hands = new Hands({
      locateFile: (file) => `https://cdn.jsdelivr.net/npm/@mediapipe/hands/${file}`,
    });
    hands.setOptions({
      maxNumHands: 2,
      modelComplexity: 1,
      minDetectionConfidence: 0.65,
      minTrackingConfidence: 0.65,
    });
    hands.onResults((results) => {
      latestHandResults = results;
    });

    async function startCamera() {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({
          video: {
            width: { ideal: 1920 },
            height: { ideal: 1080 },
            facingMode: 'user'
          },
          audio: false,
        });
        video.srcObject = stream;
        video.onloadedmetadata = () => {
          video.play();
          canvas.width = video.videoWidth;
          canvas.height = video.videoHeight;
          bgCanvas.width = video.videoWidth;
          bgCanvas.height = video.videoHeight;

          const camera = new Camera(video, {
            onFrame: async () => {
              await hands.send({ image: video });
            },
            width: video.videoWidth,
            height: video.videoHeight,
          });
          camera.start();

          setTimeout(() => {
            captureBackground();
          }, 1200);

          requestAnimationFrame(renderLoop);
        };
      } catch (err) {
        console.error("Camera access error:", err);
      }
    }

    function captureBackground() {
      bgCtx.drawImage(video, 0, 0, bgCanvas.width, bgCanvas.height);
      bgCaptured = true;
      console.log("[doniahub] Background captured successfully.");
    }

    function checkPinchGesture(handResults, width, height, now) {
      if (!handResults || !handResults.multiHandLandmarks || handResults.multiHandLandmarks.length === 0) {
        pinchStartTime = 0;
        pinchTriggered = false;
        return { isPinching: false, pinchX: 0, pinchY: 0, progress: 0, readyToToggle: false };
      }

      for (const lm of handResults.multiHandLandmarks) {
        const palmSize = Math.hypot((lm[0].x - lm[9].x) * width, (lm[0].y - lm[9].y) * height);
        if (palmSize < 15) continue;

        const dx = (lm[4].x - lm[8].x) * width;
        const dy = (lm[4].y - lm[8].y) * height;
        const tipDist = Math.hypot(dx, dy);

        const relativeRatio = tipDist / palmSize;

        const zDiff = Math.abs(lm[4].z - lm[8].z);

        if (relativeRatio < 0.22 && zDiff < 0.08) {
          const pinchX = (lm[4].x + lm[8].x) * 0.5 * width;
          const pinchY = (lm[4].y + lm[8].y) * 0.5 * height;

          if (pinchStartTime === 0) {
            pinchStartTime = now;
          }

          const elapsed = now - pinchStartTime;
          const progress = Math.min(1.0, elapsed / PINCH_HOLD_DURATION);
          const readyToToggle = progress >= 1.0 && !pinchTriggered;

          if (readyToToggle) {
            pinchTriggered = true;
          }

          return { isPinching: true, pinchX, pinchY, progress, readyToToggle };
        }
      }

      pinchStartTime = 0;
      pinchTriggered = false;
      return { isPinching: false, pinchX: 0, pinchY: 0, progress: 0, readyToToggle: false };
    }

    function drawHandSkeleton(ctx, handResults, width, height) {
      if (!handResults || !handResults.multiHandLandmarks) return;

      for (const landmarks of handResults.multiHandLandmarks) {
        drawConnectors(ctx, landmarks, HAND_CONNECTIONS, {
          color: '#ff007f',
          lineWidth: 4,
        });

        for (const lm of landmarks) {
          const x = lm.x * width;
          const y = lm.y * height;

          ctx.save();
          ctx.beginPath();
          ctx.arc(x, y, 5, 0, 2 * Math.PI);
          ctx.fillStyle = '#00ffcc';
          ctx.shadowColor = '#00ffcc';
          ctx.shadowBlur = 12;
          ctx.fill();
          ctx.restore();
        }
      }
    }

    function renderLoop() {
      const now = performance.now();
      const w = canvas.width;
      const h = canvas.height;

      const pinchState = checkPinchGesture(latestHandResults, w, h, now);

      if (pinchState.readyToToggle) {
        if (now - lastToggleTime > debounceDelay) {
          if (!bgCaptured) {
            captureBackground();
          }
          invisibleMode = !invisibleMode;
          lastToggleTime = now;
        }
      }

      const targetProgress = invisibleMode ? 1.0 : 0.0;
      cloakProgress += (targetProgress - cloakProgress) * DISSOLVE_SPEED;
      if (Math.abs(targetProgress - cloakProgress) < 0.005) {
        cloakProgress = targetProgress;
      }

      const easeAlpha = 0.5 - 0.5 * Math.cos(cloakProgress * Math.PI);

      if (easeAlpha >= 0.998 && bgCaptured) {
        ctx.clearRect(0, 0, w, h);
        ctx.drawImage(bgCanvas, 0, 0, w, h);
      } else if (easeAlpha <= 0.002) {
        ctx.clearRect(0, 0, w, h);
        ctx.drawImage(video, 0, 0, w, h);
      } else if (bgCaptured) {
        ctx.clearRect(0, 0, w, h);
        ctx.drawImage(bgCanvas, 0, 0, w, h);

        ctx.save();
        ctx.globalAlpha = 1.0 - easeAlpha;
        ctx.drawImage(video, 0, 0, w, h);
        ctx.restore();
      }

      drawHandSkeleton(ctx, latestHandResults, w, h);

      requestAnimationFrame(renderLoop);
    }

    window.addEventListener('keydown', (e) => {
      if (e.key === 'b' || e.key === 'B') {
        captureBackground();
      } else if (e.key === 't' || e.key === 'T' || e.key === ' ') {
        invisibleMode = !invisibleMode;
      }
    });

    window.addEventListener('DOMContentLoaded', startCamera);
  </script>
</body>
</html>