{"id":935,"date":"2026-05-14T08:15:53","date_gmt":"2026-05-14T06:15:53","guid":{"rendered":"https:\/\/pieterbatenburg.com\/?page_id=935"},"modified":"2026-05-14T10:49:21","modified_gmt":"2026-05-14T08:49:21","slug":"spelletjes","status":"publish","type":"page","link":"https:\/\/pieterbatenburg.com\/?page_id=935","title":{"rendered":"Reactiespel"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"nl\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Reactiespel - Pure HTML<\/title>\n    <script src=\"https:\/\/cdn.tailwindcss.com\"><\/script>\n    <style>\n        body {\n            background-color: #1a202c;\n            margin: 0;\n            display: flex;\n            justify-content: center;\n            align-items: center;\n            min-height: 100vh;\n            color: white;\n            font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n        }\n\n        #game-container {\n            position: relative;\n            width: 100%;\n            max-width: 800px;\n            height: 500px;\n            background-color: #4a5568; \n            border: 4px solid #cbd5e0;\n            border-radius: 12px;\n            overflow: hidden;\n            box-shadow: 0 20px 50px rgba(0,0,0,0.5);\n        }\n\n        .target-circle {\n            position: absolute;\n            width: 90px;\n            height: 90px;\n            background-color: rgba(255, 255, 255, 0.15);\n            border: 4px dashed #63b3ed;\n            border-radius: 50%;\n            transform: translate(-50%, -50%);\n            transition: border-color 0.2s, background-color 0.2s;\n            z-index: 10;\n        }\n\n        .ball {\n            position: absolute;\n            width: 30px;\n            height: 30px;\n            background-color: #ff0000;\n            border-radius: 50%;\n            transform: translate(-50%, -50%);\n            box-shadow: 0 0 25px #ff0000;\n            z-index: 20;\n        }\n\n        .modal {\n            position: absolute;\n            inset: 0;\n            background: rgba(26, 32, 44, 0.9);\n            display: flex;\n            flex-direction: column;\n            justify-content: center;\n            align-items: center;\n            z-index: 100;\n            text-align: center;\n            padding: 2rem;\n        }\n\n        .game-btn {\n            background-color: #48bb78;\n            color: white;\n            padding: 1rem 2.5rem;\n            border-radius: 12px;\n            cursor: pointer;\n            border: none;\n            font-weight: 900;\n            font-size: 1.5rem;\n            transition: all 0.1s;\n            box-shadow: 0 6px 0px #2f855a;\n            text-transform: uppercase;\n        }\n\n        .game-btn:hover {\n            background-color: #38a169;\n            transform: translateY(-2px);\n            box-shadow: 0 8px 0px #276749;\n        }\n\n        .game-btn:active {\n            transform: translateY(4px);\n            box-shadow: 0 2px 0px #276749;\n        }\n\n        .score-item {\n            background: rgba(0, 0, 0, 0.3);\n            border-left: 4px solid #4a5568;\n            transition: all 0.2s;\n        }\n\n        .score-item.latest {\n            border-left-color: #48bb78;\n            background: rgba(72, 187, 120, 0.1);\n        }\n    <\/style>\n<\/head>\n<body>\n\n    <div id=\"game-container\">\n        <!-- Header met stats -->\n        <div class=\"absolute top-0 left-0 w-full p-6 flex justify-between items-center z-50 pointer-events-none\">\n            <div id=\"turn-display\" class=\"text-2xl font-black uppercase tracking-widest opacity-50\">Poging 1<\/div>\n            <div id=\"timer-display\" class=\"text-5xl font-mono font-black drop-shadow-md\">0.000s<\/div>\n        <\/div>\n\n        <!-- Gameplay Elementen -->\n        <div id=\"target\" class=\"target-circle\" style=\"left: 50%; top: 50%;\"><\/div>\n        <div id=\"ball\" class=\"ball\" style=\"left: 50%; top: 50%;\"><\/div>\n        \n        <!-- Instructie -->\n        <div class=\"absolute bottom-6 w-full text-center pointer-events-none\">\n            <span class=\"bg-black\/60 px-6 py-2 rounded-full text-sm font-bold tracking-widest uppercase border border-white\/20\">\n                Spatiebalk of tik om te vangen\n            <\/span>\n        <\/div>\n\n        <!-- Start Scherm -->\n        <div id=\"start-modal\" class=\"modal\">\n            <h1 class=\"text-6xl font-black mb-2 tracking-tighter italic\">REACTIE<span class=\"text-green-500\">SPEL<\/span><\/h1>\n            <p class=\"text-gray-400 mb-8 font-medium uppercase tracking-widest\">Hoe snel zijn jouw reflexen?<\/p>\n            <button class=\"game-btn\" onclick=\"game.start()\">Start Spel<\/button>\n        <\/div>\n\n        <!-- Resultaat Scherm -->\n        <div id=\"result-modal\" class=\"modal\" style=\"display: none;\">\n            <h2 class=\"text-2xl font-bold text-gray-400 uppercase\">Jouw Tijd<\/h2>\n            <div id=\"final-time\" class=\"text-7xl font-mono font-black text-yellow-400 my-4\">0.000s<\/div>\n            \n            <div class=\"w-full max-w-sm mb-6\">\n                <div id=\"score-list\" class=\"space-y-2 max-h-40 overflow-y-auto pr-2\">\n                    <!-- Scores komen hier -->\n                <\/div>\n            <\/div>\n\n            <button class=\"game-btn\" onclick=\"game.resetRound()\">Volgende Ronde<\/button>\n        <\/div>\n    <\/div>\n\n    <script>\n        const game = {\n            attempt: 1,\n            scores: [],\n            isRunning: false,\n            isOverlapping: false,\n            overlapStartTime: 0,\n            lastOverlapTime: 0,\n            animationId: null,\n\n            \/\/ State voor objecten\n            ball: { x: 0, y: 0, dx: 0, dy: 0 },\n            target: { x: 0, y: 0, angle: 0 },\n\n            \/\/ DOM Elementen\n            els: {\n                container: document.getElementById('game-container'),\n                ball: document.getElementById('ball'),\n                target: document.getElementById('target'),\n                timer: document.getElementById('timer-display'),\n                turn: document.getElementById('turn-display'),\n                startModal: document.getElementById('start-modal'),\n                resultModal: document.getElementById('result-modal'),\n                finalTime: document.getElementById('final-time'),\n                scoreList: document.getElementById('score-list')\n            },\n\n            start() {\n                this.els.startModal.style.display = 'none';\n                this.resetRound();\n            },\n\n            resetRound() {\n                this.els.resultModal.style.display = 'none';\n                this.isRunning = true;\n                this.isOverlapping = false;\n                this.overlapStartTime = 0;\n                this.lastOverlapTime = performance.now();\n                \n                this.els.turn.innerText = `Poging ${this.attempt}`;\n                this.els.timer.innerText = '0.000s';\n\n                \/\/ Initialiseer positie en snelheid\n                const w = this.els.container.clientWidth;\n                const h = this.els.container.clientHeight;\n                const speed = 5 + (this.attempt * 0.4);\n\n                this.ball.x = Math.random() * (w - 100) + 50;\n                this.ball.y = Math.random() * (h - 100) + 50;\n                this.ball.dx = (Math.random() > 0.5 ? 1 : -1) * speed;\n                this.ball.dy = (Math.random() > 0.5 ? 1 : -1) * speed;\n\n                this.update();\n            },\n\n            update() {\n                if (!this.isRunning) return;\n\n                const w = this.els.container.clientWidth;\n                const h = this.els.container.clientHeight;\n\n                \/\/ 1. Update Target (beweegt in een patroon)\n                this.target.angle += 0.02;\n                this.target.x = (w \/ 2) + Math.cos(this.target.angle) * (w * 0.25);\n                this.target.y = (h \/ 2) + Math.sin(this.target.angle * 1.5) * 100;\n\n                this.els.target.style.left = `${this.target.x}px`;\n                this.els.target.style.top = `${this.target.y}px`;\n\n                \/\/ 2. Update Ball (fysica + lichte aantrekkingskracht)\n                const dx = this.target.x - this.ball.x;\n                const dy = this.target.y - this.ball.y;\n                const dist = Math.sqrt(dx*dx + dy*dy);\n\n                \/\/ Aantrekkingskracht als de bal te lang weg blijft\n                if (performance.now() - this.lastOverlapTime > 2000) {\n                    this.ball.dx += dx * 0.01;\n                    this.ball.dy += dy * 0.01;\n                }\n\n                this.ball.x += this.ball.dx;\n                this.ball.y += this.ball.dy;\n\n                \/\/ Bounce wanden\n                if (this.ball.x < 15 || this.ball.x > w - 15) this.ball.dx *= -1;\n                if (this.ball.y < 15 || this.ball.y > h - 15) this.ball.dy *= -1;\n\n                this.els.ball.style.left = `${this.ball.x}px`;\n                this.els.ball.style.top = `${this.ball.y}px`;\n\n                \/\/ 3. Check Overlap\n                const hit = dist < 60;\n                if (hit) this.lastOverlapTime = performance.now();\n\n                if (hit && !this.isOverlapping) {\n                    this.isOverlapping = true;\n                    this.overlapStartTime = performance.now();\n                } else if (!hit && this.isOverlapping) {\n                    this.isOverlapping = false;\n                }\n\n                \/\/ UI Feedback\n                this.els.target.style.borderColor = this.isOverlapping ? '#4ade80' : '#63b3ed';\n                this.els.target.style.backgroundColor = this.isOverlapping ? 'rgba(74, 222, 128, 0.3)' : 'rgba(255, 255, 255, 0.15)';\n\n                if (this.isOverlapping) {\n                    const elapsed = (performance.now() - this.overlapStartTime) \/ 1000;\n                    this.els.timer.innerText = `${elapsed.toFixed(3)}s`;\n                } else {\n                    this.els.timer.innerText = '0.000s';\n                }\n\n                this.animationId = requestAnimationFrame(() => this.update());\n            },\n\n            handleAction() {\n                if (!this.isRunning) return;\n\n                if (this.isOverlapping) {\n                    this.isRunning = false;\n                    cancelAnimationFrame(this.animationId);\n                    \n                    const score = (performance.now() - this.overlapStartTime) \/ 1000;\n                    this.scores.unshift({ attempt: this.attempt, time: score });\n                    this.attempt++;\n                    this.showResults(score);\n                } else {\n                    \/\/ Flash rood bij misser\n                    this.els.container.style.borderColor = '#fc8181';\n                    setTimeout(() => this.els.container.style.borderColor = '#cbd5e0', 100);\n                }\n            },\n\n            showResults(lastScore) {\n                this.els.finalTime.innerText = `${lastScore.toFixed(3)}s`;\n                this.els.scoreList.innerHTML = this.scores.slice(0, 5).map((s, i) => `\n                    <div class=\"score-item ${i === 0 ? 'latest' : ''} p-3 rounded-lg flex justify-between items-center text-sm\">\n                        <span class=\"font-bold opacity-50 uppercase\">Ronde ${s.attempt}<\/span>\n                        <span class=\"font-mono font-black text-lg\">${s.time.toFixed(3)}s<\/span>\n                    <\/div>\n                `).join('');\n                this.els.resultModal.style.display = 'flex';\n            }\n        };\n\n        \/\/ Event Listeners\n        window.addEventListener('keydown', (e) => {\n            if (e.code === 'Space') {\n                e.preventDefault();\n                game.handleAction();\n            }\n        });\n\n        game.els.container.addEventListener('touchstart', (e) => {\n            e.preventDefault();\n            game.handleAction();\n        }, { passive: false });\n\n        game.els.container.addEventListener('mousedown', (e) => {\n            if (game.isRunning) game.handleAction();\n        });\n    <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-935","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=\/wp\/v2\/pages\/935","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=935"}],"version-history":[{"count":7,"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=\/wp\/v2\/pages\/935\/revisions"}],"predecessor-version":[{"id":947,"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=\/wp\/v2\/pages\/935\/revisions\/947"}],"wp:attachment":[{"href":"https:\/\/pieterbatenburg.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=935"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}