Canvas Mouse Interaction

kimbyungchan·2020년 11월 11일
4

canvas

목록 보기
2/4

🥴 캔버스를 활용해 마우스를 이용한 간단한 인터랙션을 구현해봅시다.

https://velog.io/@kimbyungchan/canvas-animation
이전글을 참고하시면 기본적인 구조에대해 더 쉽게 이해하실수있습니다.

// Vector.ts
export default class Vector {
x: number;
y: number;

constructor(x: number, y: number) {
this.x = x;
this.y = y;
}

distance(other: Vector): number {
return Math.sqrt(Math.pow(this.x - other.x, 2) + Math.pow(this.y - other.y, 2));
}
}

기존 Vector class에 두 점의 길이를 구하는 distance함수를 추가해줍니다.

//index.ts
import Shape from './Shape';
import Vector from './Vector';
import Circle from './Circle';

export default class App {
static instance: App;

width: number = window.innerWidth;
height: number = window.innerHeight;
canvas: HTMLCanvasElement;
context: CanvasRenderingContext2D;
delta: number = 0;
startTime: number;
frameRequestHandle: number;
shapes: Array<Shape> = [];
mousePosition: Vector = new Vector(0, 0);

constructor() {
App.instance = this;

this.canvas = document.createElement('canvas');
this.canvas.width = this.width;
this.canvas.height = this.height;

this.context = this.canvas.getContext('2d')!;
this.startTime = Date.now();
this.frameRequestHandle = window.requestAnimationFrame(this.frameRequest);

const column = 10;
const row = 10;

const columnWidth = this.width / column;
const columnHeight = this.width / column;

for (let y = 0; y < row; y++) {
for (let x = 0; x < column; x++) {
const position = new Vector(columnWidth * x + columnWidth * 0.5, columnHeight * y + columnHeight * 0.5);
this.shapes.push(new Circle(position));
}
}

document.body.appendChild(this.canvas);
}

onMouseMove = (e: MouseEvent) => {
this.mousePosition = new Vector(e.clientX, e.clientY);
}

frameRequest = () => {
this.frameRequestHandle = window.requestAnimationFrame(this.frameRequest);
const currentTime = Date.now();
this.delta = (currentTime - this.startTime) * 0.001;
this.startTime = currentTime;

this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);

for (let i = 0; i < this.shapes.length; i++) {
this.shapes[i].update(this.delta);
this.shapes[i].render(this.context);
}
}
}

new App();
});

기본적으로 싱글톤 패턴에 사용할 instance 변수와, Circle을 일정한 간격으로 추가해주었습니다.

이제 마우스와 원의 거리에 따라 크기가 달라지게 변경해봅시다.

import Shape from './Shape';
import Vector from './Vector';
import App from './index';

const PI2 = Math.PI * 2;

export default class Circle extends Shape {
color: string;

constructor(position: Vector) {
super(position);

this.color = 'rgba(0, 0, 0)';
}

update(delta: number) {
const distance = Math.max(App.instance.mousePosition.distance(this.position), 10);
}

render(context: CanvasRenderingContext2D) {
context.beginPath();
context.fillStyle = this.color;
context.fill();
}
}

싱글톤패턴으로 App에 아까 추가한 mousePosition을 불러와 길이를 구하고

마우스와 거리가 가까워 질수록 크기가 커지는데 약간 부자연스럽게 뚝뚝 끊기는 부분을 다듬어 봅시다.

// Circle.ts
import Shape from './Shape';
import Vector from './Vector';
import App from './index';

const PI2 = Math.PI * 2;

export default class Circle extends Shape {
color: string;

constructor(position: Vector) {
super(position);

this.color = 'rgba(0, 0, 0)';
}

update(delta: number) {
const distance = Math.max(App.instance.mousePosition.distance(this.position), 10);
}

render(context: CanvasRenderingContext2D) {
context.beginPath();
context.fillStyle = this.color;
}