How do I make an object change it's direction smoothly? - javascript

I have a simple Javascript program that displays a small rectangle in a canvas. The rectangle moves towards the mouse position. When it changes direction, it does so with sharp corners. As in, if the rectangle left a line behind, when I move my mouse in a circle, the rectangle would draw a tilted square.
What I'd want to happen, is that it would draw a circle. No sharp corners.
Here's the code I am using for changing the direction:
function changeDir()
{
if(mouseXCoord-5<x && x<mouseXCoord+5)
{
xDirection = 0;//stop moving if close to mouse
}
else if(x>mouseXCoord)
{
xDirection = -1;
}
else if(x<mouseXCoord)
{
xDirection = 1;
}
if(mouseYCoord-5<y && y<mouseYCoord+5)
{
yDirection = 0;//stop moving if close to mouse
}
else if(y>mouseYCoord)
{
yDirection = -1;
}
else if(y<mouseYCoord)
{
yDirection = 1;
}
}
The draw function:
function draw()
{
context2D.clearRect(0, 0, canvas.width, canvas.height);
fillwith = context2D.fillStyle='red';
context2D.fillRect(x,y,10,10);
changeDir();
x = x + (thrust * xDirection);
y = y + (thrust * yDirection);
console.log(x,y,xDirection, yDirection,mouseXCoord,mouseYCoord);
}
So, how do I do that?
Update:
I changed the changeDir() function so that it makes the corners of the tilted square rounded.
function changeDir()
{
if(mouseXCoord-5<x && x<mouseXCoord+5)
{
xstop = true;//stop moving if close to mouse
}
else if(x>mouseXCoord)
{
if(Math.abs(xthrust)==mainThrust)
{
xthrust = -1*mainThrust;
}
else
{
xthrust--;
}
xstop = false;//make sure it moves
}
else if(x<mouseXCoord)
{
if(xthrust==mainThrust)
{
xthrust = mainThrust;
}
else
{
xthrust++;
}
xstop = false;//make sure it moves
}
if(mouseYCoord-5<y && y<mouseYCoord+5)
{
ystop = true;//stop moving if close to mouse
}
else if(y>mouseYCoord)
{
if(Math.abs(ythrust)==mainThrust)
{
ythrust = -1*mainThrust;
}
else
{
ythrust--;
}
ystop = false;//make sure it moves
}
else if(y<mouseYCoord)
{
if(ythrust==mainThrust)
{
ythrust = mainThrust;
}
else
{
ythrust++;
}
ystop = false;//make sure it moves
}
}
Here's the variables I declare:
const FPS = 5;
var x = 300;
var y = 200;
var xDirection = 1;
var yDirection = 1;
var image = new Image();
var canvas = null;
var context2D = null;
var mouseXCoord = 0;
var mouseYCoord = 0;
var mainThrust = 5;
var xthrust = mainThrust;
var ythrust = mainThrust;
var xstop = false;
var ystop = false;
Where it actualy moves:
changeDir();
if(!xstop)
x = x + (xthrust);
if(!ystop)
y = y + (ythrust);
Ok, here's my new code thanks to cape1232. I actually started over entirely. I get smooth turning, but the speed the block moves at changes. Demo at: http://develzone.davidreagan.net/jsMoveTesting/index.html
var gameVars = {
fps: 30
}
var object = {
name: 'default',
xpos: 200,
ypos: 200,
xVect: 1,
yVect: 1,
thrust: 15
}
ctx = null;
canvas = null;
xMousePos = 0;
yMousePos = 0;
runGame = null;
function init()
{
canvas = document.getElementById('canvas');
ctx = canvas.getContext('2d');
$('#canvas').mousemove(getMousePos);
$('#canvas').click(stop);
//setTimeout('clearInterval(runGame);',30000);
}
function start()
{
runGame = setInterval('run();',1000/gameVars.fps);
}
function run()
{
ctx.clearRect(0, 0, canvas.width, canvas.height);
moveBlock();
//ctx.translate(object.xpos,object.ypos);
drawBlock();
showMousePos = 'X: ' + xMousePos + ' Y: ' + yMousePos;
ctx.fillText(showMousePos, 215,50);
}
function stop()
{
//alert('hit stop');
console.log('clicked');
//if(e.keyCode == 113)
if(runGame)
{
clearInterval(runGame);
runGame = false;
//console.log('stop true');
}
else
start();
}
function drawBlock()
{
ctx.fillRect(object.xpos,object.ypos,10,10);
}
function moveBlock()
{
xDiff = xMousePos - object.xpos;
yDiff = yMousePos - object.ypos;
minDiff = Math.max(Math.min(xDiff, yDiff), 1);
deltaX = xDiff / minDiff;
deltaY = yDiff / minDiff;
// Scale the deltas to limit the largest to mainThrust
maxDelta = Math.max(Math.max(deltaX, deltaY), 1)
if (maxDelta>object.thrust)
{
deltaX = deltaX * object.thrust / maxDelta;
deltaY = deltaY * object.thrust / maxDelta;
}
if(object.xpos >= canvas.width)
{
object.xpos = 0;
}
else
{
object.xpos += deltaX;
//console.log('moveBlock xpos else: '+object.xpos);
}
if(object.ypos >= canvas.height)
{
object.ypos = 0;
}
else
{
object.ypos += deltaY;
//console.log('moveBlock ypos else: '+object.ypos);
}
console.log('xpos: '+object.xpos);
console.log('ypos: '+object.ypos);
console.log('xMousePos: '+xMousePos);
console.log('yMousePos: '+yMousePos);
console.log('xDiff: '+xDiff);
console.log('yDiff: '+yDiff);
console.log('minDiff: '+minDiff);
console.log('deltaX: '+xDiff+'/'+minDiff+ ' = '+ deltaX);
console.log('deltaY: '+yDiff+'/'+minDiff+ ' = '+ deltaY);
console.log('maxDelta: '+maxDelta);
}
function getMousePos(e)
{
xMousePos = e.pageX;
yMousePos = e.pageY;
//console.log('Mouse Moved');
}
window.onload = init;

