HTML5 캔버스와 JavaScript를 사용하여 애니메이션을 만들고 싶습니다. 아이디어는 다음과 같이 다른 객체에 대한 클래스를 작성하는 것입니다.
class Line {
constructor(x1, y1, x2, y2) {
this.x1 = x1;
this.y1 = y2;
...
}
draw() {
}
}
class Circle {
constructor(x, y, radius) {
this.x = x;
...
}
draw() {}
}
...
그런 다음 기본 코드에서해야 할 일은 중간에 일시 중지를 사용하여 모양을 차례로 그리는 것입니다.
let line1 = new Line(x1, y1, x2, y2);
let circle = new Circle(x, y, r);
let line2 = new Line(x1, y1, x2, y2);
line1.draw()
pause()
circle.draw()
pause()
line2.draw()
...
예를 들어 일부 라이브러리를 사용하여 약속 및 중첩 된 콜백 함수를 처리 할 필요가없는 쉬운 방법이 있습니까?
키 프레임을 사용하여 거의 모든 것에 애니메이션 효과를 줄 수 있습니다.
아래의 예 (더 많은 글을 쓰려고했지만 너무 늦어서 대답을 수락했습니다)는 매우 기본적인 키 프레임 유틸리티가 애니메이션을 만드는 방법을 보여줍니다.
키 프레임은 단지 인 time
과value
값에 이름을 부여하는 트랙에 키 프레임이 추가됩니다.
따라서 이름 x
(위치) 및 키 {time : 0, value : 100}, {time : 1000, value : 900}은 0 ~ 1 초 동안 x
속성을에서 100
로 변경합니다.900
예를 들어 원
const circle = {
x: 0,
y: 0,
r: 10,
col : "",
draw() {
ctx.fillStyle = this.col;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill()
}
};
시간이 지남에 따라 속성이 변경 될 수 있습니다.
먼저 트랙 객체를 만들고 키를 정의합니다.
const circleTracks = createTracks();
// properties to animate
circleTracks.addTrack("x");
circleTracks.addTrack("y");
circleTracks.addTrack("r");
circleTracks.addTrack("col");
그런 다음 특정 타임 스탬프에 키 프레임을 추가합니다.
circleTracks.addKeysAtTime(0, {x: 220, y :85, r: 20, col: "#F00"});
circleTracks.addKeysAtTime(1000, {x: 220, y :50, r: 50, col: "#0F0"});
circleTracks.addKeysAtTime(2000, {x: 420, y :100, r: 20, col: "#00F"});
circleTracks.addKeysAtTime(3000, {x: 180, y :160, r: 10, col: "#444"});
circleTracks.addKeysAtTime(4000, {x: 20, y :100, r: 20});
circleTracks.addKeysAtTime(5000, {x: 220, y :85, r: 10, col: "#888"});
circleTracks.addKeysAtTime(5500, {r: 10, col: "#08F"});
circleTracks.addKeysAtTime(6000, {r: 340, col: "#00F"});
준비가되면 키를 정리하십시오 (시간 순서없이 추가 할 수 있음).
circleTracks.clean();
시작을 추구
circleTracks.seek(0);
그리고 개체를 업데이트
circleTracks.update(circle);
애니메이션을 적용하려면 눈금 및 업데이트 함수를 호출하고 원을 그립니다.
circleTracks.tick();
circleTracks.update(circle);
circle.draw();
애니메이션을 시작하려면 클릭하십시오. 끝났 으면 다음을 사용하여 애니메이션을 스크럽 할 수 있습니다.tracks.seek(time)
가장 기본적인 키 프레임 애니메이션입니다.
키 프레임의 가장 좋은 점은 애니메이션을 코드에서 분리하여 애니메이션을 간단한 데이터 구조로 가져오고 내보낼 수 있다는 것입니다.
const ctx = canvas.getContext("2d");
requestAnimationFrame(mainLoop);
const allTracks = [];
function addKeyframedObject(tracks, object) {
tracks.clean();
tracks.seek(0);
tracks.update(object);
allTracks.push({tracks, object});
}
const FRAMES_PER_SEC = 60, TICK = 1000 / FRAMES_PER_SEC; //
const key = (time, value) => ({time, value});
var playing = false;
var showScrubber = false;
var currentTime = 0;
function mainLoop() {
ctx.clearRect(0 ,0 ,ctx.canvas.width, ctx.canvas.height);
if(playing) {
for (const animated of allTracks) {
animated.tracks.tick();
animated.tracks.update(animated.object);
}
}
for (const animated of allTracks) {
animated.object.draw();
}
if(showScrubber) {
slide.update();
slide.draw();
if(slide.value !== currentTime) {
currentTime = slide.value;
for (const animated of allTracks) {
animated.tracks.seek(currentTime);
animated.tracks.update(animated.object);
}
}
} else {
if(mouse.button) { playing = true }
}
if(allTracks[0].tracks.time > 6300) {
showScrubber = true
playing = false;
}
requestAnimationFrame(mainLoop);
}
const text = {
x: canvas.width / 2,
y: canvas.height / 2,
alpha: 1,
text: "",
draw() {
ctx.font = "24px arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#000";
ctx.globalAlpha = this.alpha;
ctx.fillText(this.text, this.x, this.y);
ctx.globalAlpha = 1;
}
}
const circle = {
x: 0,
y: 0,
r: 10,
col : "",
draw() {
ctx.fillStyle = this.col;
ctx.beginPath();
ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
ctx.fill()
}
}
const circleTracks = createTracks();
circleTracks.addTrack("x");
circleTracks.addTrack("y");
circleTracks.addTrack("r");
circleTracks.addTrack("col");
circleTracks.addKeysAtTime(0, {x: 220, y :85, r: 20, col: "#F00"});
circleTracks.addKeysAtTime(1000, {x: 220, y :50, r: 50, col: "#0F0"});
circleTracks.addKeysAtTime(2000, {x: 420, y :100, r: 20, col: "#00F"});
circleTracks.addKeysAtTime(3000, {x: 180, y :160, r: 10, col: "#444"});
circleTracks.addKeysAtTime(4000, {x: 20, y :100, r: 20});
circleTracks.addKeysAtTime(5000, {x: 220, y :85, r: 10, col: "#888"});
circleTracks.addKeysAtTime(5500, {r: 10, col: "#08F"});
circleTracks.addKeysAtTime(6000, {r: 340, col: "#00F"});
addKeyframedObject(circleTracks, circle);
const textTracks = createTracks();
textTracks.addTrack("alpha");
textTracks.addTrack("text");
textTracks.addKeysAtTime(0, {alpha: 1, text: "Click to start"});
textTracks.addKeysAtTime(1, {alpha: 0});
textTracks.addKeysAtTime(20, {alpha: 0, text: "Simple keyframed animation"});
textTracks.addKeysAtTime(1000, {alpha: 1});
textTracks.addKeysAtTime(2000, {alpha: 0});
textTracks.addKeysAtTime(3500, {alpha: 0, text: "The END!" });
textTracks.addKeysAtTime(3500, {alpha: 1});
textTracks.addKeysAtTime(5500, {alpha: 1});
textTracks.addKeysAtTime(6000, {alpha: 0, text: "Use slider to scrub"});
textTracks.addKeysAtTime(6300, {alpha: 1});
addKeyframedObject(textTracks, text);
function createTracks() {
return {
tracks: {},
addTrack(name, keys = [], value) {
this.tracks[name] = {name, keys, idx: -1, value}
},
addKeysAtTime(time, keys) {
for(const name of Object.keys(keys)) {
this.tracks[name].keys.push(key(time, keys[name]));
}
},
clean() {
for(const track of Object.values(this.tracks)) {
track.keys.sort((a,b) => a.time - b.time);
}
},
seek(time) { // seek to random time
this.time = time;
for(const track of Object.values(this.tracks)) {
if (track.keys[0].time > time) {
track.idx = -1; // befor first key
}else {
let idx = 1;
while(idx < track.keys.length) {
if(track.keys[idx].time > time && track.keys[idx-1].time <= time) {
track.idx = idx - 1;
break;
}
idx += 1;
}
}
}
this.tick(0);
},
tick(timeStep = TICK) {
const time = this.time += timeStep;
for(const track of Object.values(this.tracks)) {
if(track.keys[track.idx + 1] && track.keys[track.idx + 1].time <= time) {
track.idx += 1;
}
if(track.idx === -1) {
track.value = track.keys[0].value;
} else {
const k1 = track.keys[track.idx];
const k2 = track.keys[track.idx + 1];
if (typeof k1.value !== "number" || !k2) {
track.value = k1.value;
} else if (k2) {
const unitTime = (time - k1.time) / (k2.time - k1.time);
track.value = (k2.value - k1.value) * unitTime + k1.value;
}
}
}
},
update(obj) {
for(const track of Object.values(this.tracks)) {
obj[track.name] = track.value;
}
}
};
};
const slide = {
min: 0,
max: 6300,
value: 6300,
top: 160,
left: 1,
height: 9,
width: 438,
slide: 10,
slideX: 0,
draw() {
ctx.fillStyle = "#000";
ctx.fillRect(this.left-1, this.top-1, this.width+ 2, this.height+ 2);
ctx.fillStyle = "#888";
ctx.fillRect(this.left, this.top, this.width, this.height);
ctx.fillStyle = "#DDD";
this.slideX = (this.value - this.min) / (this.max - this.min) * (this.width - this.slide) + this.left;
ctx.fillRect(this.slideX, this.top + 1, this.slide, this.height - 2);
},
update() {
if(mouse.x > this.left && mouse.x < this.left + this.width &&
mouse.y > this.top && mouse.y < this.top + this.height) {
if (mouse.button && !this.captured) {
this.captured = true;
} else {
canvas.style.cursor = "ew-resize";
}
}
if (this.captured) {
if (!mouse.button) {
this.captured = false;
canvas.style.cursor = "default";
} else {
this.value = ((mouse.x - this.left) / this.width) * (this.max - this.min) + this.min;
canvas.style.cursor = "none";
this.value = this.value < this.min ? this.min : this.value > this.max ? this.max : this.value;
}
}
}
};
const mouse = {x : 0, y : 0, button : false};
function mouseEvents(e){
const bounds = canvas.getBoundingClientRect();
mouse.x = e.pageX - bounds.left - scrollX;
mouse.y = e.pageY - bounds.top - scrollY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));
canvas { border: 1px solid black; }
<canvas id="canvas" width="440" height="170"><canvas>
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다