It's really very simple, but I can't seem to figure out how to do this. I get "Uncaught TypeError: this.resizeCanvas is not a function" from the console in Chrome. The code is meant to resize the canvas in which I draw an analog clock.
<edit>
Pasting the entirety of the code, not just the protion giving me headache
</edit>
JAVASCRIPT
function Hands(canvas)
{
this.angle = 0;
this.beginX = canvas.width / 2;
this.beginY = canvas.height / 2;
this.endX = 0;
this.endY = 0;
this.radius = 0;
this.length = 0;
this.modLength = 0;
this.color = '';
this.lineCap = '';
this.lineWidth = 0;
this.rotation = ( Math.PI * 2 ) / 4;
this.draw = function(ctx)
{
this.length = this.radius - this.modLength;
this.endX = this.beginX + Math.cos( this.angle - this.rotation ) * this.length;
this.endY = this.beginY + Math.sin( this.angle - this.rotation ) * this.length;
ctx.beginPath();
ctx.moveTo( this.beginX, this.beginY );
ctx.lineWidth = this.lineWidth;
ctx.strokeStyle = this.color;
ctx.lineCap = this.lineCap;
ctx.lineTo( this.endX, this.endY );
ctx.stroke();
};
}
function AnalogClock()
{
this.canvas = document.getElementById( "clockface" );
this.context = this.canvas.getContext('2d');
this.margin = 30;
this.rotation = (Math.PI * 2) / 4; // Rotate 0 rad to be at top of circle
this.centerX = this.canvas.width / 2;
this.centerY = this.canvas.height / 2;
this.secondHand = new Hands(this.canvas);
this.secondHand.lineWidth = 3;
this.secondHand.modLength = 100;
this.secondHand.beginX = this.centerX;
this.secondHand.beginY = this.centerY;
this.secondHand.color = '#ff0000';
this.secondHand.radius = this.canvas.height / 2.031;
this.minuteHand = new Hands(this.canvas);
this.minuteHand.lineWidth = 10;
this.minuteHand.modLength = 100;
this.minuteHand.beginX = this.centerX;
this.minuteHand.beginY = this.centerY;
this.minuteHand.color = '#101010';
this.minuteHand.radius = this.canvas.height / 2.031;
this.hourHand = new Hands(this.canvas);
this.hourHand.lineWidth = 16;
this.hourHand.modLength = 175;
this.hourHand.beginX = this.centerX;
this.hourHand.beginY = this.centerY;
this.hourHand.color = '#101010';
this.hourHand.radius = this.canvas.height / 2.031;
this.drawSecondHand = function( s )
{
this.secondHand.angle = ( Math.PI * 2 ) * ( s / 60 );
this.secondHand.draw( this.context );
};
this.drawMinuteHand = function( m )
{
this.minuteHand.angle = ( Math.PI * 2 ) * ( m / 60 );
this.minuteHand.draw( this.context );
};
this.drawHourHand = function( h )
{
this.hourHand.angle = ( Math.PI * 2 ) * ( h / 12 );
this.hourHand.draw( this.context );
};
this.drawDot = function( x, y, radius, radians, color )
{
this.context.beginPath();
this.context.arc( x, y, radius, 0, Math.PI * 2, false );
this.context.fillStyle = color;
this.context.fill();
};
this.drawClockFace = function()
{
var distance = this.centerY - 80;
for( i = 0; i < 60; i++ )
{
var targetX = this.centerX + Math.cos( ( ( Math.PI * 2 ) * ( i / 60 ) ) - this.rotation ) * distance;
var targetY = this.centerY + Math.sin( ( ( Math.PI * 2 ) * ( i / 60 ) ) - this.rotation ) * distance;
this.drawDot( targetX, targetY, 3, this.minuteHand.color);
}
for( i = 0; i < 12; i++ )
{
var targetX = this.centerX + Math.cos( ( ( Math.PI * 2 ) * ( i / 12 ) ) - this.rotation ) * distance;
var targetY = this.centerY + Math.sin( ( ( Math.PI * 2 ) * ( i / 12 ) ) - this.rotation ) * distance;
this.drawDot( targetX, targetY, 8, this.hourHand.color );
}
};
this.resizeCanvas = function()
{
this.canvas.width = window.innerWidth - this.margin;
this.canvas.height = window.innerHeight - this.margin;
};
this.draw = function()
{
var now = new Date();
this.resizeCanvas();
this.drawClockFace();
this.drawHourHand( now.getHours() );
this.drawMinuteHand( now.getMinutes() );
this.drawSecondHand( now.getSeconds() );
this.drawDot( this.centerX, this.centerY, 10, this.secondHand.color ); // Center dot
};
}
/********
LOAD APP
*********/
// ctx.translate(0.5, 0.5); // Anti-aliasing
var analogClock = new AnalogClock();
function tick()
{
setInterval( analogClock.draw, 1000 );
analogClock.draw();
}
document.addEventListener("DOMContentLoaded", function(){ tick(); });
HTML
<!DOCTYPE html>
<html>
<head>
<style>
body {
background: #fefefe;
}
canvas {
background: #fefefe;
}
</style>
</head>
<body id="root">
<canvas id="clockface"></canvas>
<script src="clock.js"></script>
</body>
</html>
You need to code your tick more like this...
function tick() {
analogClock.draw();
//setInterval(tick, 1000); - WRONG
setTimeout(tick, 1000); // Or preferably use requestAnimationFrame.
}
window.addEventListener("load", function() {
tick();
});
However, when I do this, it runs very poorly in Firefox. Perhaps requestAnimationFrame will work better, but there's something that's locking it up that I can't quite put my finger on. However, this does resolve your current issue of resizeCanvas coming back as undefined. When you remove the tick and just call resizeCanvas or draw directly, they work fine. And once the tick is looping correctly, it also works fine.
I suspect the performance issue is being caused by the resize of the canvas being called on EVERY tick. You probably don't want to be doing that. You only need to call that, if the window is resized.
And the resultant clock is upside down BTW. In your Hands object, this corrects it...
this.rotation = -Math.PI / 2;
The root of your problem is that functions called by setTimeout and setInterval always have a this object of the current window. When using either of these functions on an object's method, you must either use a wrapper function or use apply()
//wrapper
setTimeout(function(){analogClock.draw();}, 1000);
or
//apply
setTimeout(function(){analogClock.draw.apply(analogClock), 1000);
Instead you should write:
function tick()
{
analogClock.draw();
}
document.addEventListener("DOMContentLoaded", function(){ setInterval(tick, 1000); })
or
function tick()
{
analogClock.draw();
setInterval( analogClock.draw.apply(analogClock), 1000 );
}
document.addEventListener("DOMContentLoaded", function(){ tick(); });
Change clock.resize() to myClock.resize().
Related
https://codepen.io/deathshadow8123/pen/KKNaLXq That is my pen from codepen, however when I create an html doc, and put the js into a tag and the css into a tag it doesn't show up on the webpage I also put the in there. Pls help me I do not know how to make this work. Any ideas?
if you can't access codepen here is my code:
var w = c.width = window.innerWidth,
h = c.height = window.innerHeight,
ctx = c.getContext( '2d' ),
opts = {
chars: '\1234567890ìqwertyuiopè+asdfghjklòàù<zxcvbnm,.-|!"£$%&/()=?^QWERTYUIOPé*ASDFGHJKLç°§>ZXCVBNM;:_[]##€{}'.split(''), // every key in the italian keyboard layout. It sucks, we don't even have a backtick!
font: '12px monospace',
charSize: 14,
lineHeight: 14,
hueSpeed: 1,
repaintAlpha: .1,
stripesParXxY: .1,
stripeSpeed: .5,
beforeSpawning: 50
},
tick = 0,
endX = ( w / opts.charSize + 1 ) |0,
endY = ( h / opts.lineHeight + 1 ) |0,
sum = w + h,
stripes = [];
ctx.font = opts.font;
ctx.fillStyle = '#111';
ctx.fillRect( 0, 0, w, h );
function loop() {
window.requestAnimationFrame( loop );
tick += opts.hueSpeed;
ctx.fillStyle = 'rgba(0,0,0,alp)'.replace( 'alp', opts.repaintAlpha );
ctx.fillRect( 0, 0, w, h );
stripes.map( function( stripe ){ stripe.step(); } );
}
function Stripe(){
this.reset();
}
Stripe.prototype.reset = function() {
this.x = ( Math.random() * endX ) |0;
this.y = -Math.random() * opts.beforeSpawning;
}
Stripe.prototype.step = function() {
this.y += opts.stripeSpeed;
drawLetter( this.x, this.y|0 );
if( this.y > endX )
this.reset();
}
function drawLetter( x, y ){
x *= opts.charSize;
y *= opts.lineHeight;
ctx.fillStyle = 'hsl(hue,80%,50%)'.replace( 'hue', ( x + y ) / sum * 360 + tick );
ctx.fillText( opts.chars[ ( Math.random() * opts.chars.length ) |0 ], x, y );
}
for( var i = 0; i < endX*endX * opts.stripesParXxY; ++i )
stripes.push( new Stripe );
loop();
window.addEventListener( 'resize', function(){
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = '#111';
ctx.fillRect( 0, 0, w, h );
ctx.font = opts.font;
endX = ( w / opts.charSize + 1 ) |0;
endY = ( h / opts.lineHeight + 1 ) |0;
sum = w + h;
stripes.length = 0;
for( var i = 0; i < endX*endY * opts.stripesParXxY; ++i )
stripes.push( new Stripe );
})
canvas {
position: absolute;
top: 0;
left: 0;
}
<canvas id=c></canvas>
Once the DOM is loaded, you need to get a reference to your canvas DOM element at the top of your script or load handler function.
const c = document.getElementById("c");
Also its best to use quotes in your html.
e.g.
<canvas id="c"></canvas>
The problem of your code is that you have not put your canvas' id inside "", putting t
I have seen the code. There is a small error in html file and this is the cause of the project not working. It is simple, Just put your canvas id in "" doublequotes.
<canvas id="c"></canvas>
Thanks, Hope it helps
So I am trying to get the clearInterval to work. This is from: https://codepen.io/whqet/pen/Auzch (I modified it a bit to make it more towards what I needed. Essentially, I want the animation to cease after 10000 ms. Excuse the messy coding, I threw in a timer at the bottom so I could see whether or not it would work.. Any assistance would be appreciated. Thanks!
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 callback's argument
When requestAnimationFrame calls the callback function it supplies a high precision time in ms. You can use this time to control the timing of your animations.
For example the following animation loop will stop 10 seconds after the first frame is called.
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);
}
This gives you better timing control in animations than using setTimeout especially if you have more than one thing to control the timing of (see demo)
Demo
The demo uses the same method to count down the time and control the fireworks. A few seconds before the end the fireworks are held back and then a second before a group are fired to explode in time with zero (Well there about!!!)
// 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>
I'm starting to work with some canvas animations and am having trouble understanding what's happening in a CodePen. Here is the animation in question: https://codepen.io/towc/pen/mJzOWJ
var w = c.width = window.innerWidth,
h = c.height = window.innerHeight,
ctx = c.getContext( '2d' ),
opts = {
len: 20,
count: 50,
baseTime: 10,
addedTime: 10,
dieChance: .05,
spawnChance: 1,
sparkChance: .1,
sparkDist: 10,
sparkSize: 2,
color: 'hsl(hue,100%,light%)',
baseLight: 50,
addedLight: 10, // [50-10,50+10]
shadowToTimePropMult: 6,
baseLightInputMultiplier: .01,
addedLightInputMultiplier: .02,
cx: w / 2,
cy: h / 2,
repaintAlpha: .04,
hueChange: .1
},
tick = 0,
lines = [],
dieX = w / 2 / opts.len,
dieY = h / 2 / opts.len,
baseRad = Math.PI * 2 / 6;
ctx.fillStyle = 'black';
ctx.fillRect( 0, 0, w, h );
function loop() {
window.requestAnimationFrame( loop );
++tick;
ctx.globalCompositeOperation = 'source-over';
ctx.shadowBlur = 0;
ctx.fillStyle = 'rgba(0,0,0,alp)'.replace( 'alp', opts.repaintAlpha );
ctx.fillRect( 0, 0, w, h );
ctx.globalCompositeOperation = 'lighter';
if( lines.length < opts.count && Math.random() < opts.spawnChance )
lines.push( new Line );
lines.map( function( line ){ line.step(); } );
}
function Line(){
this.reset();
}
Line.prototype.reset = function(){
this.x = 0;
this.y = 0;
this.addedX = 0;
this.addedY = 0;
this.rad = 0;
this.lightInputMultiplier = opts.baseLightInputMultiplier + opts.addedLightInputMultiplier * Math.random();
this.color = opts.color.replace( 'hue', tick * opts.hueChange );
this.cumulativeTime = 0;
this.beginPhase();
}
Line.prototype.beginPhase = function(){
this.x += this.addedX;
this.y += this.addedY;
this.time = 0;
this.targetTime = ( opts.baseTime + opts.addedTime * Math.random() ) |0;
this.rad += baseRad * ( Math.random() < .5 ? 1 : -1 );
this.addedX = Math.cos( this.rad );
this.addedY = Math.sin( this.rad );
if( Math.random() < opts.dieChance || this.x > dieX || this.x < -dieX || this.y > dieY || this.y < -dieY )
this.reset();
}
Line.prototype.step = function(){
++this.time;
++this.cumulativeTime;
if( this.time >= this.targetTime )
this.beginPhase();
var prop = this.time / this.targetTime,
wave = Math.sin( prop * Math.PI / 2 ),
x = this.addedX * wave,
y = this.addedY * wave;
ctx.shadowBlur = prop * opts.shadowToTimePropMult;
ctx.fillStyle = ctx.shadowColor = this.color.replace( 'light', opts.baseLight + opts.addedLight * Math.sin( this.cumulativeTime * this.lightInputMultiplier ) );
ctx.fillRect( opts.cx + ( this.x + x ) * opts.len, opts.cy + ( this.y + y ) * opts.len, 2, 2 );
if( Math.random() < opts.sparkChance )
ctx.fillRect( opts.cx + ( this.x + x ) * opts.len + Math.random() * opts.sparkDist * ( Math.random() < .5 ? 1 : -1 ) - opts.sparkSize / 2, opts.cy + ( this.y + y ) * opts.len + Math.random() * opts.sparkDist * ( Math.random() < .5 ? 1 : -1 ) - opts.sparkSize / 2, opts.sparkSize, opts.sparkSize )
}
loop();
window.addEventListener( 'resize', function(){
w = c.width = window.innerWidth;
h = c.height = window.innerHeight;
ctx.fillStyle = 'black';
ctx.fillRect( 0, 0, w, h );
opts.cx = w / 2;
opts.cy = h / 2;
dieX = w / 2 / opts.len;
dieY = h / 2 / opts.len;
});
canvas {
position: absolute;
top: 0;
left: 0;
}
<canvas id=c></canvas>
If you let the animation run for a while so the "sparks" spread out they leave behind a faint gray-ish trail. What is causing this trail and is it possible to prevent it?
Thank you!
I have a top to bottom falling canvas animation, but I want to reverse it (go from bottom to top). I can't figure out how to change the direction
I have my code here
http://js.do/code/176399
sample:
function Ember(x, y, s /*radius*/ , p /*num points*/ ) {
this.x = x;
this.y = y;
this.s = s;
this.p = p;
this.img = document.createElement('canvas');
var context = this.img.getContext('2d'),
len = this.s / 1.45;
this.img.height = this.img.width = this.s * 2;
context.translate(this.s, this.s);
context.rotate((Math.PI * 1 / 10));
context.beginPath();
for (var i = 0; i < p; i++) {
context.lineTo(0, -s);
context.rotate((Math.PI * 2 / (p * 2)));
context.lineTo(0, s);
context.rotate((Math.PI * 2 / (p * 2)));
}
context.closePath();
context.shadowBlur = this.s / 3;
context.shadowColor = 'rgba(174, 12, 1, 0.9)';
context.fillStyle = 'rgba(133,80,33,.45)';
context.fill();
}
Just need to reverse the "fall" to bottom to top.
Update:
following off of Jared's suggestion
Changed:
embers[i].y += embers[i].vy;
to:
embers[i].y -= embers[i].vy;
However, I only get one animation cycle would like to have it continually animate new embers on the screen
Take a look at this line where you update the ember's Y position:
embers[i].y = -embers[i].s;
Try changing this to something like this:
embers[i].y = embers[i].s;
<canvas id="canvas"></canvas>
<script>
function Ember(x, y, s /*radius*/ , p /*num points*/ ) {
this.x = x;
this.y = y;
this.s = s;
this.p = p;
this.img = document.createElement('canvas');
var context = this.img.getContext('2d'),
len = this.s / 1.45;
this.img.height = this.img.width = this.s * 2;
context.translate(this.s, this.s);
context.rotate((Math.PI * 1 / 10));
context.beginPath();
for (var i = 0; i < p; i++) {
context.lineTo(0, -s);
context.rotate((Math.PI * 2 / (p * 2)));
context.lineTo(0, s);
context.rotate((Math.PI * 2 / (p * 2)));
}
context.closePath();
context.shadowBlur = this.s / 3;
context.shadowColor = 'rgba(174, 12, 1, 0.9)';
context.fillStyle = 'rgba(133,80,33,.45)';
context.fill();
}
window.requestAnimationFrame = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var canvas, context, height, width, embers;
setTimeout(init, 10);
function init() {
canvas = document.getElementById('canvas');
context = canvas.getContext('2d');
height = canvas.height = document.body.offsetHeight;
width = canvas.width = document.body.offsetWidth;
embers = [];
for (var i = 0; i < 150; i++) {
var x = Math.random() * width,
y = Math.random() * height,
s = Math.random() * 5 + 5,
p = Math.random() * 8 + 8;
var s = new Ember(x, y, s, p);
s.vx = Math.random() * 2 - 1;
s.vy = Math.random() * 2 + 1;
s.r = Math.random() * 360;
embers.push(s);
}
update();
render();
}
function update() {
for (var i = 0, l = embers.length; i < l; i++) {
embers[i].r += embers[i].vx / 10;
embers[i].x += embers[i].vx;
embers[i].y -= embers[i].vy;
if (embers[i].y < 0) {
embers[i].y = Math.random()*height;
embers[i].vx = Math.random() * 2 - 1;
embers[i].vy = Math.random() * 2 + 1;
}
}
setTimeout(update, 1000 / 30);
}
function render() {
context.clearRect(0, 0, width, height);
for (var i = 0, l = embers.length; i < l; i++) {
context.save();
context.translate(embers[i].x, embers[i].y);
context.rotate(embers[i].r);
context.drawImage(embers[i].img, 0, 0);
context.restore();
}
requestAnimationFrame(render);
}
</script>
Wrong if condition in update function, change it to check if v(embers[i].y < 0), ans set appropriate new y position
if (embers[i].y < 0) {
embers[i].y = Math.random()*height;
How would I write script that would move a shape in svg linearly with javascript. I want to use requestanimframe.
Here is an example using canvas. I just want to do the same thing except with svg.
The script obtains a context to the canvas then it uses javascript to draw a shape with the properties of the context. Then it manages the animation of the shape on the canvas in a linear motion. I just want to use svg for the shape instead of the canvas context properties.
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
#myCanvas {
border: 1px solid #9C9898;
}
</style>
<script>
window.requestAnimFrame = (function(callback){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback){
window.setTimeout(callback, 1000 / 60);
};
})();
function animate(lastTime, myRectangle){
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
// update
var date = new Date();
var time = date.getTime();
var timeDiff = time - lastTime;
var linearSpeed = 100; // pixels / second
var linearDistEachFrame = linearSpeed * timeDiff / 1000;
var currentX = myRectangle.x;
if (currentX < canvas.width - myRectangle.width - myRectangle.borderWidth / 2) {
var newX = currentX + linearDistEachFrame;
myRectangle.x = newX;
}
lastTime = time;
// clear
context.clearRect(0, 0, canvas.width, canvas.height);
// draw
context.beginPath();
context.rect(myRectangle.x, myRectangle.y, myRectangle.width, myRectangle.height);
context.fillStyle = "#8ED6FF";
context.fill();
context.lineWidth = myRectangle.borderWidth;
context.strokeStyle = "black";
context.stroke();
// request new frame
requestAnimFrame(function(){
animate(lastTime, myRectangle);
});
}
window.onload = function(){
var myRectangle = {
x: 0,
y: 50,
width: 100,
height: 50,
borderWidth: 5
};
var date = new Date();
var time = date.getTime();
animate(time, myRectangle);
};
</script>
</head>
<body onmousedown="return false;">
<canvas id="myCanvas" width="578" height="200">
</canvas>
</body>
Probably the easiest way to move an element in SVG with JavaScript is to modify the transform attribute of the element. This isn't the best method in terms of performance, but it is very readable and simple.
Most simply:
var el = document.getElementById( "mySVGElement" );
for( var i = 0; i < 100; i++ )
{
setTimeout( function( ) {
el.setAttribute( "transform", "translate( " + i + ", 0 )" );
}, 200 + i );
}
Or if you want a function to do it:
function translateElement( element, distance )
{
var x, y;
for( var i = 0; i < 100; i++ )
{
setTimeout( function( ) {
x = parseInt( distance.x * i / 100 );
y = parseInt( distance.y * i / 100 );
element.setAttribute( "transform", "translate( " + x + ", " + y + " )" );
}, 20 + i );
}
}
or per Erik's advice:
function translateElement( element, distance )
{
var x, y;
for( var i = 0; i < 100; i++ )
{
setTimeout( function( ) {
x = distance.x * i / 100;
y = distance.y * i / 100;
element.transform.baseVal.getItem( 0 ).setTranslate( x, y );
}, 20 + i );
}
}
Where element is the element you're moving and distance is an object of the form:
{
x: xOffset,
y: yOffset
}