AdhyanshVerma's picture
download
raw
10.1 kB
**index.html**
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Pattern Lock</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: radial-gradient(circle at 30% 20%, #2a3a6a, #0f1530 70%);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
color: #fff;
padding: 20px;
overflow: hidden;
}
.wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 22px;
}
.title {
font-size: 20px;
font-weight: 600;
letter-spacing: 2px;
text-transform: uppercase;
color: rgba(255,255,255,0.85);
}
.lock-container {
position: relative;
width: min(90vw, 360px);
aspect-ratio: 1 / 1;
background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 24px;
box-shadow: 0 20px 60px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.06);
touch-action: none;
user-select: none;
-webkit-user-select: none;
}
.svg-layer {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 1;
pointer-events: none;
overflow: visible;
}
.grid {
position: absolute;
inset: 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
z-index: 2;
padding: 12%;
}
.cell {
display: flex;
align-items: center;
justify-content: center;
}
.node {
width: 32%;
height: 32%;
border-radius: 50%;
background: rgba(255,255,255,0.12);
border: 2px solid rgba(255,255,255,0.28);
transition: background 0.15s ease, border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.node.active {
background: #4a90ff;
border-color: #7ab0ff;
transform: scale(1.18);
box-shadow: 0 0 18px rgba(74,144,255,0.7);
}
.node.error {
background: #ff4a4a;
border-color: #ff8a8a;
box-shadow: 0 0 18px rgba(255,74,74,0.7);
}
.line {
stroke: #4a90ff;
stroke-width: 2.5;
stroke-linecap: round;
stroke-linejoin: round;
fill: none;
filter: drop-shadow(0 0 4px rgba(74,144,255,0.6));
transition: stroke 0.15s ease;
}
.line.error {
stroke: #ff4a4a;
filter: drop-shadow(0 0 4px rgba(255,74,74,0.6));
}
.result {
height: 26px;
font-size: 18px;
font-weight: 600;
letter-spacing: 0.5px;
text-align: center;
}
.result.success { color: #4ade80; }
.result.error { color: #f87171; }
.reset-btn {
padding: 10px 28px;
border: 1px solid rgba(255,255,255,0.2);
border-radius: 10px;
background: rgba(255,255,255,0.08);
color: #fff;
font-size: 14px;
font-weight: 500;
letter-spacing: 1px;
cursor: pointer;
transition: background 0.15s ease, border-color 0.15s ease;
}
.reset-btn:hover { background: rgba(255,255,255,0.16); border-color: rgba(255,255,255,0.35); }
.reset-btn:active { transform: translateY(1px); }
</style>
</head>
<body>
<div class="wrap">
<div class="title">Pattern Lock</div>
<div class="lock-container" id="container">
<svg class="svg-layer" id="svg" viewBox="0 0 100 100" preserveAspectRatio="none">
<path class="line" id="path"></path>
<path class="line" id="trail"></path>
</svg>
<div class="grid" id="grid"></div>
</div>
<div class="result" id="result"></div>
<button class="reset-btn" id="reset">Reset</button>
</div>
<script>
(function () {
const CORRECT_PATTERN = [1, 2, 3, 6, 9];
const HIT_RADIUS = 7; // viewBox units
const container = document.getElementById('container');
const grid = document.getElementById('grid');
const pathEl = document.getElementById('path');
const trailEl = document.getElementById('trail');
const resultEl = document.getElementById('result');
const resetBtn = document.getElementById('reset');
let sequence = [];
let recording = false;
let nodeCenters = []; // {x,y} in viewBox units (0..100)
let lastPoint = null;
let errorTimeout = null;
// Build nodes
for (let i = 1; i <= 9; i++) {
const cell = document.createElement('div');
cell.className = 'cell';
const node = document.createElement('div');
node.className = 'node';
node.dataset.index = i;
cell.appendChild(node);
grid.appendChild(cell);
}
function computeCenters() {
const rect = container.getBoundingClientRect();
nodeCenters = [];
const nodes = grid.querySelectorAll('.node');
nodes.forEach((n) => {
const r = n.getBoundingClientRect();
const cx = ((r.left + r.width / 2) - rect.left) / rect.width * 100;
const cy = ((r.top + r.height / 2) - rect.top) / rect.height * 100;
nodeCenters.push({ x: cx, y: cy });
});
}
function getPointerCoords(e) {
const rect = container.getBoundingClientRect();
let clientX, clientY;
if (e.touches && e.touches.length) {
clientX = e.touches[0].clientX;
clientY = e.touches[0].clientY;
} else if (e.changedTouches && e.changedTouches.length) {
clientX = e.changedTouches[0].clientX;
clientY = e.changedTouches[0].clientY;
} else {
clientX = e.clientX;
clientY = e.clientY;
}
return {
x: (clientX - rect.left) / rect.width * 100,
y: (clientY - rect.top) / rect.height * 100
};
}
function distToSegment(px, py, x1, y1, x2, y2) {
const dx = x2 - x1, dy = y2 - y1;
const len2 = dx * dx + dy * dy;
let t = len2 === 0 ? 0 : ((px - x1) * dx + (py - y1) * dy) / len2;
t = Math.max(0, Math.min(1, t));
const cx = x1 + t * dx, cy = y1 + t * dy;
return Math.hypot(px - cx, py - cy);
}
function updateLines() {
if (sequence.length === 0) {
pathEl.setAttribute('d', '');
return;
}
let d = '';
sequence.forEach((idx, i) => {
const c = nodeCenters[idx - 1];
d += (i === 0 ? 'M' : 'L') + c.x.toFixed(3) + ',' + c.y.toFixed(3) + ' ';
});
pathEl.setAttribute('d', d.trim());
}
function updateTrail(x, y) {
if (!recording || sequence.length === 0 || x == null) {
trailEl.setAttribute('d', '');
return;
}
const last = nodeCenters[sequence[sequence.length - 1] - 1];
trailEl.setAttribute('d', `M${last.x.toFixed(3)},${last.y.toFixed(3)} L${x.toFixed(3)},${y.toFixed(3)}`);
}
function activateNode(idx) {
const node = grid.querySelector(`.node[data-index="${idx}"]`);
if (node) node.classList.add('active');
}
function clearPattern() {
if (errorTimeout) { clearTimeout(errorTimeout); errorTimeout = null; }
sequence = [];
grid.querySelectorAll('.node').forEach((n) => {
n.classList.remove('active');
n.classList.remove('error');
});
pathEl.classList.remove('error');
trailEl.classList.remove('error');
pathEl.setAttribute('d', '');
trailEl.setAttribute('d', '');
resultEl.textContent = '';
resultEl.className = 'result';
}
function arraysEqual(a, b) {
if (a.length !== b.length) return false;
return a.every((v, i) => v === b[i]);
}
function finish() {
if (!recording) return;
recording = false;
lastPoint = null;
updateTrail(null, null);
if (sequence.length === 0) return;
if (arraysEqual(sequence, CORRECT_PATTERN)) {
resultEl.textContent = 'Access Granted';
resultEl.className = 'result success';
} else {
resultEl.textContent = 'Access Denied';
resultEl.className = 'result error';
grid.querySelectorAll('.node.active').forEach((n) => n.classList.add('error'));
pathEl.classList.add('error');
trailEl.classList.add('error');
errorTimeout = setTimeout(clearPattern, 1000);
}
}
function start(e) {
if (e.cancelable) e.preventDefault();
clearPattern();
computeCenters();
const p = getPointerCoords(e);
// Only begin recording if press starts on a node
let hitIdx = null;
for (let i = 0; i < 9; i++) {
const c = nodeCenters[i];
if (Math.hypot(c.x - p.x, c.y - p.y) <= HIT_RADIUS) { hitIdx = i + 1; break; }
}
if (hitIdx === null) return;
recording = true;
sequence = [hitIdx];
activateNode(hitIdx);
lastPoint = p;
updateLines();
updateTrail(p.x, p.y);
}
function move(e) {
if (!recording) return;
if (e.cancelable) e.preventDefault();
const p = getPointerCoords(e);
const prev = lastPoint || p;
// Find unvisited nodes whose center lies near the segment prev->p
const candidates = [];
for (let i = 1; i <= 9; i++) {
if (sequence.includes(i)) continue;
const c = nodeCenters[i - 1];
const d = distToSegment(c.x, c.y, prev.x, prev.y, p.x, p.y);
if (d <= HIT_RADIUS) {
const distFromPrev = Math.hypot(c.x - prev.x, c.y - prev.y);
candidates.push({ idx: i, distFromPrev });
}
}
candidates.sort((a, b) => a.distFromPrev - b.distFromPrev);
let changed = false;
for (const cand of candidates) {
if (!sequence.includes(cand.idx)) {
sequence.push(cand.idx);
activateNode(cand.idx);
changed = true;
}
}
if (changed) updateLines();
updateTrail(p.x, p.y);
lastPoint = p;
}
// Mouse
container.addEventListener('mousedown', start);
window.addEventListener('mousemove', move);
window.addEventListener('mouseup', finish);
// Touch
container.addEventListener('touchstart', start, { passive: false });
window.addEventListener('touchmove', move, { passive: false });
window.addEventListener('touchend', finish);
window.addEventListener('touchcancel', finish);
// Reset
resetBtn.addEventListener('click', clearPattern);
// Recompute on resize
window.addEventListener('resize', () => {
computeCenters();
updateLines();
});
// Initial
computeCenters();
})();
</script>
</body>
</html>
```

Xet Storage Details

Size:
10.1 kB
·
Xet hash:
60adc31044213d48db9fb88660dfd5d97f9cc7102d4be3d6a6112696d7223eb5

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.