რთული ანიმაციები
წინა თავში განვიხილეთ რამოდენიმე მარტივი ანიმაცია კანვასში და გავარკვიეთ, თუ როგორ შეიძლება ელემენტების გადაადგილება დროის ინტერვალით კანვასში. ამ თავში კი განვიხილავთ თუ როგორ შეიძლება ანიმაციებზე ფიზიკის დამატება.
ბურთის დახატვა
ერთ-ერთი კარგი მაგალითი არის ბურთი და მისი ფიზიკა. პირველ რიგში შემოვიტანოთ ბურთი ჩვენს კანვასში.
<canvas id="ballCanvas" width="600" height="300"></canvas>
როგორც ყოველთვის, პირველ რიგში გვჭირდება დასახატი კონტექსტი. შევქმნათ ობიექტი, სადაც შევინახავთ ბურთის თვისებებსა და მეთდებს. ამ ყველაფრის ობიექტში შენახვა კარგია, რადგან ნებისმიერ დროს გვექნება წვდომა მის თვისებებსა და მეთოდებზე ერთ ადგილას, თუმცა შესაძლებელია მათი ცალ-ცალკე აღწერაც.
const canvas = document.querySelector('#ballCanvas');
const ctx = canvas.getContext('2d');
const ball = {
x: 100,
y: 100,
radius: 25,
color: 'steelblue',
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
},
};
ball.draw();
ამ მაგალითში არაფერი განსაკუთრებული არ ხდება, ვიღებთ წინა სტატიებში განხილულ arc
მეთოდს, რომ მივიღოთ წრე.
სიჩქარის დამატება
მიღებულ ბურთზე უკვე შეგვიძლია მცირედი ანიმაციების გაწერა. შეგვიძლია წინა თავში შესწავლილი მასალა გამოვიყენოთ ანიმაციის გასაახლებლად. ბურთმა ყოველ მოძრაობაზე უნდა მიიღოს ახალი სიჩქარე და ახალი პოზიცია. ყოველი კადრის (frame) ცვლილების დროს უნდა წავშალოთ ძველი ბურთი და გამოვაჩინოთ ახალი მის ნაცვლად.
const canvas = document.querySelector('#ballCanvas');
const ctx = canvas.getContext('2d');
let raf;
const ball = {
x: 100,
y: 100,
vx: 5,
vy: 2,
radius: 25,
color: 'blue',
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
},
};
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.draw();
ball.x += ball.vx;
ball.y += ball.vy;
raf = window.requestAnimationFrame(draw);
}
canvas.addEventListener('mouseover', (e) => {
raf = window.requestAnimationFrame(draw);
});
canvas.addEventListener('mouseout', (e) => {
window.cancelAnimationFrame(raf);
});
ball.draw();
ამ შემთხვევაში ანიმაციის გასაშვებად ვიყენებთ requestAnimationFrame
მეთოდს, რომელიც მოდის გლობალური window
ობიექტიდან. მეთოდს არგუმენტად ვაძლევთ თუ რომელი ფუნქცია უნდა გაუშვას, რაც ჩვენს შემთხვევაში არის draw
.
ეს მოქმედებები კი ხდება, როცა კანვასზე იქნება მაუსი, ხოლო როცა კანვასი მაუს დატოვებს, ვიყენებთ
window.cancelAnimationFrame
მეთოდს,
რომელიც შეწყვიტავს ბოლოს გამოყენებულ requestAnimationFrame
-ს.
საზღვრები
საზღვრების გარეშე ჩვენი ბურთი უსასრულოდ მიდის კანვასის საზღვრებიდან. ჩვენ უნდა შევამოწმოთ, ყოველ ჯერზე x
და y
პოზიცია
ხომ არ არის კანვასის არეალიდან გასული და, თუ არის, მაშინ საპირისპირო მიმართულებით შევაბრუნოთ ბურთი, რომ საზღვრებს არ გაცდეს.
განახლება გავუკეთოთ ჩვენს draw
ფუნქციას:
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.draw();
ball.x += ball.vx;
ball.y += ball.vy;
if (ball.y + ball.vy > canvas.height - ball.radius || ball.y + ball.vy < ball.radius) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width - ball.radius || ball.x + ball.vx < ball.radius) {
ball.vx = -ball.vx;
}
raf = window.requestAnimationFrame(draw);
}
აჩქარება
იმისათვის, რომ მოძრაობა უფრო რეალური გახდეს, შეგვიძლია სიჩქარე შევცვალოთ შემდეგნაირად:
ball.vy *= 0.99;
ball.vy += 0.25;
ასე ვერტიკალური სიჩქარე (ball.vy
) შენელდება ყოველი კადრის განახლებაზე, რაც ბურთს
უფრო მიიზიდავს ქვემოთკენ.
ამ განახლებებით ჩვენი კოდი უნდა გამოიყურებოდეს შემდეგნაირად:
<canvas id="ballCanvas" style="border: 1px solid black" width="600" height="300"></canvas>
const canvas = document.querySelector('#ballCanvas');
const ctx = canvas.getContext('2d');
let raf;
const ball = {
x: 100,
y: 100,
vx: 5,
vy: 2,
radius: 25,
color: 'steelblue',
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
},
};
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ball.draw();
ball.x += ball.vx;
ball.y += ball.vy;
ball.vy *= 0.99;
ball.vy += 0.25;
if (ball.y + ball.vy > canvas.height - ball.radius || ball.y + ball.vy < ball.radius) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width - ball.radius || ball.x + ball.vx < ball.radius) {
ball.vx = -ball.vx;
}
raf = window.requestAnimationFrame(draw);
}
canvas.addEventListener('mouseover', (e) => {
raf = window.requestAnimationFrame(draw);
});
canvas.addEventListener('mouseout', (e) => {
window.cancelAnimationFrame(raf);
});
ball.draw();
მიმავალი ეფექტი
ჩვენს ბურთს ამჟამად არ აქვს კვალის (Trailing) ეფექტი. ყოველი კადრის განახლებაზე, ჩვენ უბრალოდ
ვასუფთავებთ წინა ბურთს და ვცვლით ახლით. შეგვიძლია clearRect
-ის
ნაცვლად გამოვიყენოთ fillRect
მეთოდი,
რომელიც მარტივი კვალის ეფექტს დაგვიტოვებს.
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
მაუსის კონტროლის დამატება
ბურთზე უფრო მეტი კონტროლისთვის, შეგვიძლია გამოვიყენოთ mousemove
ივენთი, რომ ბურთი მოგვყვებოდეს მაუსის მოძრაობისას, ხოლო როცა მოხდება click
ივენთი, ბურთმა დაიწყოს იგივე მოძრაობა.
<canvas id="ballCanvas" style="border: 1px solid black" width="600" height="300"></canvas>
const canvas = document.querySelector('#ballCanvas');
const ctx = canvas.getContext('2d');
let raf;
let running = false;
const ball = {
x: 100,
y: 100,
vx: 5,
vy: 1,
radius: 25,
color: 'steelblue',
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
},
};
function clear() {
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function draw() {
clear();
ball.draw();
ball.x += ball.vx;
ball.y += ball.vy;
if (ball.y + ball.vy > canvas.height - ball.radius || ball.y + ball.vy < ball.radius) {
ball.vy = -ball.vy;
}
if (ball.x + ball.vx > canvas.width - ball.radius || ball.x + ball.vx < ball.radius) {
ball.vx = -ball.vx;
}
raf = window.requestAnimationFrame(draw);
}
canvas.addEventListener('mousemove', (e) => {
if (!running) {
clear();
ball.x = e.clientX;
ball.y = e.clientY;
ball.draw();
}
});
canvas.addEventListener('click', (e) => {
if (!running) {
raf = window.requestAnimationFrame(draw);
running = true;
}
});
canvas.addEventListener('mouseout', (e) => {
window.cancelAnimationFrame(raf);
running = false;
});
ball.draw();
შეჯამება
ამ თავში განვიხილეთ, თუ როგორ უნდა შევქმნათ შედარებით კომპლექსური ანიმაციები მცირედი ფიზიკის გამოყენებით. სხვადასხვა ივენეთების გამოყენებამ კი მოგვცა უფრო მეტი კონტროლი, რაც უფრო ლამაზსა და გამართულს ხდის ჩვენს ანიმაციას. თუ დავუმატებთ არსებულ ანიმაციას მცირედ პლატფორმას და სხვადასხვა ყუთებს, შეგვიძლია Breakout-ის თამაშიც ავაწყოთ.
კანვასის ბოლო სტატიებში შეგიძლიათ იხილოთ ორი თამაში:
იხილეთ სტატიის სამაგალითო კოდები playground-ში: