Created
August 9, 2023 20:14
-
-
Save Brostoffed/d53947bb53bb1a6e7bafe893df3cce53 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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