You don't want your xDirection and yDirection to be only 1 or -1. They need to be proportional to the difference between the mouse and the rectangle position.
Edited to take comments into account.
function changeDir()
{
xDiff = mouseXCoord - x;
yDiff = mouseYCoord - y;
// Scale the smallest diff to be 1 (or less)
minDiff = max(min(xDiff, yDiff), 1);
deltaX = xDiff / minDiff;
deltaY = yDiff / minDiff;
// Scale the deltas to limit the largest to mainThrust
maxDelta = max(max(deltaX, deltaY), 1)
if (maxDelta>mainThrust)
{
deltaX = deltaX * mainThrust / maxDelta;
deltaY = deltaY * mainThrust / maxDelta;
}
if(mouseXCoord-5<x && x<mouseXCoord+5)
{
xDirection = 0;//stop moving if close to mouse
}
else
{
xDirection = deltaX;
}
if(mouseYCoord-5<y && y<mouseYCoord+5)
{
yDirection = 0;//stop moving if close to mouse
}
else
{
yDirection = deltaY;
}
}

Instead of having xDirection and yDirection (the sine and cosine of your direction, actually) sharply defined as 0, 1, or -1, you need to define more precisely the direction you should eventually be heading in, and recall what direction you were last moving in and how many "angular steps" you've done in changing the direction from what was to what it should be.
How many such angular steps you want to take for a change of direction, and whether each step should be of the same size or depend on how fast you're moving and/or how brusquely you're swerving, etc, is something you should adapt by trial and error, since it appears that what you're mostly after is to have things "look" right, so it's hard to give a precise prescription (for what looks right to you;-).

Related

Rotate the user character on mouse position

I'm trying to get the character to rotate the character object based on where the mouse is.
So far I got it to rotate incrementally without mouse position. I was checking if it effected my zombie's chasing capabilities.
My script
let player, zombie, mouseX, mouseY;;
let bgCanvas = document.getElementById('backgroundCan');
function startGame() {
document.getElementById("startScreen").style.display = "none";
player = new playerComponent(350, 220);
zombie = new zombieComponent(750, 220);
gameArea.start();
}
let gameArea = {
canvas : document.createElement("canvas"),
start : function() {
this.canvas.width = 800;
this.canvas.height = 500;
this.canvas.style = "position: absolute";
this.context = this.canvas.getContext("2d");
document.body.insertBefore(this.canvas, document.body.childNodes[2]);
this.interval = setInterval(updateArea, 20);
window.addEventListener('keydown', function (e) {
gameArea.keys = (gameArea.keys || []);
gameArea.keys[e.keyCode] = true;
})
window.addEventListener('keyup', function (e) {
gameArea.keys[e.keyCode] = false;
});
this.canvas.addEventListener("mousemove", function(e){
mouseX = e.clientX - ctx.canvas.offsetLeft;
mouseY = e.clientY - ctx.canvas.offsetTop;
});
this.canvas.addEventListener("mousedown", function(e){
let gShot = new Audio('assets/shot.mp3');
gShot.play();
var mX = e.clientX - ctx.canvas.offsetLeft;
var mY = e.clientY - ctx.canvas.offsetTop;
if(mX >= zombie.x && mX < zombie.x+zombie.w && mY >= zombie.y && mY < zombie.y+zombie.h){
if(zombie.health > 0){
zombie.health += -1;
zombie.speedX += 10;
zombie.newPos();
zombie.update();
}
else {
zombie.status = "dead";
}
}
});
},
clear : function() {
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
}
function playerComponent(x, y){
this.x = x;
this.y = y;
this.speedX = 0;
this.speedY = 0;
this.health = 10;
this.status = "alive";
let rotNum = 1;
this.update = function(){
ctx = gameArea.context;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(rotNum * Math.PI / 180);
playerSprite = new Image();
playerSprite.src = "assets/playerGun.png";
ctx.drawImage(playerSprite, 0, 0);
ctx.restore();
rotNum++;
}
this.newPos = function() {
this.x += this.speedX;
this.y += this.speedY;
}
}
function updateArea() {
gameArea.clear();
if(player.status == "alive"){
player.speedX = 0;
player.speedY = 0;
if (gameArea.keys && gameArea.keys[65]) {
if(player.x > 20){
player.speedX = -3;
}
}
if (gameArea.keys && gameArea.keys[68]) {
if(player.x < 740){
player.speedX = 3;
}
}
if (gameArea.keys && gameArea.keys[87]) {
if(player.y > 20){
player.speedY = -3;
}
}
if (gameArea.keys && gameArea.keys[83]) {
if(player.y < 445){
player.speedY = 3;
}
}
player.newPos();
player.update();
}
if(zombie.status == "alive"){
if(zombie.x > player.x){
zombie.speedX = -1;
}
else{
zombie.speedX = 1;
}
if(zombie.y > player.y){
zombie.speedY = -1;
}
else{
zombie.speedY = 1;
}
zombie.newPos();
zombie.update();
}
else{
zombie.update();
}
}
So far, I have the mouse position on the canvas and am able to rotate the character, but I just don't how to connect the two. How should use the mouse position and the character position to rotate towards the mouse? The character is initially facing right (i think?), at least the sprite initially is.
Here's an image illustrating the situation:
You have the mouseY, playerY and mouseX, playerX
Therefore you can calculate the height and base of the triangle,
Thus the angle with
However, since in the second and third quadrants y/x will return an angle in the first and fourth quadrants, you need to use the Math.atan2(y,x) function in Javascript, not Math.atan(y/x). This will give you an angle between -180 and 180 instead of between -90 and 90.
Atan Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
Then all you have to do is rotate based on the angle!
(P.S. Remember that you will have to convert between radians and degrees)

HTML5 Canvas | Bouncing Balls | Looping through an image array and placing different background images on each ball, centered

I am having a bit of a nightmare applying different background images onto balls that bounce around in the canvas under the effect of gravity and the bounds of my canvas, eventually settling down and stacking at the bottom.
I have created an image array, that I am trying to loop through and create a ball for each image, where the image becomes a centred background.
I can centre the image, but this makes the balls appear to leave the bounds of my canvas. So have reverted that change.
I am unable to get the background image to be different from the previous ball. Where the image shown on all balls is the last image in the array. However the number of balls created does reflect the number of images in the array.
Here is a link to a codepen:
https://codepen.io/jason-is-my-name/pen/BbNRXB
html, body{
width:100%;
height:100%;
margin: 0;
padding: 0;
background: #333333;
}
*{
margin: 0;
padding: 0;
}
.container {
width: 410px;
height: 540px;
}
#ball-stage{
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<canvas id="ball-stage" ></canvas>
</div>
<script>
/*
/*
* Created by frontside.com.au
* Amended by Jason
*/
$(document).ready(function () {
$.ajaxSetup({
cache: true
});
var url1 = "https://code.createjs.com/easeljs-0.6.0.min.js";
$.getScript(url1, function () {
new App();
})
});
function App() {
var self = this;
self.running = false;
self.initialized = false;
var stageClicked = false;
var stage, canvas;
var canvasWidth = 410;
var canvasHeight = 540;
var bounce = -0.75;
var balls = [];
var _gravityY = 1;
var _gravityX = 0;
var FPS = 30;
var infoText, detailsText;
var ballsInitalized = false;
var iOS = navigator.userAgent.match(/(iPod|iPhone|iPad)/);
self.initialize = function () {
toggleListeners(true);
self.initCanvas();
self.initGame();
};
var toggleListeners = function (enable) {
if (!enable) return;
};
self.refresh = function () {}
self.initCanvas = function () {
canvas = $("#ball-stage").get(0);
stage = new createjs.Stage(canvas);
window.addEventListener('resize', onStageResize, false);
onStageResize();
createjs.Touch.enable(stage);
createjs.Ticker.addListener(tick);
createjs.Ticker.setFPS(FPS);
self.initialized = true;
}
self.initGame = function () {
initBalls(canvasWidth, canvasHeight);
}
var onStageResize = function () {
stage.canvas.width = canvasWidth;
stage.canvas.height = canvasHeight;
}
var initBalls = function (stageX, stageY) {
var imagesArray = ["img-1.png","img-2.png","img-3.png","img-4.png","img-5.png","img-6.png","img-7.png","img-8.png"];
for (var i = 0; i < imagesArray.length; i++) {
console.log(i);
var imageArray = imagesArray[i];
console.log(imageArray);
setTimeout(function () {
var arrayImage = new Image();
console.log(arrayImage);
arrayImage.onload = function(){
addBall(arrayImage, stageX / 2, 0);
}
arrayImage.src = imageArray;
}, i * 1000);
}
}
var addBall = function (img, x, y) {
console.log(img);
var shape = new createjs.Shape();
shape.id = balls.length;
shape.radius = 51.25;
shape.mass = shape.radius;
shape.x = x;
shape.y = y;
shape.vx = rand(-3, 3);
shape.vy = rand(-3, 3);
var image = new Image();
image.src = img;
shape.graphics.beginBitmapFill(img,'repeat').drawCircle(0, 0, shape.radius);
stage.addChild(shape);
balls.push(shape);
}
var numBalls = function () {
return balls.length;
}
var tick = function () {
balls.forEach(move);
for (var ballA, i = 0, len = numBalls() - 1; i < len; i++) {
ballA = balls[i];
for (var ballB, j = i + 1; j < numBalls(); j++) {
ballB = balls[j];
checkCollision(ballA, ballB);
}
}
stage.update();
}
var rotate = function (x, y, sin, cos, reverse) {
return {
x: (reverse) ? (x * cos + y * sin) : (x * cos - y * sin),
y: (reverse) ? (y * cos - x * sin) : (y * cos + x * sin)
};
}
var checkCollision = function (ball0, ball1) {
var dx = ball1.x - ball0.x,
dy = ball1.y - ball0.y,
dist = Math.sqrt(dx * dx + dy * dy);
//collision handling code here
if (dist < ball0.radius + ball1.radius) {
//calculate angle, sine, and cosine
var angle = Math.atan2(dy, dx),
sin = Math.sin(angle),
cos = Math.cos(angle),
//rotate ball0's position
pos0 = {
x: 0,
y: 0
}, //point
//rotate ball1's position
pos1 = rotate(dx, dy, sin, cos, true),
//rotate ball0's velocity
vel0 = rotate(ball0.vx, ball0.vy, sin, cos, true),
//rotate ball1's velocity
vel1 = rotate(ball1.vx, ball1.vy, sin, cos, true),
//collision reaction
vxTotal = vel0.x - vel1.x;
vel0.x = ((ball0.mass - ball1.mass) * vel0.x + 2 * ball1.mass * vel1.x) /
(ball0.mass + ball1.mass);
vel1.x = vxTotal + vel0.x;
//update position - to avoid objects becoming stuck together
var absV = Math.abs(vel0.x) + Math.abs(vel1.x),
overlap = (ball0.radius + ball1.radius) - Math.abs(pos0.x - pos1.x);
pos0.x += vel0.x / absV * overlap;
pos1.x += vel1.x / absV * overlap;
//rotate positions back
var pos0F = rotate(pos0.x, pos0.y, sin, cos, false),
pos1F = rotate(pos1.x, pos1.y, sin, cos, false);
//adjust positions to actual screen positions
// ball1.x = ball0.x + pos1F.x;
setBallX(ball1, ball0.x + pos1F.x)
//ball1.y = ball0.y + pos1F.y;
setBallY(ball1, ball0.y + pos1F.y)
// ball0.x = ball0.x + pos0F.x;
setBallX(ball0, ball0.x + pos0F.x)
// ball0.y = ball0.y + pos0F.y;
setBallY(ball0, ball0.y + pos0F.y)
//rotate velocities back
var vel0F = rotate(vel0.x, vel0.y, sin, cos, false),
vel1F = rotate(vel1.x, vel1.y, sin, cos, false);
ball0.vx = vel0F.x;
ball0.vy = vel0F.y;
ball1.vx = vel1F.x;
ball1.vy = vel1F.y;
}
}
var checkWalls = function (ball) {
if (ball.x + ball.radius > canvas.width) {
// ball.x = canvas.width - ball.radius;
setBallX(ball, canvas.width - ball.radius)
ball.vx *= bounce;
} else
if (ball.x - ball.radius < 0) {
// ball.x = ball.radius;
setBallX(ball, ball.radius)
ball.vx *= bounce;
}
if (ball.y + ball.radius > canvas.height) {
// ball.y = canvas.height - ball.radius;
setBallY(ball, canvas.height - ball.radius)
ball.vy *= bounce;
} else
if (ball.y - ball.radius < 0) {
//ball.y = ball.radius;
setBallY(ball, ball.radius)
ball.vy *= bounce;
}
}
var move = function (ball) {
ball.vy += _gravityY;
ball.vx += _gravityX;
setBallX(ball, ball.x + ball.vx)
setBallY(ball, ball.y + ball.vy)
checkWalls(ball);
}
var setBallX = function (ball, x) {
if (isNaN(ball.pointerID)) {
ball.x = x
}
}
var setBallY = function (ball, y) {
if (isNaN(ball.pointerID)) {
ball.y = y
}
}
var rand = function (min, max) {
return Math.random() * (max - min) + min;
return (Math.random() * max) + min;
}
self.initialize();
return self;
}
window.log = function f() {
log.history = log.history || [];
log.history.push(arguments);
if (this.console) {
var args = arguments,
newarr;
args.callee = args.callee.caller;
newarr = [].slice.call(args);
if (typeof console.log === 'object') log.apply.call(console.log, console, newarr);
else console.log.apply(console, newarr);
}
};
(function (a) {
function b() {}
for (var c = "assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","), d; !!(d = c.pop());) {
a[d] = a[d] || b;
}
})
(function () {
try {
console.log();
return window.console;
} catch (a) {
return (window.console = {});
}
}());
</script>
I have been trapped at this point in the code for about a week now and could really do with some genius' help!
Aims:
Add balls equivalent to the length of the image array.
Each ball with have its respective image as a centred background.
The balls will not leave the bounds of the canvas.
Relevant code:
initBalls()
addBall()
Thanks, Jason.
https://codepen.io/prtjohanson/pen/vPKQBg
What needed to be changed:
for (let i = 0; i < imagesArray.length; i++) {
console.log(i);
const imageArray = imagesArray[i];
setTimeout(function() {
var arrayImage = new Image();
arrayImage.onload = function() {
addBall(arrayImage, stageX / 2, 0);
};
arrayImage.src = imageArray;
}, i * 1000);
}
By the time the setTimeout callback fired, your for loop had already finished and with a var declaration, the for loop iteration has no scope of its own, with a let, each iteration has its own scope like a function does.
If it must run on browsers that don't have let or const keywords, let me know, I can provide a solution for them as well
This will work in IE11 and other browsers that don't support ES6
for (var i = 0; i < imagesArray.length; i++) {
(function(imageArray) {
setTimeout(function() {
var arrayImage = new Image();
arrayImage.onload = function() {
console.log('Add'+i);
addBall(arrayImage, stageX / 2, 0);
};
arrayImage.src = imageArray;
}, i * 1000);
})(imagesArray[i]);
}
To get the images centered, without them going out of the bounds of canvas, use a 2D transform on the beginBitmapFill operation:
var transform = new createjs.Matrix2D();
transform.appendTransform(-shape.radius, -shape.radius, 1, 1, 0);
shape.graphics.beginBitmapFill(img, "repeat", transform).drawCircle(0, 0, shape.radius);
As for there being not as many balls as there are URLs in the array, it seems that sometimes the image source URL prompts "I am not a robot" captcha. If replaced with URL-s under your control, the issue should go away.

how to fill particles inside rectangle?

I'm looking to draw a rectangle basically text but just for clearing insight I'm working it with rectangle with small particles inside rectangle the basic I idea I got from https://yalantis.com/ but in my attempt I'm stuck here with solid filled rectangle with a color I have specified for particles. Please help me.. :)
Thanks here is my code:
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Off Screen Canvas</title>
<script>
function createOffscreenCanvas() {
var offScreenCanvas = document.createElement('canvas');
offScreenCanvas.width = '1360';
offScreenCanvas.height = '400';
var context = offScreenCanvas.getContext("2d");
var W=200;
var H=200;
particleCount = 200;
particles = []; //this is an array which will hold our particles Object/Class
function Particle() {
this.x = Math.random() * W;
this.y = Math.random() * H;
this.direction ={"x": -1 + Math.random()*2, "y": -1 + Math.random()*2};
this.vx = 2 * Math.random() + 4 ;
this.vy = 2 * Math.random() + 4;
this.radius = .9 * Math.random() + 1;
this.move = function(){
this.x += this.vx * this.direction.x;
this.y += this.vy * this.direction.y;
};
this.changeDirection = function(axis){
this.direction[axis] *= -1;
};
this.draw = function() {
context.beginPath();
context.fillStyle = "#0097a7";
context.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
context.fill();
};
this.boundaryCheck = function(){
if(this.x >= W){
this.x = W;
this.changeDirection("x");
}
else if(this.x <= 0){
this.x = 0;
this.changeDirection("x");
}
if(this.y >= H){
this.y = H;
this.changeDirection("y");
}
else if(this.y <= 0){
this.y = 0;
this.changeDirection("y");
}
};
}
function createParticles(){
for (var i = particleCount-1; i >= 0; i--) {
p = new Particle();
particles.push(p);
}
}// end createParticles
function drawParticles(){
for (var i = particleCount-1; i >= 0; i--){
p = particles[i];
p.draw();
}
} //end drawParticles
function updateParticles(){
for(var i = particles.length - 1; i >=0; i--){
p = particles[i];
p.move();
p.boundaryCheck();
}
}//end updateParticle
createParticles();
var part=drawParticles();
context.fillStyle=part;
context.fillRect(W-190, H-190, W, H);
context.fill();
return offScreenCanvas;
}
function copyToOnScreen(offScreenCanvas) {
var onScreenContext=document.getElementById('onScreen').getContext('2d');
var offScreenContext = offScreenCanvas.getContext('2d');
var image=offScreenContext.getImageData(10,10,200,200);
onScreenContext.putImageData(image,offScreenCanvas.width/2,offScreenCanvas.height/4);
}
function main() {
copyToOnScreen(createOffscreenCanvas());
}
</script>
<style>
canvas {
border: 1px solid red;
}
</style>
</head>
<body onload="main()">
<canvas id="onScreen" width="1360" height="400"></canvas>
</body>
</html>
I see you have not found what you are looking for yet. Below is something quick to get you on your way. There is a whole range of stuff being used from canvas,mouse,particles, etc most of which is without comments. There is no load balancing or compliance testing and because it uses babel to be compatible with IE11 I have no clue how it runs on those browsers.
I will add to this answer some other time but for now I am a little over it.
const textList = ["1","2","3","Testing","text","to","particles"];
var textPos = 0;
function createParticles(text){
createTextMap(
text, // text to display
40, // font size
"Arial", // font
{ // style fot rendering font
fillStyle : "#6AF",
strokeStyle : "#F80",
lineWidth : 2,
lineJoin : "round",
},{ // bounding box to find a best fit for
top : 0,
left : 0,
width : canvas.width,
height : canvas.height,
}
)
}
// This function starts the animations
var started = false;
function startIt(){
started = true;
const next = ()=>{
var text = textList[(textPos++ ) % textList.length];
particles.mouseFX.dist = canvas.height / 8;
createParticles(text);
setTimeout(moveOut,text.length * 100 + 3000);
}
const moveOut = ()=>{
particles.moveOut();
setTimeout(next,2000);
}
setTimeout(next,0);
}
function setStyle(ctx,style){
Object.keys(style).forEach(key => ctx[key] = style[key]);
}
// the following function create the particles from text using a canvas
// the canvas used is dsplayed on the main canvas top left fro referance.
var tCan = createImage(100, 100); // canvas used to draw text
function createTextMap(text,size,font,style,fit){
// function to conver to colour hex value
const hex = (v)=> (v < 16 ? "0" : "") + v.toString(16);
// set up font so we can find the size.
tCan.ctx.font = size + "px " + font;
// get size of text
var width = Math.ceil(tCan.ctx.measureText(text).width + size);
// resize the canvas to fit the text
tCan.width = width;
tCan.height = Math.ceil(size *1.2);
// c is alias for context
var c = tCan.ctx;
// set up font
c.font = size + "px " + font;
c.textAlign = "center";
c.textBaseline = "middle";
// set style
setStyle(c,style);
// only do stroke and fill if they are set in styles object
if(style.strokeStyle){
c.strokeText(text, width / 2, tCan.height / 2);
}
if(style.fillStyle){
c.fillText(text, width / 2, tCan.height/ 2);
}
// prep the particles
particles.empty();
// get the pixel data
var data = c.getImageData(0,0,width,tCan.height).data;
var x,y,ind,rgb,a;
// find pixels with alpha > 128
for(y = 0; y < tCan.height; y += 1){
for(x = 0; x < width; x += 1){
ind = (y * width + x) << 2; // << 2 is equiv to * 4
if(data[ind + 3] > 128){ // is alpha above half
rgb = `#${hex(data[ind ++])}${hex(data[ind ++])}${hex(data[ind ++])}`;
// add the particle
particles.add(Vec(x, y), Vec(x, y), rgb);
}
}
}
// scale the particles to fit bounding box
var scale = Math.min(fit.width / width, fit.height / tCan.height);
particles.each(p=>{
p.home.x = ((fit.left + fit.width) / 2) + (p.home.x - (width / 2)) * scale;
p.home.y = ((fit.top + fit.height) / 2) + (p.home.y - (tCan.height / 2)) * scale;
})
.findCenter() // get center used to move particles on and off of screen
.moveOffscreen() // moves particles off the screen
.moveIn(); // set the particles to move into view.
}
// vector object a quick copy from other code.
function Vec(x,y){ // because I dont like typing in new
return new _Vec(x,y);
}
function _Vec(x = 0,y = 0){
this.x = x;
this.y = y;
return this;
}
_Vec.prototype = {
setAs(vec){
this.x = vec.x;
this.y = vec.y;
},
toString(){
return `vec : { x : ${this.x}, y : ${this.y} );`
}
}
// basic particle
const particle = {
pos : null,
delta : null,
home : null,
col : "black",
}
// array of particles
const particles = {
items : [], // actual array of particles
mouseFX : { // mouse FX
power : 20,
dist : 100,
curve : 3, // polynomial power
on : true,
},
fx : {
speed : 0.4,
drag : 0.15,
size : 4,
jiggle : 8,
},
// direction 1 move in -1 move out
direction : 1,
moveOut(){this.direction = -1; return this},
moveIn(){this.direction = 1; return this},
length : 0, // Dont touch this from outside particles.
each(callback){ // custom iteration
for(var i = 0; i < this.length; i++){
callback(this.items[i],i);
}
return this;
},
empty(){ // empty but dont dereference
this.length = 0;
return this;
},
deRef(){ // call to clear memory
this.items.length = 0;
this.length = 0;
},
add(pos,home,col){ // adds a particle
var p;
if(this.length < this.items.length){
p = this.items[this.length++];
// p.pos.setAs(pos);
p.home.setAs(home);
p.delta.x = 0;
p.delta.y = 0;
p.col = col;
}else{
this.items.push(
Object.assign(
{},
particle,
{
pos,
home,
col,
delta : Vec()
}
)
);
this.length = this.items.length
}
return this;
},
draw(){ // draws all
var p, size, sizeh;
sizeh = (size = this.fx.size) / 2;
for(var i = 0; i < this.length; i++){
p = this.items[i];
ctx.fillStyle = p.col;
ctx.fillRect(p.pos.x - sizeh, p.pos.y - sizeh, size, size);
}
},
update(){ // update all particles
var p,x,y,d;
var mP = this.mouseFX.power;
var mD = this.mouseFX.dist;
var mC = this.mouseFX.curve;
var fxJ = this.fx.jiggle;
var fxD = this.fx.drag;
var fxS = this.fx.speed;
for(var i = 0; i < this.length; i++){
p = this.items[i];
p.delta.x += (p.home.x - p.pos.x ) * fxS + (Math.random() - 0.5) * fxJ;
p.delta.y += (p.home.y - p.pos.y ) * fxS + (Math.random() - 0.5) * fxJ;
p.delta.x *= fxD;
p.delta.y *= fxD;
p.pos.x += p.delta.x * this.direction;
p.pos.y += p.delta.y * this.direction;
if(this.mouseFX.on){
x = p.pos.x - mouse.x;
y = p.pos.y - mouse.y;
d = Math.sqrt(x * x + y * y);
if(d < mD){
x /= d;
y /= d;
d /= mD;
d = (1-Math.pow(d,mC)) * mP;
p.pos.x += x * d;
p.pos.y += y * d;
}
}
}
return this;
},
findCenter(){ // find the center of particles maybe could do without
var x,y;
y = x = 0;
this.each(p => {
x += p.home.x;
y += p.home.y;
});
this.center = Vec(x / this.length, y / this.length);
return this;
},
moveOffscreen(){ // move start pos offscreen
var dist,x,y;
dist = Math.sqrt(this.center.x * this.center.x + this.center.y * this.center.y);
this.each(p => {
var d;
x = p.home.x - this.center.x;
y = p.home.y - this.center.y;
d = Math.max(0.0001,Math.sqrt(x * x + y * y)); // max to make sure no zeros
p.pos.x = p.home.x + (x / d) * dist;
p.pos.y = p.home.y + (y / d) * dist;
});
return this;
},
}
function onResize(){ // called from boilerplate
if(!started){
startIt();
}
}
/** SimpleFullCanvasMouse.js begin **/
// the following globals are available
// w, h, cw, ch, width height centerWidth centerHeight of canvas
// canvas, ctx, mouse, globalTime
//MAIN animation loop
function display() { // call once per frame
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0, 0, w, h);
if(tCan){
// ctx.drawImage(tCan,0,0);
}
particles.update();
particles.draw();
}
/******************************************************************************
The code from here down is generic full page mouse and canvas boiler plate
code. As I do many examples which all require the same mouse and canvas
functionality I have created this code to keep a consistent interface. The
Code may or may not be part of the answer.
This code may or may not have ES6 only sections so will require a transpiler
such as babel.js to run on legacy browsers.
*****************************************************************************/
// V2.0 ES6 version for Stackoverflow and Groover QuickRun
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0;
// You can declare onResize (Note the capital R) as a callback that is also
// called once at start up. Warning on first call canvas may not be at full
// size.
;(function(){
const RESIZE_DEBOUNCE_TIME = 100;
var resizeTimeoutHandle;
var firstRun = true;
function createCanvas () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
function resizeCanvas () {
if (canvas === undefined) { canvas = createCanvas() }
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals() }
if (typeof onResize === "function") {
clearTimeout(resizeTimeoutHandle);
if (firstRun) { onResize() }
else { resizeTimeoutHandle = setTimeout(onResize, RESIZE_DEBOUNCE_TIME) }
firstRun = false;
}
}
function setGlobals () {
cw = (w = canvas.width) / 2;
ch = (h = canvas.height) / 2;
}
mouse = (function () {
function preventDefault(e) { e.preventDefault() }
var m; // alias for mouse
var mouse = {
x : 0, y : 0, w : 0, // mouse position and wheel
alt : false, shift : false, ctrl : false, // mouse modifiers
buttonRaw : 0,
over : false, // true if mouse over the element
buttonOnMasks : [0b1, 0b10, 0b100], // mouse button on masks
buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
active : false,
bounds : null,
eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(","),
event(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left - scrollX;
m.y = e.pageY - m.bounds.top - scrollY;
m.alt = e.altKey;
m.shift = e.shiftKey;
m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
else if (t === "mouseout") { m.over = false }
else if (t === "mouseover") { m.over = true }
else if (t === "mousewheel") {m.w = e.wheelDelta }
else if (t === "DOMMouseScroll") { m.w = -e.detail }
if (m.callbacks) { m.callbacks.forEach(c => c(e)) }
if ((m.buttonRaw & 2) && m.crashRecover !== null) {
if (typeof m.crashRecover === "function") { setTimeout(m.crashRecover, 0) }
}
e.preventDefault();
},
addCallback(callback) {
if (typeof callback === "function") {
if (m.callbacks === undefined) { m.callbacks = [callback] }
else { m.callbacks.push(callback) }
}
},
start(element) {
if (m.element !== undefined) { m.remove() }
m.element = element === undefined ? document : element;
m.eventNames.forEach(name => document.addEventListener(name, mouse.event) );
document.addEventListener("contextmenu", preventDefault, false);
m.active = true;
},
remove() {
if (m.element !== undefined) {
m.eventNames.forEach(name => document.removeEventListener(name, mouse.event) );
document.removeEventListener("contextmenu", preventDefault);
m.element = m.callbacks = undefined;
m.active = false;
}
}
}
m = mouse;
return mouse;
})();
function done() { // Clean up. Used where the IDE is on the same page.
window.removeEventListener("resize", resizeCanvas)
mouse.remove();
document.body.removeChild(canvas);
canvas = ctx = mouse = undefined;
}
function update(timer) { // Main update loop
if(ctx === undefined){ return }
globalTime = timer;
display(); // call demo code
requestAnimationFrame(update);
}
setTimeout(function(){
canvas = createCanvas();
mouse.start(canvas, true);
resizeCanvas();
if(typeof Groover !== "undefined" && Groover.ide){ mouse.crashRecover = done }; // Requires Ace.js and GrooverDev.js. Prevents
window.addEventListener("resize", resizeCanvas);
requestAnimationFrame(update);
},0);
})();
/** SimpleFullCanvasMouse.js end **/
/** CreateImage.js begin **/
// creates a blank image with 2d context
function createImage(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
/** CreateImage.js end **/
canvas {
background : black;
}
well after all the attempts like hell I finally got my answer thanks to moƔois answer to my post using particle slider. here is the code. Thanks everyone for your help :)

Fade out text and fade in next canvas

So I have a canvas in my game which will display some text but I would like it to be if you have ever played Clicker Heros when you click theres some damage text displayed but its faded in and out slowly and kinda moves upward while fading out I would like to produce the same effect here
So what I have is a function which is called when the user clicks the Terminal I need the text to produce a similiar behavior but I am very new to canvas and not sure how to do so here is my current code
var canvas = document.getElementById("terminalCanvas");
var terminal = canvas.getContext("2d");
terminal.fillStyle = "#000000";
terminal.fillRect(0,0,150,200);
function WriteToCanvas(){
if(Game.Player.HTMLSupport == 1){
var rand = Math.floor(Math.random() * 122) + 1;
var tag = htmltags[rand];
terminal.font = "20px Comic Sans MS";
terminal.fillStyle = "rgb(0,255,1)";
terminal.textAlign = "center";
terminal.fillText(tag, canvas.width/2, canvas.height/2);
ClearCanvas();
}
}
function ClearCanvas(){
terminal.clearRect(0,0,canvas.width,canvas.height);
terminal.fillStyle = "#000000";
terminal.fillRect(0,0,150,200);
}
A particle system is what you need. Bellow at the top of the code is what you need to do a simple and memory efficient particle system.
Uses a particle pool. Dead particles go to the pool when their time is up. When new particles are needed check if any dead ones are in the pool, if so resurrect them, else create a new one. This avoids incurring the dreaded GC lag that particle systems can make work extra hard.
It may pay that you don't use the fillText (as it is very slow) to render the particle, but pre render and use drawImage. Up to you.
Sorry I dont have time for a deeper explanation
/*=====================================================================================
ANSWER code start
=====================================================================================*/
const LIFETIME = 1 / 180; // 180 frames
const YDIST = -140; // number of pixels to move over the life
const MOVE_CURVE_SHAPE = 1.5;
const FADE_CURVE_SHAPE = 1.5;
const FADE_CURVE_ADVANCE = 0.25; // Want the fade not to start early on the fade curve
var particles = []; // array to hold live particles
var particlePool = []; // to hold the dead
// this function is called once a frame
function display(){ // put code in here
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0,0,w,h);
ctx.font = "40px Comic Sans MS";
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("Click mouse to add particles",canvas.width/2, 30);
if(mouse.buttonRaw & 1){
var p;
if(particlePool.length){ // check if the are any dead particles in the pool
p = particlePool.shift(); // if so get the first on in out
}else{ // nothing in the pool so create a new on
p = {};
}
// set up the paticle
var text = (Math.floor(Math.random()* 10) *100) + "!"; // the text to display
p = createParticle(mouse.x,mouse.y,text,p); // set up the particle
particles.push(p); // push it onto the active array
mouse.buttonRaw = 0; // clear mouse down;
}
updateParticles(); // update particles
renderParticles(); // and draw them
}
// sets up a particle x,y startung pos, text the value to display, p and object to hold the data
function createParticle(x,y,text,p){
p.life = 1; // when this get down to zero it is dead
p.x = x;
p.y = y;
p.text = text;
return p;
}
// ease functions
var easeBell = function (x, pow) { // x 0-1 pos > 0
x = x * 2;
if( x > 1){
x = 1 - (x - 1);
var xx = Math.pow(x,pow);
return xx / (xx + Math.pow(1 - x, pow));
}else{
var xx = Math.pow(x,pow);
return xx / (xx + Math.pow(1 - x, pow));
}
}
var ease = function (x, pow) { // x 0-1 pos > 0
var xx = Math.pow(x,pow);
return xx / (xx + Math.pow(1 - x, pow));
}
function updateParticles(){ // update the life and death of the particles
for(var i = 0; i < particles.length; i ++){
var p = particles[i];
p.life -= LIFETIME;
if(p.life <= 0){ // time is up this particle is dead
// move it to the grave
particlePool.push(p);
particles.splice(i,1); // remove it from the array
i -= 1; // adjust i so we dont skip any
}
}
}
function renderParticles(){
ctx.font = "20px Comic Sans MS"
ctx.fillStyle = "#F00";
for(var i = 0; i < particles.length; i ++){ // for each active particle
var p = particles[i];
var fadeCurveVal = 1- p.life;
fadeCurveVal *= (1 - FADE_CURVE_ADVANCE); // scale it down
fadeCurveVal += FADE_CURVE_ADVANCE; // move it forward
ctx.globalAlpha = easeBell(fadeCurveVal,FADE_CURVE_SHAPE); // get the fade fx
var y = p.y + ease((1-p.life)/2,MOVE_CURVE_SHAPE) * YDIST * 2;
ctx.fillText(p.text,p.x,y);
}
}
/*=====================================================================================
ANSWER code End
=====================================================================================*/
/** SimpleFullCanvasMouse.js begin **/
const CANVAS_ELEMENT_ID = "canv";
const U = undefined;
var w, h, cw, ch; // short cut vars
var canvas, ctx, mouse;
var globalTime = 0;
var createCanvas, resizeCanvas, setGlobals;
var L = typeof log === "function" ? log : function(d){ console.log(d); }
createCanvas = function () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
c.id = CANVAS_ELEMENT_ID;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === U) { canvas = createCanvas(); }
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals(); }
}
setGlobals = function(){ cw = (w = canvas.width) / 2; ch = (h = canvas.height) / 2; }
mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, w : 0, alt : false, shift : false, ctrl : false, buttonRaw : 0,
over : false, // mouse is over the element
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === U) { m.x = e.clientX; m.y = e.clientY; }
m.alt = e.altKey; m.shift = e.shiftKey; m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1]; }
else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2]; }
else if (t === "mouseout") { m.buttonRaw = 0; m.over = false; }
else if (t === "mouseover") { m.over = true; }
else if (t === "mousewheel") { m.w = e.wheelDelta; }
else if (t === "DOMMouseScroll") { m.w = -e.detail; }
if (m.callbacks) { m.callbacks.forEach(c => c(e)); }
e.preventDefault();
}
m.addCallback = function (callback) {
if (typeof callback === "function") {
if (m.callbacks === U) { m.callbacks = [callback]; }
else { m.callbacks.push(callback); }
} else { throw new TypeError("mouse.addCallback argument must be a function"); }
}
m.start = function (element, blockContextMenu) {
if (m.element !== U) { m.removeMouse(); }
m.element = element === U ? document : element;
m.blockContextMenu = blockContextMenu === U ? false : blockContextMenu;
m.mouseEvents.forEach( n => { m.element.addEventListener(n, mouseMove); } );
if (m.blockContextMenu === true) { m.element.addEventListener("contextmenu", preventDefault, false); }
}
m.remove = function () {
if (m.element !== U) {
m.mouseEvents.forEach(n => { m.element.removeEventListener(n, mouseMove); } );
if (m.contextMenuBlocked === true) { m.element.removeEventListener("contextmenu", preventDefault);}
m.element = m.callbacks = m.contextMenuBlocked = U;
}
}
return mouse;
})();
var done = function(){
window.removeEventListener("resize",resizeCanvas)
mouse.remove();
document.body.removeChild(canvas);
canvas = ctx = mouse = U;
L("All done!")
}
resizeCanvas(); // create and size canvas
mouse.start(canvas,true); // start mouse on canvas and block context menu
window.addEventListener("resize",resizeCanvas); // add resize event
function update(timer){ // Main update loop
globalTime = timer;
display(); // call demo code
// continue until mouse right down
if (!(mouse.buttonRaw & 4)) { requestAnimationFrame(update); } else { done(); }
}
requestAnimationFrame(update);
/** SimpleFullCanvasMouse.js end **/

Draw random coloured circles on canvas

I am trying to create a animation where circles run from right to left. The circles' colours are selected randomly by a function. I have created a fiddle where one circle runs from right to left. Now my function creates a random colour. This function is executed every second and the circle changes its colour every second, instead of a new circle with the random picked colour become created. How can I change it so that it draws a new circle every second on the canvas and doesn't only change the colour of the circle?
This is my function:
function getRandomElement(array) {
if (array.length == 0) {
return undefined;
}
return array[Math.floor(Math.random() * array.length)];
}
var circles = [
'#FFFF00',
'#FF0000',
'#0000FF'
];
function drawcircles() {
ctx.beginPath();
ctx.arc(ballx * 108, canvasHeight / 2, x*5, 0, 2*Math.PI, false);
ctx.fillStyle = getRandomElement(circles);
ctx.fill();
ctx.closePath;
}
Comments on your question:
You are changing the color of the fillStyle to a random color at each frame. This is the reason why it keeps "changing" color. Set it to the color of the circle:
context.fillStyle = circle.color;
make circles with x, y, diameter, bounciness, speed, and color using an array
draw and update them with requestAnimationFrame (mine is a custom function)
My answer:
I made this just last night, where some circles follow the cursor and "bounce" off the edges of the screen. I tested the code and it works.
I might post a link later, but here is all of the code right now...
<!DOCTYPE html>
<html>
<body>
<canvas id="canvas"></canvas>
<script>
var lastTime = 0;
function requestMyAnimationFrame(callback, time)
{
var t = time || 16;
var currTime = new Date().getTime();
var timeToCall = Math.max(0, t - (currTime - lastTime));
var id = window.setTimeout(function(){ callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
canvas.style.width = canvas.width + "px";
canvas.style.height = canvas.height + "px";
var circles = [];
var mouse =
{
x: 0,
y: 0
}
function getCoordinates(x, y)
{
return "(" + x + ", " + y + ")";
}
function getRatio(n, d)
{
// prevent division by 0
if (d === 0 || n === 0)
{
return 0;
}
else
{
return n/d;
}
}
function Circle(x,y,d,b,s,c)
{
this.x = x;
this.y = y;
this.diameter = Math.round(d);
this.radius = Math.round(d/2);
this.bounciness = b;
this.speed = s;
this.color = c;
this.deltaX = 0;
this.deltaY = 0;
this.drawnPosition = "";
this.fill = function()
{
context.beginPath();
context.arc(this.x+this.radius,this.y+this.radius,this.radius,0,Math.PI*2,false);
context.closePath();
context.fill();
}
this.clear = function()
{
context.fillStyle = "#ffffff";
this.fill();
}
this.draw = function()
{
if (this.drawnPosition !== getCoordinates(this.x, this.y))
{
context.fillStyle = this.color;
// if commented, the circle will be drawn if it is in the same position
//this.drawnPosition = getCoordinates(this.x, this.y);
this.fill();
}
}
this.keepInBounds = function()
{
if (this.x < 0)
{
this.x = 0;
this.deltaX *= -1 * this.bounciness;
}
else if (this.x + this.diameter > canvas.width)
{
this.x = canvas.width - this.diameter;
this.deltaX *= -1 * this.bounciness;
}
if (this.y < 0)
{
this.y = 0;
this.deltaY *= -1 * this.bounciness;
}
else if (this.y+this.diameter > canvas.height)
{
this.y = canvas.height - this.diameter;
this.deltaY *= -1 * this.bounciness;
}
}
this.followMouse = function()
{
// deltaX/deltaY will currently cause the circles to "orbit" around the cursor forever unless it hits a wall
var centerX = Math.round(this.x + this.radius);
var centerY = Math.round(this.y + this.radius);
if (centerX < mouse.x)
{
// circle is to the left of the mouse, so move the circle to the right
this.deltaX += this.speed;
}
else if (centerX > mouse.x)
{
// circle is to the right of the mouse, so move the circle to the left
this.deltaX -= this.speed;
}
else
{
//this.deltaX = 0;
}
if (centerY < mouse.y)
{
// circle is above the mouse, so move the circle downwards
this.deltaY += this.speed;
}
else if (centerY > mouse.y)
{
// circle is under the mouse, so move the circle upwards
this.deltaY -= this.speed;
}
else
{
//this.deltaY = 0;
}
this.x += this.deltaX;
this.y += this.deltaY;
this.x = Math.round(this.x);
this.y = Math.round(this.y);
}
}
function getRandomDecimal(min, max)
{
return Math.random() * (max-min) + min;
}
function getRoundedNum(min, max)
{
return Math.round(getRandomDecimal(min, max));
}
function getRandomColor()
{
// array of three colors
var colors = [];
// go through loop and add three integers between 0 and 255 (min and max color values)
for (var i = 0; i < 3; i++)
{
colors[i] = getRoundedNum(0, 255);
}
// return rgb value (RED, GREEN, BLUE)
return "rgb(" + colors[0] + "," + colors[1] + ", " + colors[2] + ")";
}
function createCircle(i)
{
// diameter of circle
var minDiameter = 25;
var maxDiameter = 50;
// bounciness of circle (changes speed if it hits a wall)
var minBounciness = 0.2;
var maxBounciness = 0.65;
// speed of circle (how fast it moves)
var minSpeed = 0.3;
var maxSpeed = 0.45;
// getRoundedNum returns a random integer and getRandomDecimal returns a random decimal
var x = getRoundedNum(0, canvas.width);
var y = getRoundedNum(0, canvas.height);
var d = getRoundedNum(minDiameter, maxDiameter);
var c = getRandomColor();
var b = getRandomDecimal(minBounciness, maxBounciness);
var s = getRandomDecimal(minSpeed, maxSpeed);
// create the circle with x, y, diameter, bounciness, speed, and color
circles[i] = new Circle(x,y,d,b,s,c);
}
function makeCircles()
{
var maxCircles = getRoundedNum(2, 5);
for (var i = 0; i < maxCircles; i++)
{
createCircle(i);
}
}
function drawCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].draw();
ii++;
}
}
}
function clearCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].clear();
ii++;
}
}
}
function updateCircles()
{
var ii = 0;
for (var i = 0; ii < circles.length; i++)
{
if (circles[i])
{
circles[i].keepInBounds();
circles[i].followMouse();
ii++;
}
}
}
function update()
{
requestMyAnimationFrame(update,10);
updateCircles();
}
function draw()
{
requestMyAnimationFrame(draw,1000/60);
context.clearRect(0,0,canvas.width,canvas.height);
drawCircles();
}
function handleError(e)
{
//e.preventDefault();
//console.error(" ERROR ------ " + e.message + " ------ ERROR ");
}
window.addEventListener("load", function()
{
window.addEventListener("error", function(e)
{
handleError(e);
});
window.addEventListener("resize", function()
{
canvas.width = window.innerWidth - 20;
canvas.height = window.innerHeight - 20;
});
window.addEventListener("mousemove", function(e)
{
mouse.x = e.layerX || e.offsetX;
mouse.y = e.layerY || e.offsetY;
});
makeCircles();
update();
draw();
});
</script>
</body>
</html>

Categories

Resources