// facedock.js // Code for face cursor and docking to certain elements const { value, everyFrame } = window.popmotion; const cursorD = 88; const cursor = document.createElement('div'); document.querySelector('body').appendChild(cursor); cursor.style.width = cursorD + "px"; cursor.style.height = cursorD + "px"; cursor.style.borderRadius = (cursorD / 2) + "px"; cursor.style.background = "#000000"; cursor.style.position = "fixed"; cursor.style.zIndex = 2; cursor.style.pointerEvents = "none"; cursor.style.overflow = "hidden"; cursor.style.transform = "scale(0)"; cursor.style.transition = "transform .25s"; cursor.style.transitionTimingFunction = "cubic-bezier(0.32, 0.08, 0.24, 1)"; const video = document.createElement('video'); cursor.appendChild(video); video.autoplay = true; video.style.position = "absolute"; video.style.width = "100%"; // most webcams are landscape video.style.height = "100%"; // most webcams are landscape video.style.objectFit = "cover"; video.style.transform = "scaleX(-1)"; video.style.borderRadius = 1; // Default video before demo/for mobile const facedockVideo = document.getElementById("facedock-video"); const dock = document.querySelector(".facedock"); const toggle = document.getElementById("facedock-toggle"); var toggleLabel = toggle.innerText; var demoOn = false; toggle.addEventListener("click", e => { if (!demoOn) { demoOn = true; if (navigator.mediaDevices.getUserMedia) { navigator.mediaDevices.getUserMedia({ video: true }) .then(function (stream) { video.srcObject = stream; cursor.style.transform = "scale(1)"; dock.style.transform = "scale(1)"; facedockVideo.style.opacity = 0; toggle.innerText = "Stop demo"; }) .catch(function (error) { console.log("Video stream issue"); }) } } else { demoOn = false; cursor.style.transform = "scale(0)"; dock.style.transform = "scale(0)"; setTimeout(() => { video.srcObject = null; facedockVideo.style.opacity = 1; toggle.innerText = toggleLabel; }, 500); } }); const cursorStyler = styler(cursor); cursorStyler.set({ top: 0, left: 0, }) const springDamping = { stiffness: 345, damping: 28, } var dockProgress = 0; var mouseX = 0; var mouseY = 0; // strategy: use 0-1 spring animation to map between mouse pos and prev position function transition(start, end) { function convertRange(value, r1, r2) { return (value - r1[0]) * (r2[1] - r2[0]) / (r1[1] - r1[0]) + r2[0]; } return convertRange(dockProgress, [0, 1], [start, end]); } everyFrame().start(t => { if (!demoOn) { return; } let dockX = dock.getBoundingClientRect().left; let dockY = dock.getBoundingClientRect().top; let x = transition(mouseX + 32, dockX); let y = transition(mouseY + 32, dockY); cursor.style.left = x + "px"; cursor.style.top = y + "px"; let w = transition(cursorD, dock.clientWidth) + 4; let h = transition(cursorD, dock.clientHeight) + 4; cursor.style.width = w + "px"; cursor.style.height = h + "px"; cursor.style.borderRadius = transition(cursorD/2, 4) + "px"; }); window.addEventListener('mousemove', e => { if (!demoOn) { return; } mouseX = e.clientX; mouseY = e.clientY; }) // TODO: handle scroll whiel mouse over dock.addEventListener('mouseover', e => { if (!demoOn) { return; } mouseOver = true; spring({ from: dockProgress, to: 1, ...springDamping }).start(v => dockProgress = v); }); dock.addEventListener('mouseout', e => { if (!demoOn) { return; } spring({ from: dockProgress, to: 0, ...springDamping }).start(v => dockProgress = v); })