Update index.php
This commit is contained in:
210
index.php
210
index.php
@@ -508,6 +508,68 @@ try {
|
|||||||
gap:6px;
|
gap:6px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== Keyboard player + selection styles ===== */
|
||||||
|
|
||||||
|
#kb-player{
|
||||||
|
position:fixed;
|
||||||
|
width:26px;
|
||||||
|
height:26px;
|
||||||
|
border-radius:40%;
|
||||||
|
background:radial-gradient(circle at 30% 20%, #ffffff, #7cf5ff);
|
||||||
|
box-shadow:
|
||||||
|
0 0 10px rgba(124,245,255,.9),
|
||||||
|
0 0 26px rgba(124,245,255,.5);
|
||||||
|
transform:translate(-50%, -50%);
|
||||||
|
z-index:999;
|
||||||
|
pointer-events:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#kb-player::after{
|
||||||
|
content:"";
|
||||||
|
position:absolute;
|
||||||
|
left:50%;
|
||||||
|
top:50%;
|
||||||
|
width:4px;
|
||||||
|
height:9px;
|
||||||
|
border-radius:10px;
|
||||||
|
background:rgba(0,0,0,.35);
|
||||||
|
transform:translate(-50%,-80%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#kb-hint{
|
||||||
|
position:fixed;
|
||||||
|
bottom:10px;
|
||||||
|
left:50%;
|
||||||
|
transform:translateX(-50%);
|
||||||
|
padding:6px 12px;
|
||||||
|
font-size:11px;
|
||||||
|
border-radius:999px;
|
||||||
|
background:rgba(0,0,0,.65);
|
||||||
|
border:1px solid rgba(255,255,255,.10);
|
||||||
|
z-index:998;
|
||||||
|
color:var(--muted);
|
||||||
|
}
|
||||||
|
#kb-hint kbd{
|
||||||
|
display:inline-block;
|
||||||
|
min-width:18px;
|
||||||
|
padding:1px 5px;
|
||||||
|
border-radius:4px;
|
||||||
|
border:1px solid rgba(255,255,255,.3);
|
||||||
|
font-size:10px;
|
||||||
|
font-weight:600;
|
||||||
|
text-align:center;
|
||||||
|
background:rgba(255,255,255,.06);
|
||||||
|
margin:0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Highlight when orb is overlapping a nav link */
|
||||||
|
.mk-nav-link.kb-highlight{
|
||||||
|
background:radial-gradient(circle at 0 0,var(--accent-soft),transparent 55%);
|
||||||
|
border-color:var(--accent-border);
|
||||||
|
color:var(--text);
|
||||||
|
transform:translateX(2px);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -646,5 +708,153 @@ try {
|
|||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<!-- Keyboard-player orb + hint -->
|
||||||
|
<div id="kb-player"></div>
|
||||||
|
<div id="kb-hint">
|
||||||
|
Move with <kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd>, stand on a nav item, press <kbd>E</kbd> to select.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function(){
|
||||||
|
const player = document.getElementById('kb-player');
|
||||||
|
const hint = document.getElementById('kb-hint');
|
||||||
|
const interactables = Array.from(document.querySelectorAll('.mk-nav-link'));
|
||||||
|
|
||||||
|
// Initial position (near left side / sidebar)
|
||||||
|
let x = 160;
|
||||||
|
let y = 220;
|
||||||
|
const speed = 230; // pixels per second
|
||||||
|
const keys = { w:false, a:false, s:false, d:false };
|
||||||
|
|
||||||
|
function updatePlayer(){
|
||||||
|
player.style.left = x + 'px';
|
||||||
|
player.style.top = y + 'px';
|
||||||
|
}
|
||||||
|
updatePlayer();
|
||||||
|
|
||||||
|
// Key handling
|
||||||
|
window.addEventListener('keydown', (e)=>{
|
||||||
|
switch(e.code){
|
||||||
|
case 'KeyW':
|
||||||
|
case 'KeyA':
|
||||||
|
case 'KeyS':
|
||||||
|
case 'KeyD':
|
||||||
|
case 'KeyE':
|
||||||
|
e.preventDefault(); // stop page scrolling on these keys
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.code === 'KeyW') keys.w = true;
|
||||||
|
if(e.code === 'KeyA') keys.a = true;
|
||||||
|
if(e.code === 'KeyS') keys.s = true;
|
||||||
|
if(e.code === 'KeyD') keys.d = true;
|
||||||
|
|
||||||
|
if(e.code === 'KeyE'){
|
||||||
|
tryInteract();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('keyup', (e)=>{
|
||||||
|
if(e.code === 'KeyW') keys.w = false;
|
||||||
|
if(e.code === 'KeyA') keys.a = false;
|
||||||
|
if(e.code === 'KeyS') keys.s = false;
|
||||||
|
if(e.code === 'KeyD') keys.d = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Movement loop
|
||||||
|
let lastTime = null;
|
||||||
|
function loop(timestamp){
|
||||||
|
if(lastTime === null) lastTime = timestamp;
|
||||||
|
const dt = (timestamp - lastTime) / 1000;
|
||||||
|
lastTime = timestamp;
|
||||||
|
|
||||||
|
let vx = 0, vy = 0;
|
||||||
|
if(keys.w) vy -= 1;
|
||||||
|
if(keys.s) vy += 1;
|
||||||
|
if(keys.a) vx -= 1;
|
||||||
|
if(keys.d) vx += 1;
|
||||||
|
|
||||||
|
if(vx !== 0 || vy !== 0){
|
||||||
|
const len = Math.hypot(vx, vy) || 1;
|
||||||
|
vx /= len;
|
||||||
|
vy /= len;
|
||||||
|
|
||||||
|
x += vx * speed * dt;
|
||||||
|
y += vy * speed * dt;
|
||||||
|
|
||||||
|
const half = 13;
|
||||||
|
const maxW = window.innerWidth;
|
||||||
|
const maxH = window.innerHeight;
|
||||||
|
|
||||||
|
if(x < half) x = half;
|
||||||
|
if(y < half) y = half;
|
||||||
|
if(x > maxW - half) x = maxW - half;
|
||||||
|
if(y > maxH - half) y = maxH - half;
|
||||||
|
|
||||||
|
updatePlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHighlights();
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
}
|
||||||
|
requestAnimationFrame(loop);
|
||||||
|
|
||||||
|
function updateHighlights(){
|
||||||
|
const pRect = player.getBoundingClientRect();
|
||||||
|
let best = null;
|
||||||
|
let bestArea = 0;
|
||||||
|
|
||||||
|
interactables.forEach(el=>{
|
||||||
|
const r = el.getBoundingClientRect();
|
||||||
|
const overlapX = Math.max(0, Math.min(pRect.right, r.right) - Math.max(pRect.left, r.left));
|
||||||
|
const overlapY = Math.max(0, Math.min(pRect.bottom, r.bottom) - Math.max(pRect.top, r.top));
|
||||||
|
const area = overlapX * overlapY;
|
||||||
|
|
||||||
|
if(area > 1 && area > bestArea){
|
||||||
|
bestArea = area;
|
||||||
|
best = el;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
interactables.forEach(el => el.classList.remove('kb-highlight'));
|
||||||
|
if(best){
|
||||||
|
best.classList.add('kb-highlight');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryInteract(){
|
||||||
|
const highlighted = document.querySelector('.mk-nav-link.kb-highlight');
|
||||||
|
if(!highlighted) return;
|
||||||
|
|
||||||
|
const href = highlighted.getAttribute('href');
|
||||||
|
|
||||||
|
if(href && href.startsWith('#') && href.length > 1){
|
||||||
|
const target = document.querySelector(href);
|
||||||
|
if(target){
|
||||||
|
target.scrollIntoView({ behavior:'smooth', block:'start' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(href && href !== '#'){
|
||||||
|
window.location.href = href;
|
||||||
|
} else {
|
||||||
|
highlighted.click();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust player if window size changes (keeps it on-screen)
|
||||||
|
window.addEventListener('resize', ()=>{
|
||||||
|
const half = 13;
|
||||||
|
const maxW = window.innerWidth;
|
||||||
|
const maxH = window.innerHeight;
|
||||||
|
if(x > maxW - half) x = maxW - half;
|
||||||
|
if(y > maxH - half) y = maxH - half;
|
||||||
|
updatePlayer();
|
||||||
|
});
|
||||||
|
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user