Skip to content

Instantly share code, notes, and snippets.

@Brostoffed
Created August 9, 2023 20:14
Show Gist options
  • Save Brostoffed/d53947bb53bb1a6e7bafe893df3cce53 to your computer and use it in GitHub Desktop.
Save Brostoffed/d53947bb53bb1a6e7bafe893df3cce53 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="myCanvas"></canvas>
<script>
class CanvasInput {
constructor(canvas, options = {}) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.options = options;
this.text = '';
this.cursorPosition = 0;
this.isFocused = false;
this.scrollOffset = 0;
this.init();
}
init() {
this.canvas.addEventListener('mousedown', this.handleMouseDown.bind(this));
this.canvas.addEventListener('mousemove', this.handleMouseMove.bind(this));
this.canvas.addEventListener('mouseup', this.handleMouseUp.bind(this));
this.canvas.addEventListener('dblclick', this.handleDoubleClick.bind(this)); // Add this line
document.addEventListener('keydown', this.handleKeyDown.bind(this));
this.drawInput();
}
drawInput() {
this.ctx.strokeRect(10, 10, this.canvas.width - 20, 30);
this.renderText();
}
renderText() {
this.ctx.font = '16px sans-serif';
this.ctx.fillStyle = 'black';
this.ctx.fillText(this.text, 15, 30);
}
renderCursor() {
const cursorX = 15 + this.ctx.measureText(this.text.slice(0, this.cursorPosition)).width;
this.ctx.beginPath();
this.ctx.moveTo(cursorX, 15);
this.ctx.lineTo(cursorX, 35);
this.ctx.stroke();
}
handleDoubleClick(event) {
const rect = this.canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
if (this.isFocused) {
const clickedPosition = this.getCursorPositionFromMouse(x);
this.selectWordAt(clickedPosition);
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
this.renderSelection();
}
}
handleMouseDown(event) {
const rect = this.canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
if (x >= 10 && x <= this.canvas.width - 10 && y >= 10 && y <= 40) {
this.isFocused = true;
this.cursorPosition = this.getCursorPositionFromMouse(x);
this.selectionStart = this.cursorPosition;
this.selectionEnd = this.cursorPosition;
this.isSelecting = true; // Add this line to track the selection state
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
this.renderCursor();
} else {
this.isFocused = false;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
}
}
handleMouseMove(event) {
if (this.isSelecting) { // Add this condition to update selection while dragging
const rect = this.canvas.getBoundingClientRect();
const x = event.clientX - rect.left;
this.selectionEnd = this.getCursorPositionFromMouse(x);
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
this.renderSelection(); // Add this line to render the selection
}
}
handleMouseUp(event) {
this.isSelecting = false; // Add this line to stop the selection state
}
handleKeyDown(event) {
if (this.isFocused) {
if (event.key === 'Backspace') {
if (this.selectionStart !== this.selectionEnd) {
// Delete selected text
this.text = this.text.slice(0, this.selectionStart) + this.text.slice(this.selectionEnd);
this.cursorPosition = this.selectionStart;
this.selectionEnd = this.selectionStart;
}
else {
this.text = this.text.slice(0, this.cursorPosition - 1) + this.text.slice(this.cursorPosition);
this.cursorPosition -= 1;
}
} else if (event.key === 'ArrowLeft' && event.altKey) {
// Jump to the previous word when Option + Left arrow is pressed
this.cursorPosition = this.text.lastIndexOf(' ', this.cursorPosition - 1);
if (this.cursorPosition === -1) this.cursorPosition = 0;
} else if (event.key === 'ArrowRight' && event.altKey) {
// Jump to the next word when Option + Right arrow is pressed
this.cursorPosition = this.text.indexOf(' ', this.cursorPosition + 1);
if (this.cursorPosition === -1) this.cursorPosition = this.text.length;
} else if (event.key === 'ArrowLeft') {
this.cursorPosition = Math.max(0, this.cursorPosition - 1);
} else if (event.key === 'ArrowRight') {
this.cursorPosition = Math.min(this.text.length, this.cursorPosition + 1);
} else if (event.key === 'c' && event.metaKey) {
// Copy selected text
if (this.selectionStart !== this.selectionEnd) {
const selectedText = this.text.slice(this.selectionStart, this.selectionEnd);
navigator.clipboard.writeText(selectedText);
}
} else if (event.key === 'v' && event.metaKey) {
// Paste clipboard content
navigator.clipboard.readText().then(clipboardText => {
this.text = this.text.slice(0, this.cursorPosition) + clipboardText + this.text.slice(this.cursorPosition);
this.cursorPosition += clipboardText.length;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
this.renderCursor();
});
} else if (event.key === 'a' && event.metaKey) {
// Select all text
this.selectionStart = 0;
this.selectionEnd = this.text.length;
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
this.renderSelection();
event.preventDefault(); // Add this line to prevent default browser behavior
} else if (event.key.length === 1) {
this.text = this.text.slice(0, this.cursorPosition) + event.key + this.text.slice(this.cursorPosition);
this.cursorPosition += 1;
}
if (!event.metaKey) {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.drawInput();
this.renderCursor();
}
}
}
selectWordAt(position) {
const leftBoundary = this.text.slice(0, position).lastIndexOf(' ') + 1;
const rightBoundary = this.text.indexOf(' ', position);
this.selectionStart = leftBoundary;
this.selectionEnd = rightBoundary === -1 ? this.text.length : rightBoundary;
}
getCursorPositionFromMouse(x) {
let cursorPosition = 0;
for (let i = 0; i < this.text.length; i++) {
const textWidth = this.ctx.measureText(this.text.slice(0, i + 1)).width;
if (x - 15 > textWidth) {
cursorPosition = i + 1;
} else {
break;
}
}
return cursorPosition;
}
renderSelection() {
const selectionStartX = 15 + this.ctx.measureText(this.text.slice(0, this.selectionStart)).width;
const selectionEndX = 15 + this.ctx.measureText(this.text.slice(0, this.selectionEnd)).width;
this.ctx.fillStyle = 'rgba(0, 123, 255, 0.3)';
this.ctx.fillRect(selectionStartX, 15, selectionEndX - selectionStartX, 20);
this.ctx.fillStyle = 'black';
this.renderText();
}
}
const canvas = document.getElementById('myCanvas');
const input = new CanvasInput(canvas);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment