state test working, added random experimental html
This commit is contained in:
229
experiment.html
Normal file
229
experiment.html
Normal file
@@ -0,0 +1,229 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Canvas Text Editor</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
background: #d8cbb0;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
background: #e7dcc6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<canvas id="c"></canvas>
|
||||
|
||||
<script>
|
||||
const canvas = document.getElementById("c");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
|
||||
function resize() {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
|
||||
canvas.style.width = w + "px";
|
||||
canvas.style.height = h + "px";
|
||||
|
||||
canvas.width = Math.floor(w * dpr);
|
||||
canvas.height = Math.floor(h * dpr);
|
||||
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
/* =====================
|
||||
Editor State
|
||||
===================== */
|
||||
|
||||
const fontSize = 13;
|
||||
const lineHeight = 20;
|
||||
const padding = 20;
|
||||
const font = `${fontSize}px monospace`;
|
||||
|
||||
let lines = [""];
|
||||
let cursor = { line: 0, col: 0 };
|
||||
|
||||
let blink = true;
|
||||
let blinkEnabled = true;
|
||||
let typingTimeout = null;
|
||||
|
||||
/* =====================
|
||||
Rendering
|
||||
===================== */
|
||||
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = "#631414";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.font = font;
|
||||
ctx.textBaseline = "top";
|
||||
ctx.fillStyle = "#d2531a";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
ctx.fillText(
|
||||
lines[i],
|
||||
padding,
|
||||
padding + i * lineHeight
|
||||
);
|
||||
}
|
||||
|
||||
if (blink) {
|
||||
const before = lines[cursor.line].slice(0, cursor.col);
|
||||
const x = padding + ctx.measureText(before).width;
|
||||
const y = padding - 2 + cursor.line * lineHeight;
|
||||
|
||||
ctx.fillRect(x, y, 2, fontSize + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Cursor Blink Logic
|
||||
===================== */
|
||||
|
||||
setInterval(() => {
|
||||
if (!blinkEnabled) return;
|
||||
blink = !blink;
|
||||
draw();
|
||||
}, 500);
|
||||
|
||||
function stopBlinkWhileTyping() {
|
||||
blinkEnabled = false;
|
||||
blink = true;
|
||||
draw();
|
||||
|
||||
clearTimeout(typingTimeout);
|
||||
typingTimeout = setTimeout(() => {
|
||||
blinkEnabled = true;
|
||||
blink = true;
|
||||
draw();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Helpers
|
||||
===================== */
|
||||
|
||||
function clampCursor() {
|
||||
cursor.line = Math.max(0, Math.min(cursor.line, lines.length - 1));
|
||||
cursor.col = Math.max(0, Math.min(cursor.col, lines[cursor.line].length));
|
||||
}
|
||||
|
||||
function insertText(text) {
|
||||
const line = lines[cursor.line];
|
||||
lines[cursor.line] =
|
||||
line.slice(0, cursor.col) +
|
||||
text +
|
||||
line.slice(cursor.col);
|
||||
cursor.col += text.length;
|
||||
}
|
||||
|
||||
function newline() {
|
||||
const line = lines[cursor.line];
|
||||
lines[cursor.line] = line.slice(0, cursor.col);
|
||||
lines.splice(cursor.line + 1, 0, line.slice(cursor.col));
|
||||
cursor.line++;
|
||||
cursor.col = 0;
|
||||
}
|
||||
|
||||
function backspace() {
|
||||
if (cursor.col > 0) {
|
||||
const line = lines[cursor.line];
|
||||
lines[cursor.line] =
|
||||
line.slice(0, cursor.col - 1) +
|
||||
line.slice(cursor.col);
|
||||
cursor.col--;
|
||||
} else if (cursor.line > 0) {
|
||||
const prevLen = lines[cursor.line - 1].length;
|
||||
lines[cursor.line - 1] += lines[cursor.line];
|
||||
lines.splice(cursor.line, 1);
|
||||
cursor.line--;
|
||||
cursor.col = prevLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Keyboard Input
|
||||
===================== */
|
||||
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.metaKey || e.ctrlKey) return;
|
||||
|
||||
let changed = true;
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowLeft": cursor.col--; break;
|
||||
case "ArrowRight": cursor.col++; break;
|
||||
case "ArrowUp": cursor.line--; break;
|
||||
case "ArrowDown": cursor.line++; break;
|
||||
case "Backspace": backspace(); e.preventDefault(); break;
|
||||
case "Enter": newline(); e.preventDefault(); break;
|
||||
case "Tab": insertText(" "); e.preventDefault(); break;
|
||||
default:
|
||||
if (e.key.length === 1) {
|
||||
insertText(e.key);
|
||||
e.preventDefault();
|
||||
} else {
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
clampCursor();
|
||||
|
||||
if (changed) {
|
||||
stopBlinkWhileTyping();
|
||||
}
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
/* =====================
|
||||
Mouse → Cursor
|
||||
===================== */
|
||||
|
||||
canvas.addEventListener("mousedown", (e) => {
|
||||
ctx.font = font;
|
||||
|
||||
const mx = e.offsetX - padding;
|
||||
const my = e.offsetY - padding;
|
||||
|
||||
cursor.line = Math.max(
|
||||
0,
|
||||
Math.min(Math.floor(my / lineHeight), lines.length - 1)
|
||||
);
|
||||
|
||||
let x = 0;
|
||||
cursor.col = 0;
|
||||
for (let i = 0; i < lines[cursor.line].length; i++) {
|
||||
const w = ctx.measureText(lines[cursor.line][i]).width;
|
||||
if (x + w / 2 >= mx) break;
|
||||
x += w;
|
||||
cursor.col++;
|
||||
}
|
||||
|
||||
blink = true;
|
||||
blinkEnabled = true;
|
||||
draw();
|
||||
});
|
||||
|
||||
/* =====================
|
||||
Initial Draw
|
||||
===================== */
|
||||
|
||||
draw();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user