javascript - setTimeout 我做错了吗?用于烟花
问题描述
所以我试图让 clearInterval 工作。这来自:https ://codepen.io/whqet/pen/Auzch (我对其进行了一些修改以使其更符合我的需要。本质上,我希望动画在 10000 毫秒后停止。请原谅混乱的编码,我在底部放了一个计时器,这样我就可以看到它是否可以工作。任何帮助将不胜感激。谢谢!
var canvas = document.getElementById( 'canvas' ),
ctx = canvas.getContext( '2d' ),
// full screen dimensions
cw = window.innerWidth,
ch = window.innerHeight,
// firework collection
fireworks = [],
// particle collection
particles = [],
// starting hue
hue = 120,
// when launching fireworks with a click, too many get launched at once without a limiter, one launch per 5 loop ticks
limiterTotal = 5,
limiterTick = 0,
// this will time the auto launches of fireworks, one launch per 60 loop ticks
timerTotal = 60,
timerTick = 0,
mousedown = false,
// mouse x coordinate,
mx,
// mouse y coordinate
my;
// set canvas dimensions
canvas.width = cw;
canvas.height = ch;
// now we are going to setup our function placeholders for the entire demo
// get a random number within a range
function random( min, max ) {
return Math.random() * ( max - min ) + min;
}
// calculate the distance between two points
function calculateDistance( p1x, p1y, p2x, p2y ) {
var xDistance = p1x - p2x,
yDistance = p1y - p2y;
return Math.sqrt( Math.pow( xDistance, 2 ) + Math.pow( yDistance, 2 ) );
}
// create firework
function Firework( sx, sy, tx, ty ) {
// actual coordinates
this.x = sx;
this.y = sy;
// starting coordinates
this.sx = sx;
this.sy = sy;
// target coordinates
this.tx = tx;
this.ty = ty;
// distance from starting point to target
this.distanceToTarget = calculateDistance( sx, sy, tx, ty );
this.distanceTraveled = 0;
// track the past coordinates of each firework to create a trail effect, increase the coordinate count to create more prominent trails
this.coordinates = [];
this.coordinateCount = 3;
// populate initial coordinate collection with the current coordinates
while( this.coordinateCount-- ) {
this.coordinates.push( [ this.x, this.y ] );
}
this.angle = Math.atan2( ty - sy, tx - sx );
this.speed = 2;
this.acceleration = 1.05;
this.brightness = random( 50, 70 );
// circle target indicator radius
this.targetRadius = 1;
}
// update firework
Firework.prototype.update = function( index ) {
// remove last item in coordinates array
this.coordinates.pop();
// add current coordinates to the start of the array
this.coordinates.unshift( [ this.x, this.y ] );
// cycle the circle target indicator radius
if( this.targetRadius < 8 ) {
this.targetRadius += 0.3;
} else {
this.targetRadius = 1;
}
// speed up the firework
this.speed *= this.acceleration;
// get the current velocities based on angle and speed
var vx = Math.cos( this.angle ) * this.speed,
vy = Math.sin( this.angle ) * this.speed;
// how far will the firework have traveled with velocities applied?
this.distanceTraveled = calculateDistance( this.sx, this.sy, this.x + vx, this.y + vy );
// if the distance traveled, including velocities, is greater than the initial distance to the target, then the target has been reached
if( this.distanceTraveled >= this.distanceToTarget ) {
createParticles( this.tx, this.ty );
// remove the firework, use the index passed into the update function to determine which to remove
fireworks.splice( index, 1 );
} else {
// target not reached, keep traveling
this.x += vx;
this.y += vy;
}
}
// draw firework
Firework.prototype.draw = function() {
ctx.beginPath();
// move to the last tracked coordinate in the set, then draw a line to the current x and y
ctx.moveTo( this.coordinates[ this.coordinates.length - 1][ 0 ], this.coordinates[ this.coordinates.length - 1][ 1 ] );
ctx.lineTo( this.x, this.y );
ctx.strokeStyle = 'hsl(' + hue + ', 100%, ' + this.brightness + '%)';
ctx.stroke();
ctx.beginPath();
// draw the target for this firework with a pulsing circle
ctx.arc( this.tx, this.ty, this.targetRadius, 0, Math.PI * 2 );
ctx.stroke();
}
// create particle
function Particle( x, y ) {
this.x = x;
this.y = y;
// track the past coordinates of each particle to create a trail effect, increase the coordinate count to create more prominent trails
this.coordinates = [];
this.coordinateCount = 5;
while( this.coordinateCount-- ) {
this.coordinates.push( [ this.x, this.y ] );
}
// set a random angle in all possible directions, in radians
this.angle = random( 0, Math.PI * 2 );
this.speed = random( 1, 10 );
// friction will slow the particle down
this.friction = 0.95;
// gravity will be applied and pull the particle down
this.gravity = 1;
// set the hue to a random number +-50 of the overall hue variable
this.hue = random( hue - 50, hue + 50 );
this.brightness = random( 50, 80 );
this.alpha = 1;
// set how fast the particle fades out
this.decay = random( 0.015, 0.03 );
}
// update particle
Particle.prototype.update = function( index ) {
// remove last item in coordinates array
this.coordinates.pop();
// add current coordinates to the start of the array
this.coordinates.unshift( [ this.x, this.y ] );
// slow down the particle
this.speed *= this.friction;
// apply velocity
this.x += Math.cos( this.angle ) * this.speed;
this.y += Math.sin( this.angle ) * this.speed + this.gravity;
// fade out the particle
this.alpha -= this.decay;
// remove the particle once the alpha is low enough, based on the passed in index
if( this.alpha <= this.decay ) {
particles.splice( index, 1 );
}
}
// draw particle
Particle.prototype.draw = function() {
ctx. beginPath();
// move to the last tracked coordinates in the set, then draw a line to the current x and y
ctx.moveTo( this.coordinates[ this.coordinates.length - 1 ][ 0 ], this.coordinates[ this.coordinates.length - 1 ][ 1 ] );
ctx.lineTo( this.x, this.y );
ctx.strokeStyle = 'hsla(' + this.hue + ', 100%, ' + this.brightness + '%, ' + this.alpha + ')';
ctx.stroke();
}
// create particle group/explosion
function createParticles( x, y ) {
// increase the particle count for a bigger explosion, beware of the canvas performance hit with the increased particles though
var particleCount = 300;
while( particleCount-- ) {
particles.push( new Particle( x, y ) );
}
}
// main demo loop
function loop() {
// this function will run endlessly with requestAnimationFrame
requestAnimFrame( loop );
// increase the hue to get different colored fireworks over time
//hue += 0.5;
// create random color
hue= random(0, 360 );
// normally, clearRect() would be used to clear the canvas
// we want to create a trailing effect though
// setting the composite operation to destination-out will allow us to clear the canvas at a specific opacity, rather than wiping it entirely
ctx.globalCompositeOperation = 'destination-out';
// decrease the alpha property to create more prominent trails
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect( 0, 0, cw, ch );
// change the composite operation back to our main mode
// lighter creates bright highlight points as the fireworks and particles overlap each other
ctx.globalCompositeOperation = 'lighter';
// loop over each firework, draw it, update it
var i = fireworks.length;
while( i-- ) {
fireworks[ i ].draw();
fireworks[ i ].update( i );
}
// loop over each particle, draw it, update it
var i = particles.length;
while( i-- ) {
particles[ i ].draw();
particles[ i ].update( i );
}
// launch fireworks automatically to random coordinates, when the mouse isn't down
if( timerTick >= timerTotal ) {
if( !mousedown ) {
// start the firework at the bottom middle of the screen, then set the random target coordinates, the random y coordinates will be set within the range of the top half of the screen
fireworks.push( new Firework( cw / 2, ch, random( 0, cw ), random( 0, ch / 2 ) ) );
timerTick = 0;
}
} else {
timerTick++;
}
// limit the rate at which fireworks get launched when mouse is down
if( limiterTick >= limiterTotal ) {
if( mousedown ) {
// start the firework at the bottom middle of the screen, then set the current mouse coordinates as the target
fireworks.push( new Firework( cw / 2, ch, mx, my ) );
limiterTick = 0;
}
} else {
limiterTick++;
}
}
// mouse event bindings
// update the mouse coordinates on mousemove
canvas.addEventListener( 'mousemove', function( e ) {
mx = e.pageX - canvas.offsetLeft;
my = e.pageY - canvas.offsetTop;
});
// toggle mousedown state and prevent canvas from being selected
canvas.addEventListener( 'mousedown', function( e ) {
e.preventDefault();
mousedown = true;
});
canvas.addEventListener( 'mouseup', function( e ) {
e.preventDefault();
mousedown = false;
});
// once the window loads, we are ready for some fireworks!
window.onload = loop;
// when animating on canvas, it is best to use requestAnimationFrame instead of setTimeout or setInterval
window.requestAnimFrame = ( function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( ) {
window.setTimeout( timePeriodms );
};
function stopFireworks () {
var timePeriodms = 10000;
window.clearTimeout(timePeriodms);
};
})();
// now we will setup our basic variables for the demo
var minute = 0;
var sec = 00;
var zeroPholder = 0;
var counterIdv2 = setInterval(function(){
countUp2();
}, 1000);
function countUp2 () {
sec++;
if(sec == 60){
sec = 00;
minute = minute + 1;
}
if (minute == 0 && sec == 1) {
document.getElementById('count-up-2').style.color = 'red';
}
if (minute == 0 && sec == 59) {
document.getElementById('count-up-2').style.color = 'blue';
}
if (minute == 10 && sec == 00) {
document.getElementById('count-up-2').style.color = 'red';
}
if(sec == 10){
zeroPholder = '';
}else
if(sec == 00){
zeroPholder = 0;
}
document.getElementById("count-up-2").innerText = minute+':'+zeroPholder+sec;
}
body {
background: #000;
margin: 0;
}
canvas {
display: block;
}
<canvas id="canvas">Canvas is not supported in your browser.</canvas>
<table border="1" style="border-color:black;">
<tbody>
<tr>
<td style="background-color: #fff; padding: 5px;"><span>Time spent on page: </span><span id="count-up-2">0:00</span>
</td>
</tr>
</tbody>
</table>
解决方案
requestAnimationFrame回调的参数
当requestAnimationFrame调用回调函数时,它以毫秒为单位提供高精度时间。您可以使用这段时间来控制动画的时间。
例如,以下动画循环将在调用第一帧后 10 秒停止。
requestAnimationFrame(mainLoop);
var startTime;
const endTime = 10000; // in ms
function mainLoop(time) {
if (startTime === undefined) { startTime = time }
if (time - startTime > endTime) {
console.log("Animation end.");
return;
}
// draw animated content
requestAnimationFrame(mainLoop);
}
与使用相比,这为您提供了更好的动画时间控制,setTimeout
特别是如果您有不止一件事来控制时间(参见演示)
演示
该演示使用相同的方法来倒计时和控制烟花。烟花结束前几秒钟被阻止,然后在一群人被发射前一秒钟以零及时爆炸(嗯,大约!!!)
// Code based loosely on OPs code in so far as it animates fireworks.
// There is no control of fireworks via mouse.
// Uses reusable buffers to avoid GC overhead in low end devices.
const ctx = canvas.getContext( '2d' );
const GRAVITY = 0.2; // per frame squared
var cw = canvas.width = innerWidth - 40;
var ch = canvas.height = 500;
const iH = innerHeight;
var startTime;
const DISPLAY_TIME = 10000; // in ms. Time to countdown
const SHELL_X = cw / 2; // Location that shells are fired from in px
const SHELL_Y = ch;
const SHELL_TIME = 100; // in frames
const MAX_SHELLS = 8;
const MAX_PARTICLES = 1000; // Approx max particles.
const SHELL_RANDOM_RATE = 0.01; // cof of shell random fire control
const SHELL_FIRE_CURVE = 3; // Highest power of fire control exponents
var randomFire = 0; // holds the odds of a random shell being fired
Math.TAU = Math.PI * 2;
Math.PI90 = Math.PI / 2;
Math.PI270 = Math.PI + Math.PI90;
Math.rand = (m, M) => Math.random() * (M - m) + m;
Math.distance = (x1, y1, x2, y2) => ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5;
requestAnimationFrame(mainLoop);
function mainLoop(time) {
if (startTime === undefined) { startTime = time }
const timer = DISPLAY_TIME - (time - startTime);
const displayTime = timer / 1000 | 0;
if (timer <= 0) {
countdown.textContent = "HAPPY NEW YEAR 2020";
if(particles.size === 0 && shells.size === 0) {
ctx.clearRect(0,0,cw,ch);
shells.clear();
startTime = undefined;
countdown.textContent = "Click to restart";
canvas.addEventListener("click",() => requestAnimationFrame(mainLoop), {once: true});
return; // ends update
} else {
randomFire = 0; // pervent shells firing after zero time
}
} else {
countdown.textContent !== displayTime && (countdown.textContent = displayTime);
}
ctx.lineCap = "round";
ctx.globalCompositeOperation = 'destination-out';
ctx.globalAlpha = 0.2;
ctx.fillStyle = "#000"
ctx.fillRect( 0, 0, cw, ch );
ctx.globalCompositeOperation = 'lighter';
shells.update();
particles.update();
ctx.lineWidth = 2;
shells.draw();
ctx.lineWidth = 3;
particles.draw();
if (timer < 2500 && timer > 1000) { randomFire = 0 }
else if(timer <= 1000 && timer > 0) { randomFire = 1 }
if(shells.size < MAX_SHELLS && particles.size < MAX_PARTICLES) {
if(Math.random() < randomFire ** SHELL_FIRE_CURVE) {
randomFire = 0;
shells.fire(
Math.rand(cw * (1/3), cw *(2/3)),
Math.rand(iH * (3/4), iH *(4/4)),
SHELL_TIME
);
}
randomFire += SHELL_RANDOM_RATE;
}
requestAnimationFrame(mainLoop);
}
function Trail() {}
function Particle() { }
function Shell( sx, sy, tx, ty ) {
this.trail = new Trail();
this.init(sx, sy,tx,sy);
}
Trail.prototype = {
init(x, y) {
this.x1 = this.x2 = this.x3 = x;
this.y1 = this.y2 = this.y3 = y;
},
update(x, y) {
this.x3 = this.x2
this.y3 = this.y2
this.x2 = this.x1
this.y2 = this.y1
this.x1 = x;
this.y1 = y;
},
draw() {
ctx.moveTo(this.x1, this.y1);
ctx.lineTo(this.x2, this.y2);
ctx.lineTo(this.x3, this.y3);
}
};
Shell.prototype = {
init(x, y, time) {
this.x = SHELL_X;
this.y = SHELL_Y;
this.sx = (x - this.x) / (time / 2);
this.sy = ((y - this.y) * (GRAVITY / ((time) ** 0.5)))* 2;
this.power = (-this.sy * 10) | 0;
this.hue = Math.rand(360, 720) % 360 | 0;
this.active = true;
this.trail.init(this.x, this.y);
this.time = time / 2;
this.life = time / 2;
},
explode() {
this.active = false;
particles.explode(this, this.power);
},
update() {
this.time -= 1;
if (this.time <= 0) { this.explode() }
this.sy += GRAVITY;
this.x += this.sx;
this.y += this.sy;
this.trail.update(this.x, this.y);
return this.active;
},
draw() {
ctx.strokeStyle = `hsl(${this.hue},100%,${(this.time / this.life) * 100}%)`;
ctx.beginPath();
this.trail.draw();
ctx.stroke();
},
};
Particle.prototype = {
init(shell) {
this.x2 = this.x1 = this.x = shell.x;
this.y2 = this.y1 = this.y = shell.y;
this.dx = shell.sx;
this.dy = shell.sy;
this.angle = Math.rand(0, Math.TAU);
const zAng = Math.cos(Math.random() ** 2 * Math.PI)
this.speed = zAng * shell.power / 30;
this.friction = 0.95;
this.gravity = GRAVITY;
this.hue = (Math.rand(shell.hue - 5, shell.hue + 5) + 360) % 360;
this.brightness = Math.rand( 25, 50 );
this.alpha = shell.power / 10;
this.decay = Math.rand( 0.2, 0.5);
this.active = true;
},
update() {
const dx = Math.cos(this.angle);
const dy = Math.sin(this.angle);
this.x2 = this.x1;
this.y2 = this.y1;
this.x1 = this.x - dx;
this.y1 = this.y + dy;
this.speed *= this.friction;
this.x += (this.dx *= 0.9);
this.y += (this.dy *= 0.9);
this.dy += GRAVITY / 100;
this.x += dx * this.speed;
this.y += dy * this.speed;
this.alpha -= this.decay;
if( this.alpha <= 0 || this.x < 0 || this.y < 0 || this.x > cw) {
this.active = false;
}
return this.active;
},
draw() {
const alpha = this.alpha / 5 > 1 ? 1 : this.alpha / 5;
const lum = this.brightness + this.alpha
ctx.strokeStyle = `hsla(${this.hue},100%,${lum<100 ? lum : 100}%,${alpha})`;
ctx. beginPath();
ctx.moveTo( this.x2, this.y2);
ctx.lineTo( this.x, this.y );
ctx.stroke();
}
};
function BubbleArray(extension) {
return Object.assign([], {
size: 0,
update() {
var read = 0, write = 0;
while (read < this.size) {
const item = this[read];
if(read !== write) {
const temp = this[write]
this[write] = item;
this[read] = temp;
}
item.update() === true && (write ++);
read++;
}
this.size = write;
},
draw() {
var i = 0,len = this.size;
while(i < len) { this[i++].draw() }
},
add(item) {
this.size ++;
this.push(item);
},
clear() { this.length = this.size = 0 },
getInactive() { return this.size < this.length ? this[this.size++] : undefined },
},
extension,
);
}
const particles = BubbleArray({
explode(shell, count) {
var item;
while(count-- > 0) {
!(item = this.getInactive()) && this.add(item = new Particle());
item.init(shell);
}
},
});
const shells = BubbleArray({
fire(tx = mx, ty = my) {
var item;
!(item = this.getInactive()) && this.add(item = new Shell());
item.init(tx, ty, 100);
}
});
body {
padding: 0px;
}
canvas {
background: #000;
position: absolute;
top: 0px;
left: 0px;
}
#countdown {
position: absolute;
top: 20px;
left: 20px;
font-family: arial;
font-size: xx-large;
color: white;
}
<canvas id="canvas"></canvas>
<div id="countdown"></div>
推荐阅读
- python - 如何从python字典中提取具有最大值的项目的键和值
- version-control - 哪些 VCS 3D 建模师使用?
- php - 避免 composer 的 autoload.php 解析所有文件
- scala - 将 Java Future 转换为 Twitter Future 的最佳方法
- arrays - MongoDB获取日期等于最大日期的结果
- ruby-on-rails - 在 Ruby/Rails 中使用单引号而不是冒号
- java - 如何将 CheckedChange 侦听器传递给自定义视图
- android - 在 android 上共享 URL 不显示为链接。反应原生
- python - 熊猫过滤器,其中当前行字符串包含来自不同列中上一行的字符串
- java - 避免在junit4中调用CommandLineRunner