Skip to content

Instantly share code, notes, and snippets.

@zuramai
Created March 14, 2022 03:23
Show Gist options
  • Save zuramai/ca79b6e5177ab365f66810b81af6f152 to your computer and use it in GitHub Desktop.
Save zuramai/ca79b6e5177ab365f66810b81af6f152 to your computer and use it in GitHub Desktop.
Unit Circle Canvas
class UnitCircle {
constructor(canvas) {
this.canvas = canvas
this.ctx = this.canvas.getContext('2d')
this.circleSize = this.canvas.width * 2/5
this.position = [0.23, -0.5] // equivalent to [x, y]
this.mousePosition = [0, 0]
this.isMouseDown = false
this.halfWidth = this.canvas.width/2
this.halfHeight = this.canvas.height/2
this.angle = this.getAngle(this.position)
this.cursorStatus = "default"
this.cursorRadius = 10
this.cursorPoint = []
this.init()
}
init() {
this.computeCursorPoint()
this.updatePosition()
this.draw()
this.canvas.addEventListener('mousemove', this.mouseEvent.bind(this))
this.canvas.addEventListener('mousedown', this.mouseEvent.bind(this))
this.canvas.addEventListener('mouseup', this.mouseEvent.bind(this))
}
draw() {
// Clear the canvas
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.drawCircle()
this.drawLines()
this.drawTriangles()
this.drawCursor()
this.drawText()
}
drawText() {
let fontSize = 12
// Draw X Y position indicator
this.ctx.fillStyle = "white"
this.ctx.strokeStyle = "black"
this.ctx.lineWidth = 1
this.ctx.textAlign = "center"
this.ctx.font = `${fontSize}px Arial`
this.ctx.fillText(`X = ${Math.round(this.position[0] *100)/100}`,
this.cursorPoint[0] + (this.halfWidth - this.cursorPoint[0]) / 2,
this.halfHeight-fontSize)
this.ctx.strokeText(`Y = ${Math.round(this.position[1] *100)/100}`,
this.cursorPoint[0] + fontSize,
this.halfHeight + (this.cursorPoint[1] - this.halfHeight) / 2)
this.ctx.fillText(`Y = ${Math.round(this.position[1] *100)/100}`,
this.cursorPoint[0] + fontSize,
this.halfHeight + (this.cursorPoint[1] - this.halfHeight) / 2)
}
drawCircle() {
let center = [this.canvas.width/2, this.canvas.height/2]
this.ctx.lineWidth = 2
this.ctx.strokeStyle = "white"
this.ctx.beginPath()
this.ctx.arc(center[0], center[1], this.circleSize, 0, Math.PI*2)
this.ctx.stroke()
this.ctx.closePath()
this.ctx.beginPath()
this.ctx.strokeStyle = "rgba(120,120,120,.5)"
this.ctx.moveTo(center[0] - this.circleSize, center[1])
this.ctx.lineTo(center[0] + this.circleSize, center[1])
this.ctx.moveTo(center[0], center[1] - this.circleSize)
this.ctx.lineTo(center[0], center[1] + this.circleSize)
this.ctx.stroke()
this.ctx.closePath()
}
drawLines() {
this.ctx.beginPath()
this.ctx.lineWidth = 2
this.ctx.strokeStyle = "white"
// Horizontal Line
this.ctx.moveTo(this.halfWidth, this.halfHeight)
this.ctx.lineTo(this.cursorPoint[0], this.halfHeight)
// Vertical Line
this.ctx.moveTo(this.cursorPoint[0], this.halfHeight)
this.ctx.lineTo(this.cursorPoint[0], this.cursorPoint[1])
this.ctx.stroke()
this.ctx.closePath()
}
drawTriangles() {
let triangleSize = 7
let absoluteOne = (n) => n < 0 ? -1 : 1
let horizontalVerticalCorner = this.cursorPoint[0] + triangleSize * -absoluteOne(this.position[0])
let verticalHorizontalCorner = this.cursorPoint[1] + triangleSize * -absoluteOne(this.position[1])
this.ctx.fillStyle = "white"
this.ctx.beginPath()
this.ctx.moveTo(horizontalVerticalCorner, this.halfHeight - triangleSize) // top corner
this.ctx.lineTo(this.cursorPoint[0], this.halfHeight) // center corner
this.ctx.lineTo(horizontalVerticalCorner, this.halfHeight + triangleSize) // bottom corner
this.ctx.lineTo(horizontalVerticalCorner, this.halfHeight - triangleSize) // top corner
this.ctx.moveTo(this.cursorPoint[0] - triangleSize, verticalHorizontalCorner) // left corner
this.ctx.lineTo(this.cursorPoint[0], this.cursorPoint[1]) // center corner
this.ctx.lineTo(this.cursorPoint[0] + triangleSize, verticalHorizontalCorner) // bottom corner
this.ctx.lineTo(this.cursorPoint[0] - triangleSize, verticalHorizontalCorner) // right corner
this.ctx.fill()
this.ctx.closePath()
}
drawCursor() {
this.computeCursorPoint()
this.ctx.lineWidth = 2
this.ctx.beginPath()
this.ctx.fillStyle = "rgba(77, 119, 255,.5)"
this.ctx.strokeStyle = "white"
this.ctx.arc(this.cursorPoint[0], this.cursorPoint[1], this.cursorRadius, 0, Math.PI*2)
this.ctx.fill()
this.ctx.stroke()
this.ctx.closePath()
}
computeCursorPoint() {
this.cursorPoint = [
this.circleSize * Math.cos(this.angle) + this.halfWidth,
this.circleSize * Math.sin(this.angle) + this.halfHeight
]
}
getAngle(position) {
let dx = position[0] - this.halfWidth;
let dy = position[1] - this.halfHeight;
let rad = Math.atan2(dy, dx);
return rad
}
isMouseOnCursor() {
this.computeCursorPoint()
let dx = Math.abs(this.mousePosition[0] - this.cursorPoint[0])
let dy = Math.abs(this.mousePosition[1] - this.cursorPoint[1])
return dx * dx + dy * dy < this.cursorRadius * this.cursorRadius
}
mouseEvent(e) {
// Map mouse events function
let typeFunc = {
"mousemove": this.moveCircle.bind(this),
"mousedown": this.activateCircle.bind(this),
"mouseup": this.releaseCircle.bind(this)
}
// Save the mouse location
let rect = this.canvas.getBoundingClientRect()
this.mousePosition = [e.clientX - rect.x, e.clientY - rect.y]
// Execute the function
typeFunc[e.type](e)
this.draw()
}
updatePosition() {
this.angle = this.getAngle(this.mousePosition)
this.computeCursorPoint()
console.log(this.cursorPoint)
let positionX = (this.cursorPoint[0] - this.halfWidth) / this.circleSize * 1
let positionY = (this.cursorPoint[1] - this.halfHeight) / this.circleSize * 1
this.position = [
positionX > 1 || positionX < -1 ? Math.round(position[0]) : positionX,
positionY > 1 || positionY < -1 ? Math.round(position[1]) : positionY
]
}
moveCircle() {
if(!this.isMouseDown) return
if(!this.isMouseOnCursor() && this.cursorStatus !== "holding") return
this.canvas.style.cursor = "pointer"
this.updatePosition()
}
activateCircle() {
this.isMouseDown = true
if(this.isMouseOnCursor()) this.cursorStatus = "holding"
}
releaseCircle() {
this.isMouseDown = false
this.cursorStatus = "default"
this.canvas.style.cursor = "default"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment