Поэтому я пытаюсь реализовать концепцию съемки звезды по уже нарисованному холсту медленно движущихся звезд. Но я не нашел способ сделать это. Я попытался реализовать массив, чтобы он выглядел так, но след не так эффективен.
Этот код выглядит следующим образом:
var canvas = document.querySelector('canvas');
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var mouse = {
x : innerWidth/2,
y : innerHeight/2
};
var colors = [
'#3399CC',
'#67B8DE',
'#91C9E8',
'#B4DCED',
'#E8F8FF'
];
addEventListener('resize', function () {
canvas.width = innerWidth;
canvas.height = innerHeight;
init();
});
var isClicked = false;
addEventListener('click', function () {
mouse.x = event.clientX;
mouse.y = event.clientY;
isClicked = true;
});
function randomIntFromRange (min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function randomColor (colors) {
return colors[Math.floor(Math.random() * colors.length)];
}
function Stars (x, y, radius, dy, color) {
this.x = x;
this.y = y;
this.radius = radius;
this.dy = dy;
this.color = color;
this.draw = function () {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
c.shadowColor = this.color;
c.shadowBlur = 15;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.fillStyle = this.color;
c.fill();
c.closePath();
}
this.update = function () {
if (this.y < -10) {
this.y = canvas.height + 10;
this.x = randomIntFromRange(this.radius, canvas.width);
}
this.y -= this.dy;
this.draw();
}
}
function ShootingStar (x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.draw = function () {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
c.shadowColor = "red";
c.shadowBlur = 15;
c.shadowOffsetX = 0;
c.shadowOffsetY = 0;
c.fillStyle = "red";
c.fill();
c.closePath();
}
this.update = function () {
this.x += 10;
this.y += 10;
this.draw();
}
}
let stars = [];
let shooting_star = [];
function init () {
stars = [];
for (var i = 0; i < 300; i++) {
var stars_radius = randomIntFromRange(2, 3);
var stars_x = randomIntFromRange(stars_radius, canvas.width);
var stars_y = randomIntFromRange(stars_radius, canvas.height);
var stars_dy = Math.random() / 6;
var color = randomColor(colors);
stars.push(new Stars(stars_x, stars_y, stars_radius, stars_dy, color));
}
}
function Explode () {
shooting_star = [];
var shooting_star_radius = 3;
var shooting_star_x = mouse.x;
var shooting_star_y = mouse.y;
for (var i = 0; i < 50; i++) {
shooting_star.push(new ShootingStar(shooting_star_x, shooting_star_y, shooting_star_radius));
if (shooting_star_radius > 0.2) {
shooting_star_radius -= .2;
}
var initiator = randomIntFromRange(-1, 1);
console.log(initiator);
shooting_star_x -= 3;
shooting_star_y -= 3;
}
}
function animate () {
requestAnimationFrame(animate);
c.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < stars.length; i++)
stars[i].update();
for (var i = 0; i < shooting_star.length; i++)
shooting_star[i].update();
if (isClicked == true) {
Explode();
isClicked = false;
}
}
init();
animate();
Вот jsfiddle к нему https://jsfiddle.net/qjug4qdz/
Я в основном хочу, чтобы стреляющая звезда исходила из случайного местоположения в точку, где была нажата моя мышь, но тропа трудно работать с использованием массива.
Для конкретного эффекта, который вы ищете, вы можете использовать базовую систему частиц.
По мере того, как стреляющая звезда движется, вы бросаете частицу, которая начинается со скоростью звезды, а затем замедляется и исчезает.
Сначала вы начинаете с частицы. Мне нравится использовать Object.assign
при создании объектов, но вы можете использовать любой способ, который вам нравится, класс, новый, фабричный...
// This defines a particle and is copied to create new particles
const starDust = {
x : 0, // the current position
y : 0,
dx : 0, // delta x,y (velocity)
dy : 0,
drag : 0, // the rate that the particle slows down.
life : 0, // count down till particle is removed
age : 0, // the starting value of life
draw(){ // function to update and draw the particle
this.x += this.dx; // move it
this.y += this.dy;
this.dx *= this.drag; // slow it down
this.dy *= this.drag;
const unitLife = (this.life / this.age); // get the life left as a value
// from 0 to 1 where 0 is end
ctx.globalAlpha = unitLife; // set the alpha
ctx.beginPath();
ctx.arc(this.x,this.y,4,0,Math.PI); // draw the particle
this.life -= 1; // count down
return this.life > 0; // return true if still alive
}
Общей ошибкой при создании систем частиц является то, что люди забывают, что создание и уничтожение объектов добавит много работы в управление памятью javascripts. Хуже всего GC (Garbage Collection). GC является основным источником задержки, и если вы расточительны памяти, это повлияет на качество анимации. Для простых частиц это может быть не заметно, но вы можете захотеть, чтобы сотни сложных частиц порождали каждый кадр. Это когда GC действительно вредит анимации.
Большинство игровых движков уменьшают влияние ГК за счет повторного использования объектов, а не разыгрывания и воссоздания. Общим методом является пул объектов, в котором второй массив содержит объекты, которые больше не используются. Когда нужен новый объект, сначала проверяется пул, если есть неиспользуемый объект, он используется, иначе создается новый объект.
Таким образом, вы никогда не удаляете какие-либо частицы, значительно уменьшая нагрузку на GC и не позволяя анимации снимать кадры (если вы используете много частиц)
Но вам нужно предоставить способ повторной инициализации объекта. Таким образом, добавьте функцию init
к частице, которая установит ее для повторного использования
init(x,y,vx,vy){ // where x,y and velocity vx,vy of shooting star
this.x = x;
this.y = y;
this.dx = vx;
this.dy = vy;
// give a random age
this.age = (Math.random() * 100 + 60) | 0; // in frames and | 0 floors the value
this.life = this.age; // and set the life countdown
this.drag = Math.random() * 0.01 + 0.99; // the drag that slows the particle down
}
} // end of starDust object.
Чтобы управлять всеми частицами, мы создаем объект с массивами и методами для добавления, создания и рендеринга частиц. В этом случае я буду называть это dust
const dust = {
particles : [], // array of active particles
pool : [], // array of unused particels
createParticle(particleDesc){ // creates a new particle from particleDesc
return Object.assign({},particleDesc);
},
add(x,y,vx,vy){ // where x,y and velocity vx,vy
var dust;
if(this.pool.length){ // are there any particles in the pool
dust = this.pool.pop(); // get one
}else{ // else there are no spare particles so create a new one
dust = this.createParticle(starDust);
}
dust.init(x,y,vx,vy); // init the particle
this.items.push(dust); // put it in the active particle array
return dust; // return it (sometimes you want to do something with it)
},
draw(){ // updates and draws all active particles
var i = 0;
while(i < this.items.length){ // iterate each particle in items
if(this.items[i].draw() === false){ // is it dead??
this.pool.push(this.items.splice(i,1)[0]); // if dead put in the pool for later
}else{ i ++ } // if not dead get index of next particle
}
}
}//end of dust object
Самый простой способ создания частицы - использовать случайное число и установить вероятность создания частицы в каждом кадре.
В вашем основном цикле
// assuming that the falling star is called star and has an x,y and dx,dy (delta)
if(star) { // only if there is a start to spawn from
// add a particle once every 10 frame (on average
if(Math.random() < 0.1) {
dust.add(star.x, star.y, star.dx, star.dy); // add some dust at the shooting starts position and speed
}
}
dust.draw(); // draw all particles
И это все.