|
|
| <html> |
| <head> |
| <title>A test of courage</title> |
| <link href="https://fonts.googleapis.com/css2?family=Metal+Mania&display=swap" rel="stylesheet"> |
| <div id="stop-instruction" class="unselectable" style="position: absolute; top: 8%; left: 50%; transform: translate(-50%, -50%); color: #ffa502; font-size: 24px; font-family: 'Metal Mania', cursive; text-shadow: 0 0 10px #d35400, 0 0 20px #d35400; visibility: hidden; text-align: center;"> |
| Press 'Ctrl+R' to banish the spirit's jiggle! |
| </div> |
|
|
| <style> |
| |
| .metal-mania-regular { |
| font-family: "Metal Mania", system-ui; |
| font-weight: 400; |
| font-style: normal; |
| } |
| |
| |
| .arrow { |
| position: absolute; |
| top: 30%; |
| left: 30%; |
| width: 12px; |
| visibility: hidden; |
| pointer-events: none; |
| z-index: 1000; |
| } |
| |
| .pointer { |
| position: absolute; |
| top: 30%; |
| left: 30%; |
| width: 16px; |
| visibility: hidden; |
| pointer-events: none; |
| } |
| |
| body { |
| overflow-y: hidden; |
| overflow-x: hidden; |
| margin:0; |
| padding:0; |
| background: #262626; |
| cursor: none; |
| } |
| .button { |
| background: #171515; |
| width: 100%; |
| height: 100%; |
| border-radius: 6px; |
| border-width: 0px; |
| font-size: 24px; |
| transform: translateY(0); |
| color: #ffa502; |
| box-shadow: 0 0 50px #d35400; |
| cursor: none; |
| transition: .5s; |
| } |
| .darkened { |
| background: #171515; |
| opacity: 0.2; |
| box-shadow: 0 0 0px 0px !important; |
| } |
| |
| .hoverableMainButton:hover { |
| text-shadow: 0 0 5px #d35400; |
| box-shadow: 0 0 50px 20px #d35400; |
| } |
| |
| button:focus{ |
| outline: none; |
| } |
| |
| .how { |
| background: #171515; |
| width: 100%; |
| height: 100%; |
| border-radius: 6px; |
| border-width: 0px; |
| font-size: 18px; |
| transform: translateY(0); |
| color: #ffa502; |
| box-shadow: 0 0 20px #d35400; |
| cursor: none; |
| } |
| |
| #ouija { |
| box-shadow: 0 0 20px #d35400; |
| } |
| |
| .unselectable { |
| user-select: none; |
| -moz-user-select: none; |
| -khtml-user-select: none; |
| -webkit-user-select: none; |
| -o-user-select: none; |
| } |
| |
| @media only screen and (max-width: 760px) { |
| .button { |
| display: none; |
| } |
| .mobile-warning { |
| display: block; |
| } |
| } |
| |
| </style> |
|
|
| <script> |
| var recordingStatus = "off" |
| var freshRecording = [] |
| var prevTime = undefined |
| var prevX = undefined |
| var prevY = undefined |
| var offsetX = 0 |
| var offsetY = 0 |
| var userMoveCount = 0 |
| |
| var hideAllCursors = function() { |
| const pointer = document.getElementById("pointer") |
| const arrow = document.getElementById("arrow") |
| pointer.style.visibility = "hidden"; |
| arrow.style.visibility = "hidden"; |
| } |
| var paintCursorWithOffset = function(cursor, realX, realY) { |
| const w = window.innerWidth; |
| const h = window.innerHeight; |
| var x = realX + offsetX |
| var y = realY + offsetY |
| if (x > w-24) { |
| x = Math.max(realX, w-24) |
| } |
| if (y > h-24) { |
| y = Math.max(realY, h-24) |
| } |
| if (x < 0) { |
| x = 0 |
| } |
| if (y < 0) { |
| y = 0 |
| } |
| cursor.style.top = y + "px"; |
| cursor.style.left = x + "px"; |
| } |
| var mouseMovedOnBackground = function(event) { |
| const x = event.clientX |
| const y = event.clientY |
| if (recordingStatus === "recording") { |
| const time = Date.now() |
| const timeDiff = time - prevTime |
| const xDiff = x - prevX |
| const yDiff = y - prevY |
| prevTime = time |
| freshRecording.push([timeDiff, xDiff, yDiff]) |
| } |
| prevX = x |
| prevY = y |
| const pointer = document.getElementById("pointer") |
| pointer.style.visibility = "hidden"; |
| const arrow = document.getElementById("arrow") |
| arrow.style.visibility = "visible"; |
| paintCursorWithOffset(arrow, prevX, prevY) |
| userMoveCount += 1 |
| } |
| var mouseMovedOnButton = function(event) { |
| if (recordingStatus === "replaying") { |
| mouseMovedOnBackground(event); |
| return; |
| } |
| const x = event.clientX |
| const y = event.clientY |
| prevX = x |
| prevY = y |
| const arrow = document.getElementById("arrow") |
| arrow.style.visibility = "hidden"; |
| const pointer = document.getElementById("pointer") |
| pointer.style.visibility = "visible"; |
| paintCursorWithOffset(pointer, x, y) |
| userMoveCount += 1 |
| } |
| var getCandidateVal = function(x, y, candidateX, candidateY) { |
| return Math.sqrt((x - candidateX) * (x-candidateX) + (y - candidateY) * (y - candidateY)) |
| } |
| var choose = function(previousBestCandidate, candidateX, candidateY, x, y) { |
| const candidateVal = getCandidateVal(x, y, candidateX, candidateY) |
| if (!previousBestCandidate || candidateVal < previousBestCandidate.val) { |
| return { |
| 'val': candidateVal, |
| 'x': candidateX, |
| 'y': candidateY |
| } |
| } |
| return previousBestCandidate |
| } |
| var mouseMovedOnHowdy = function(event) { |
| if (recordingStatus === "replaying") { |
| mouseMovedOnBackground(event); |
| return; |
| } |
| const x = event.clientX |
| const y = event.clientY |
| |
| |
| const bounds = document.getElementById("how").getBoundingClientRect() |
| var bestCandidate = null |
| bestCandidate = choose(bestCandidate, bounds.left-10, y, x, y) |
| bestCandidate = choose(bestCandidate, x, bounds.top-10, x, y) |
| bestCandidate = choose(bestCandidate, x, bounds.bottom+10, x, y) |
| bestCandidate = choose(bestCandidate, bounds.right+10, y, x, y) |
| offsetX = bestCandidate.x - x |
| offsetY = bestCandidate.y - y |
| |
| prevX = x |
| prevY = y |
| const pointer = document.getElementById("pointer") |
| pointer.style.visibility = "hidden"; |
| const arrow = document.getElementById("arrow") |
| arrow.style.visibility = "visible"; |
| paintCursorWithOffset(arrow, x, y) |
| userMoveCount += 1 |
| } |
| var recordingIndex = 0 |
| var step = 0 |
| var recursiveTimerReplay = function() { |
| const rec = recordings[recordingIndex] |
| if (step >= rec.length) { |
| |
| step = 0 |
| recordingIndex = (recordingIndex + 1) % recordings.length |
| |
| } |
| |
| const currTime = Date.now() |
| if (currTime - prevTime >= rec[step][0]) { |
| offsetX = offsetX + rec[step][1] |
| offsetY = offsetY + rec[step][2] |
| step = step + 1 |
| paintCursorWithOffset(document.getElementById("arrow"), prevX, prevY) |
| prevTime = currTime |
| } |
| setTimeout(() => { recursiveTimerReplay() }, 0) |
| } |
| var replayMovements = function(event) { |
| const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; |
| if (width < 800 || typeof(prevX) == "undefined" || userMoveCount < 5) { |
| alert("Sorry, we can't do the trick on your device. Please try again with a desktop/laptop that has a mouse attached and has screen width of at least 800 pixels.") |
| return |
| } |
| recordingStatus = "replaying" |
| arrow = document.getElementById("arrow") |
| paintCursorWithOffset(arrow, prevX, prevY) |
| document.getElementById("pointer").style.visibility = "hidden"; |
| arrow.style.visibility = "visible"; |
| document.getElementById("butt").classList.add("darkened") |
| document.getElementById("butt").classList.remove("hoverableMainButton") |
| document.getElementById("howdiv").style.visibility = "hidden"; |
| document.getElementById("ouija").style.visibility = "hidden"; |
| document.getElementById("stop-instruction").style.visibility = "visible"; |
| prevTime = Date.now() |
| recursiveTimerReplay() |
| } |
| var recordMovements = function(event) { |
| const REC_TIME = 15000 |
| freshRecording = [] |
| recordingStatus = "recording" |
| prevTime = Date.now() |
| document.getElementById('recordButton').style.display = 'none'; |
| document.getElementById('buttdiv').style.display = 'none'; |
| for (var j=REC_TIME; j>0; j-=1000) { |
| const bah = REC_TIME - j |
| setTimeout(() => console.log(bah / 1000), bah) |
| } |
| setTimeout(() => { |
| document.getElementById('buttdiv').style.display = 'block'; |
| recordingStatus = false; |
| recordings.push(freshRecording) |
| console.log('finished recording'); |
| console.log(freshRecording) |
| }, REC_TIME) |
| } |
| |
| document.addEventListener('contextmenu', e => { |
| if (recordingStatus == 'replaying') { |
| e.preventDefault(); |
| } |
| }); |
| |
| </script> |
| </head> |
| <body> |
|
|
| <div id="bg" style="position: absolute; width: 100%; height: 100%"></div> |
|
|
| <button id="recordButton" style="position: absolute; top: 45%; left: 37%; z-index: 999; display: none;"> |
| Record |
| </button> |
|
|
| <noscript><h1>This website requires JavaScript in order to do a trick. Please enable JavaScript and refresh.</h1></noscript> |
|
|
| <h1 class="mobile-warning" style="display: none;"> |
| This website requires a laptop/desktop device with a mouse, and a sufficiently large screen. Try with another device? |
| </h1> |
|
|
| <div id="buttdiv" class="unselectable" style="position: absolute; width: 400px; height: 100px; top: 50%; left: 50%; margin: -50px 0 0 -200px;"> |
| <button id="butt" class="button hoverableMainButton unselectable" tabindex="-1"> |
| I'm not afraid to click<br>buttons on the internet |
| </button> |
| </div> |
|
|
| <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ" title="Explanation video (all the answers you need are here)" style="display: none;"></a> |
| <div id="howdiv" class="unselectable" style="position: absolute; width: 200px; height: 50px; right: 50px; bottom: 50px; margin: -50px 0 0 -200px; visibility: visible;"> |
| <button id="how" class="how unselectable" tabindex="-1"> |
| How this works |
| </button> |
| </div> |
|
|
| <div id="ouija" class="unselectable" style="position: absolute; left: 50px; bottom: 50px; visibility: visible;"> |
| <a href="https://ouija.attejuvonen.fi" target="_blank"> |
| <img src="ouija.jpg" style="width: 100px; cursor: none;"> |
| </a> |
| </div> |
| <script> |
| |
| document.body.addEventListener('mouseleave', e => { hideAllCursors() }) |
| document.getElementById('bg').addEventListener('mousemove', e => { mouseMovedOnBackground(e) }) |
| document.getElementById('butt').addEventListener('mousemove', e => { mouseMovedOnButton(e) }) |
| document.getElementById('butt').addEventListener('click', e => { replayMovements(e) }) |
| document.getElementById('recordButton').addEventListener('click', e => { recordMovements(e) }) |
| document.getElementById('how').addEventListener('mousemove', e => { mouseMovedOnHowdy(e) }) |
| document.getElementById('how').addEventListener('mouseleave', e => { offsetX = 0; offsetY = 0; }) |
| document.getElementById('ouija').addEventListener('mousemove', e => { mouseMovedOnButton(e) }) |
| |
| </script> |
| <script src="recordings.js"></script> |
|
|
| <img id="arrow" class="arrow" src="arrow_m.png" /> |
| <img id="pointer" class="pointer" src="pointer.png" /> |
|
|
| <script async defer data-domain="attejuvonen.fi" src="https://plausible.io/js/plausible.js"></script> |
| |
| </body> |
| </html> |