Joystick.ts
1 import { Point } from './Point' 2 3 export class Joystick { 4 private readonly canvas: HTMLCanvasElement 5 private readonly ctx: CanvasRenderingContext2D 6 7 private readonly outerRadius: number 8 private readonly innerRadius: number 9 10 private startPosition: Point 11 private pointerPosition: Point 12 13 private scale: number 14 15 public constructor() { 16 this.canvas = document.getElementById('joystick') as HTMLCanvasElement 17 this.ctx = this.canvas.getContext('2d') 18 19 this.outerRadius = 8 20 this.innerRadius = 5 21 22 this.startPosition = new Point(0, 0) 23 this.pointerPosition = new Point(0, 0) 24 25 this.scale = 1 26 } 27 28 public render(): void { 29 const margin = 120 // arbitrary number 30 31 this.canvas.width = this.outerRadius * 2 * this.scale + margin 32 this.canvas.height = this.outerRadius * 2 * this.scale + margin 33 34 this.canvas.style.left = `${this.startPosition.x - (this.canvas.width / 2)}px` 35 this.canvas.style.top = `${this.startPosition.y - (this.canvas.height / 2)}px` 36 37 this.ctx.save() 38 this.ctx.scale(this.scale, this.scale) 39 this.ctx.translate(margin / this.scale / 2, margin / this.scale / 2) 40 41 this.ctx.lineWidth = 1 42 43 this.ctx.beginPath() 44 this.ctx.arc(this.outerRadius, this.outerRadius, this.outerRadius, 0, 2 * Math.PI, false) 45 this.ctx.fillStyle = '#999' 46 this.ctx.fill() 47 this.ctx.strokeStyle = '#666' 48 this.ctx.stroke() 49 50 const pointerPosition = this.getAxisValues() 51 pointerPosition.x *= this.innerRadius 52 pointerPosition.y *= this.innerRadius 53 54 this.ctx.beginPath() 55 this.ctx.arc( 56 this.outerRadius + pointerPosition.x, 57 this.outerRadius + pointerPosition.y, 58 this.innerRadius, 59 0, 60 2 * Math.PI, 61 false, 62 ) 63 this.ctx.fillStyle = '#555' 64 this.ctx.fill() 65 this.ctx.strokeStyle = '#333' 66 this.ctx.stroke() 67 68 this.ctx.restore() 69 } 70 71 public getStartPosition(): Point { 72 return this.startPosition 73 } 74 75 public setStartPosition(position: Point): void { 76 this.startPosition = position 77 } 78 79 public getPointerPosition(): Point { 80 return this.pointerPosition 81 } 82 83 public setPointerPosition(position: Point): void { 84 this.pointerPosition = position 85 } 86 87 public show(): void { 88 this.canvas.style.display = 'block' 89 } 90 91 public hide(): void { 92 this.canvas.style.display = 'none' 93 } 94 95 public getScale(): number { 96 return this.scale 97 } 98 99 public setScale(scale: number): void { 100 this.scale = scale 101 } 102 103 // @see https://codepen.io/jiffy/pen/zrqwON 104 // @see https://stackoverflow.com/a/20916980 105 public getAxisValues(): Point { 106 const startPosition = this.startPosition.clone() 107 startPosition.x /= this.scale 108 startPosition.y /= this.scale 109 110 const pointerPosition = this.pointerPosition.clone() 111 pointerPosition.x /= this.scale 112 pointerPosition.y /= this.scale 113 114 const angle = Point.angleBetween(startPosition, pointerPosition) 115 116 const diffX = pointerPosition.x - startPosition.x 117 const diffY = pointerPosition.y - startPosition.y 118 119 let distance = Math.sqrt(diffX * diffX + diffY * diffY) 120 121 const coords = new Point(0, 0) 122 distance = Math.min(distance, this.innerRadius) 123 124 coords.x = distance * Math.cos(angle) 125 coords.y = distance * Math.sin(angle) 126 127 coords.x /= this.innerRadius 128 coords.y /= this.innerRadius 129 130 return coords 131 } 132 }