canvas 태그 사용법
2d 객체들은
const ctx = canvas.getContext("2d");
이후 ctx.명령어로 사용
- ctx.beginPath(); : 그리기 시작, 새로 호출하면 이전과 다른 독립적인 시작점이 생성된다. fill은 이 하나의 beginPath 지점 안에서 모든색을 채운다. beginPath 는 clothPath 로서 끝나는게 아니라 새로운 beginPath 선언으로서만 끝난다**
- stroke 의 경우 하나의 beginPath 안에서 새로 선언될 때 까지 연속적으로 선으로 그려지는 개체
- rect 는 Stroke 속성이 아닌 객체 속성이므로 beginPath의 영향을 받지 않는다.
- arc 는 stroke 속성이다. 순식간에 원을 그려낼 뿐이다.
- ctx.moveTo(x, y); : Stroke의 시작좌표 설정, 혹은 모든 객체의 시작좌표
- beginPath 와 moveTo 가 선언되지 않아도 자동으로 한 번 선언되는 시점이 있는데(계속 선언된다면 stroke 가 이어지지 않을 것이므로,) 이건 연구중,
- ctx.lineTo(x, y); : Stroke의 이동좌표 설정, 혹은 모든 객체의 이동좌표
- ctx.closePath(); : 현재 Stroke 의 마지막 좌표와 시작 좌표를 연결하여 경로를 닫습니다. 경로를 닫으면, 열린 경로와 달리 시작 점과 끝 점이 연결되어 하나의 완전한 도형을 이루게 됩니다.
- ctx.stroke(); : 하나의 beginPath 안에서 선으로 이어낸다.
- ctx.fillRect(x, y, width, height); : 네모 만들기
- ctx.strokeRect(x, y, width, height); : 테두리 네모 만들기 보기와 달리 stroke 속성이 아니다.
- ctx.arc(x, y, width, height, 0, Math.PI, 시계방향(boolean)); : stroke 의 형태로 순식간에 원을 그려낸다. fill 을 사용하면 채워진 원이 당연히 생성될 것이며, clothPath 이후에 놓아도 채워진다. 그로므로 stroke 선언을 해놓지 않으면 작동하지 않는다.
- ctx.fill(); : stroke 의 속성으로 하나의 beginpath 안에서 경로를 채우기, arc는 기본적으로 경로를 삥 두르는 형태의 명령어라 이걸 쓰면 원이 되는데, lineTo 를 통해 특정 경로를 그리면서 테스트해보면 면적을 채우면 그곳에 색이 채워진다. 그림판의 페인트통이라고 생각하면 될 듯 꼭 Stroke 선언을 하지 않아도 채워진다.
- ctx.fillStyle = "red"; : 채우기 색깔 조정
- ctx.strokeStyle = "red"; : 테두리 색깔 조정
- ctx.lineWidth = 20; : 테두리 굵기설정
- ctx.lineCap = "round"; : 테두리 끝 둥글게
- ctx.drawImage(객체, x, y, width, height) : 이미지 그려내기
- ctx.clearRect(x, y, w, h) : 캔버스의 해당 좌표값 해당 크기만큼 객체를 지워낸다.
- const @ = new Image(); : 이미지 태그 추가 가능
@.src = "./img/01.jpg";
const newImg = new Image();
newImg.src = "../img/img_02.jpg";
newImg.addEventListener("load", function() {
ctx.drawImage(newImg, x, y, width, height)
혹은
ctx.drawImage(newImg, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
});
문제는 굳이 이개쌉지랄을 해서 사진을 추가해야 한다는 점,
drawImage 안에는 변수와 나올 x, y 좌표도 적어줘야 한다는 점,
뒤의 dx, dy, dWidth, dHeight 까지 적었다면, 앞에 쓰여지는 값은 이미 나온 사진 안에서 다시 지정하는 좌표값이다.
requestAnimationFrame(재귀함수 구조로 만들어야 함)애니메이션이 일어나야되는 객체는 어레이에 오브젝트 형태로 넣고 안에 그 오브젝트를 그려내는 위치나 크기의 기본값과 애니메이팅이 일어날때 변화되는 상황까지 함수형태로 만들어 하나의 데이터로 넣어놓는다. 이후 재귀함수에서 map 문으로 애니메이션이 일어나야할 여러 요소들을 동시에 애니메이팅 함수로 무한반복 실행시키는 것.
기본적으로 그려낼 객체 데이터는 기본 데이터로, 객체 그려내기와 애니메이션이 일어날 데이터 수정상황은 함수로써 넣어놓는다.
예시)
// 애니메이션 객체 생성자 함수
function CreateItem(n) {
// 그려낼 객체의 기본 좌표 및 크기값 설정 이 값들이 곧 그려질 객체의 값이다.
// this 값으로 지정하여 새로 만들어질 오브젝트 형태에서 기본 데이터가 된다.
this.x = random(cSize.w),
this.y = random(cSize.h),
this.w = n,
this.h = n,
this.c = `rgb(${random(255)}, ${random(255)}, ${random(255)})`;
this.dx = random(5);
// 객체를 그려내는 함수와애니메이션이 일어나야 할 상황을 함수로 한번 더 안에 만들어준다.
this.draw = function () {
ctx.beginPath();
ctx.globalAlpha = 0.6;
ctx.fillStyle = this.c;
ctx.fillRect(this.x, this.y, this.w, this.h);
// ctx.arc(this.x, this.y, this.w, this.h, 0, Math.PI * 2);
// ctx.fill();
// 재귀함수로 무한히 일어나며 작동하므로 *연산자 함수를 주로 사용한다.
// lineTo 이용하여 좌표값 수정하며 애니메이팅
if(this.x > cSize.w || this.x < 0){
this.dx *= -1;
ctx.lineTo(this.x += this.dx, this.y);
} else {
ctx.lineTo(this.x += this.dx, this.y);
}
}
// 좌표값을 직접 수정하며 애니메이팅
// draw 안에 그려냄과 애니메이션이 하나로 합쳐져 있으나 이렇게 애니메이션만 따로 떼어놓을수도 있음.
// this.sliding = function () {
// this.y += this.dx;
// if (this.y > cSize.h || this.y < 0) {
// this.dx *= -1;
// }
// }
}
// 이후 for 문 사용하여 위의 요소를 얼만큼 만들어 어레이에 넣을지 정한다.
// 혹은 어레이안에 특정 객체 데이터를 직접 넣어 실행시킬 수도 있다.
let item = [];
for (let i = 0; i < 20; i++) {
const a = new CreateItem(random(100));
item.push(a);
}
// 그리고 맨위에 설명한 애니메이션 재귀함수를 넣어 아까 안에 넣어놓은 애니메이션 함수를 실행시킨다.
function aniDraw() {
ctx.clearRect(0, 0, cSize.w, cSize.h)
// 특정 순서까지만 작동시키고 싶다면 for 문 작동
// for(let i=0; i<20; i++) {
// item[i].sliding()
// item[i].draw()
// }
// 모든 객체가 모두작동해야 한다면 map 이 적절하다.
item.map(item => {
item.sliding();
item.draw();
return item;
})
// aniDraw() 원래 아렇게 들어와 있다고 보면 된다. 무한 랜더링으로 그려내는 것,
requestAnimationFrame(aniDraw);
}
aniDraw();
위와같이, 그려내는 함수가 작동했을떄 본인을 또다시 작동시키는 재귀함수 형태를 띄며 반드시
함수 안에requestAnimationFrame(함수본인이름)형태로 들어간다.
const newVideo = document.createElement("video");
newVideo.src = "../img/vid_running.mp4";
newVideo.muted = "muted";
newVideo.play();
newVideo.addEventListener("load", function(event) {
console.log(event);
});
load : 그냥 불러오기로 발생 근데 왜 발생 안돼지???
newVideo.addEventListener("loadedmetadata", function(event) {
console.log(event);
});
loadedmetadata : 재생시간정보 확인
newVideo.addEventListener("loadeddata", function(event) {
console.log(event);
});
loadeddatas : 동영상 프레임이 조금이라도 준비가 되면 발생
newVideo.addEventListener("canplay", function(event) {
console.log(event);
});
canplay : 동영상 재생시 이벤트 발생
newVideo.addEventListener("canplaythrough", function(event) {
console.log(event);
});
canplaythrough : 동영상 전체가 끊임없이 재생할 수 있을때 발생
let CA;
function videoDraw() {
ctx.drawImage(newVideo, 600, 100, 300, 200);
if(!newVideo.ended) {
CA = requestAnimationFrame(videoDraw);
} else {
cancelAnimationFrame(CA);
}
}
이후 똑같이 drawImage 사용하여 창 만들고 requestAnimationFrame(재귀함수)하여 함수를 계속 실행시켜 애니메이션 재생시킴.
cancelAnimationFrame(requestAnimationFrame(videoDraw)) : 반대로 애니메이션 프레임을 정지시킴.
drawImage 는 원래 한번만 그려주는 명령문이기 때문에 계속 실행시켜서 애니메이팅 하는 원리임
이걸 이용하면 원이 그려지게도 할 수 있는데,
let num; num++ arc 명령어에서 Math.PI * num/100 하고 리퀘스트애니메이션 하면 원이 그려짐