So I wrote this code for a simple game. The code runs at 60 fps in both Chrome and Safari but Firefox barely manages 30-40 fps. The code looks simple enough to me. What could be causing the delay?
I checked in firebug and found out that only one function "follow" is taking up all the time. Here is the code:
function checkCollision (ball0, ball1) {
var dx = ball1.X - ball0.X,
dy = ball1.Y - ball0.Y,
dist = Math.sqrt(dx * dx + dy * dy);
if (dist < ball0.rad + ball1.rad) {
var angle = Math.atan2(dy, dx),
sin = Math.sin(angle),
cos = Math.cos(angle);
var pos0 = {x: 0, y: 0},
pos1 = rotate(dx, dy, sin, cos, true),
vel0 = rotate(ball0.spdX, ball0.spdY, sin, cos, true),
vel1 = rotate(ball1.spdX, ball1.spdY, sin, cos, true),
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;
var absV = Math.abs(vel0.x) + Math.abs(vel1.x),
overlap = (ball0.rad + ball1.rad) - 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);
ball1.X = ball0.X + pos1F.x;
ball1.Y = ball0.Y + pos1F.y;
ball0.X = ball0.X + pos0F.x;
ball0.Y = ball0.Y + pos0F.y;
var vel0F = rotate(vel0.x, vel0.y, sin, cos, false),
vel1F = rotate(vel1.x, vel1.y, sin, cos, false);
ball0.spdX = vel0F.x;
ball0.spdY = vel0F.y;
ball1.spdX = vel1F.x;
ball1.spdY = vel1F.y;
}
}
function move()
{
var side,i;
for (i=0 ; i < balls.length; i++)
{
var obj = balls[i];
if (side=obj.edgeX())
{
if (side === 'l')
obj.X = obj.rad;
else if (side === 'r')
obj.X = canvas.width() - obj.rad;
obj.spdX*=-1;
}
if (side=obj.edgeY())
{
if (side === 't')
obj.Y = obj.rad;
else if (side === 'b')
obj.Y = canvas.height() - obj.rad;
obj.spdY*=-1;
}
if (leash == true && i === 0)
{
if (mouse.X>obj.X && (obj.spdX<10))
obj.spdX+=obj.accX;
else if (mouse.X<obj.X && (obj.spdX>-10))
obj.spdX-=obj.accX;
if (mouse.Y>obj.Y && (obj.spdY<10))
obj.spdY+=obj.accY;
else if (mouse.Y<obj.Y && (obj.spdY>-10))
obj.spdY-=obj.accY;
}
obj.X+=obj.spdX;
obj.Y+=obj.spdY;
if (Math.abs(obj.spdX)>0.1)
obj.spdX*=0.98;
else obj.spdX=0;
if (Math.abs(obj.spdY)>0.1)
obj.spdY*=0.98;
else obj.spdY = 0;
};
}
function follow()
{
var ballA, i, ballB,j;
requestAnimationFrame(follow);
//stats.begin();
context.clearRect(0,0,canvas.width(),canvas.height());
move();
for (i = 0, len = balls.length - 1; i < len; i++) {
ballA = balls[i];
for (j = i + 1; j < balls.length; j++) {
ballB = balls[j];
checkCollision(ballA, ballB);
}
}
balls.forEach(function(obj){
drawCircle(obj.X,obj.Y,obj.rad, obj.color);
if (leash == true && obj.control === true)
{drawLeash(mouse.X,mouse.Y,obj.X,obj.Y,obj.color);}
});
//stats.end();
};
Here is the animation: http://ipsumturpis.xtreemhost.com/follower/index.html
I vaguely remembered there used to be a problem in FF regarding with canvas drawing performance, so I have commented out drawCircle(obj.X,obj.Y,obj.rad, obj.color); and poof, magic happened - my frame rate went up from 11 FPS to 60.
Try caching balls length in a variable. Unless it’s absolutely necessary for reasons I don’t see, running balls.length (or any function) in every iteration of a loop is naturally going to be time consuming.
So try something like this;
ballslen = balls.length;
for (j = i + 1; j < ballslen; j++)
Related
i want optimize function about tang of path at specific point
is there a way to optimize this code
but it not optimazed in big paths
i want optimie this to work with large svg path in short time
function getTangent(pathNode, point){
var line_path = pathNode;
var length_at_point = 0,
total_length = line_path.getTotalLength();
while ((Math.trunc(
line_path.getPointAtLength(length_at_point).x) != Math.trunc(point[0]) ||
Math.trunc(line_path.getPointAtLength(length_at_point).y) != Math.trunc(point[1])) &&
length_at_point < total_length) {
length_at_point++;
};
var point = line_path.getPointAtLength(length_at_point),
prev = {},
next = {},
delta = {};
if (length_at_point > 1 && length_at_point < (total_length - 1)) {
prev = line_path.getPointAtLength(length_at_point - 1);
next = line_path.getPointAtLength(length_at_point + 1);
delta = {
x: next.x - prev.x,
y: next.y - prev.y
}
} else {
// don't worry about the first and last pixel or so
return;
};
var LENGTH = 700; // length of tangent line
return {
id:'tangent'
, "stroke-width":0.5
, stroke:'rgb(6,120,155)'
, x1:(point.x - delta.x * LENGTH)
, y1:(point.y - delta.y * LENGTH)
, x2:(point.x + delta.x * LENGTH)
, y2:(point.y + delta.y * LENGTH)
};
}
So I have this class which is used for shape morphing:
class ShapeOverlays {
constructor(elm) {
this.elm = elm;
this.path = elm.querySelectorAll('path');
this.numPoints = 18;
this.duration = 600;
this.delayPointsArray = [];
this.delayPointsMax = 300;
this.delayPerPath = 100;
this.timeStart = Date.now();
this.isOpened = false;
this.isAnimating = false;
}
toggle() {
this.isAnimating = true;
const range = 4 * Math.random() + 6;
for (var i = 0; i < this.numPoints; i++) {
const radian = i / (this.numPoints - 1) * Math.PI;
this.delayPointsArray[i] = (Math.sin(-radian) + Math.sin(-radian * range) + 2) / 4 * this.delayPointsMax;
}
if (this.isOpened === false) {
this.open();
} else {
this.close();
}
}
open() {
this.isOpened = true;
this.elm.classList.add('is-opened');
this.timeStart = Date.now();
this.renderLoop();
}
close() {
this.isOpened = false;
this.elm.classList.remove('is-opened');
this.timeStart = Date.now();
this.renderLoop();
}
updatePath(time) {
const points = [];
for (var i = 0; i < this.numPoints + 1; i++) {
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
}
let str = '';
str += (this.isOpened) ? `M 0 0 V ${points[0]} ` : `M 0 ${points[0]} `;
for (var i = 0; i < this.numPoints - 1; i++) {
const p = (i + 1) / (this.numPoints - 1) * 100;
const cp = p - (1 / (this.numPoints - 1) * 100) / 2;
str += `C ${cp} ${points[i]} ${cp} ${points[i + 1]} ${p} ${points[i + 1]} `;
}
str += (this.isOpened) ? `V 0 H 0` : `V 100 H 0`;
return str;
}
render() {
if (this.isOpened) {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i)));
}
} else {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - i - 1))));
}
}
}
renderLoop() {
this.render();
if (Date.now() - this.timeStart < this.duration + this.delayPerPath * (this.path.length - 1) + this.delayPointsMax) {
requestAnimationFrame(() => {
this.renderLoop();
});
}
else {
this.isAnimating = false;
}
}
}
(function() {
const elmHamburger = document.querySelector('.hamburger');
const gNavItems = document.querySelectorAll('.global-menu__item');
const elmOverlay = document.querySelector('.shape-overlays');
const overlay = new ShapeOverlays(elmOverlay);
elmHamburger.addEventListener('click', () => {
if (overlay.isAnimating) {
return false;
}
overlay.toggle();
if (overlay.isOpened === true) {
elmHamburger.classList.add('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.add('is-opened');
}
} else {
elmHamburger.classList.remove('is-opened-navi');
for (var i = 0; i < gNavItems.length; i++) {
gNavItems[i].classList.remove('is-opened');
}
}
});
}());
Can some one please explain this code? I don't really get how the paths are created using time,how the points are placed and how could I modify it.What is range used for? Why are trigonometral functions used for the delayPointsArray?
Basically it's this part that I don't get:
updatePath(time) {
const points = [];
for (var i = 0; i < this.numPoints + 1; i++) {
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
}
let str = '';
str += (this.isOpened) ? `M 0 0 V ${points[0]} ` : `M 0 ${points[0]} `;
for (var i = 0; i < this.numPoints - 1; i++) {
const p = (i + 1) / (this.numPoints - 1) * 100;
const cp = p - (1 / (this.numPoints - 1) * 100) / 2;
str += `C ${cp} ${points[i]} ${cp} ${points[i + 1]} ${p} ${points[i + 1]} `;
}
str += (this.isOpened) ? `V 0 H 0` : `V 100 H 0`;
return str;
}
render() {
if (this.isOpened) {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i)));
}
} else {
for (var i = 0; i < this.path.length; i++) {
this.path[i].setAttribute('d', this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * (this.path.length - i - 1))));
}
}
}
Why is time being used? What is the purpose of this:
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
If you look at how updatePath() is being called, it's like this:
this.updatePath(Date.now() - (this.timeStart + this.delayPerPath * i))
So the time value passed in is the difference between the current time, and the start time of the path we are working with.
So what then is the line of code you are interested in, doing?
points[i] = ease.cubicInOut(Math.min(Math.max(time - this.delayPointsArray[i], 0) / this.duration, 1)) * 100
I'm going to ignore delayPointsArray. It is modifying the start time slightly based on angle. Without seeing the full demo, I'm not sure of the reason for that.
The purpose of this line of code is to calculate how far through the current path's animation we are. The result is in the form of a coordinate value from 0 to 100.
It's doing a lot in that one line of code. So let's break down the individual steps.
Firstly, we are clamping the elapsed time to minimum of 0.
Math.max(time, 0)
In other words, anything before the animation start time becomes zero.
Then we divide by the animation's duration.
Math.max(time, 0) / duration
This will result in a value from 0, representing the start of the animation, to 1, representing the end of the animation. However, the value might also be greater than 1 if the elapsed time is after the end of the animation. Hence the next step.
Now clamp this value to a maximum of 1.
Math.min( Math.max(time, 0) / duration, 1)
We now have a value >= 0 and <= 1 whichdescribes where in the course of the animation, the path is supposed to be. 0 if we should be at the animations start position. 1 if we should be at the animations end position. And somewhere in between if the animation is in progress.
However this value is strictly linear, corresponding with the progression of time. And usually linear movement is not what you want. It is unnatural. Objects accelarate when the start moving and decelerate when the come to a stop. That will be what the easeInOut() function will be doing. If you are not familiar with easing curves, take a look at the diagram below.
Source: Google: The Basics of Easing
So we pass in a linear time value from 0..1 (horizontal axis). It will return a modified value that takes into account acceleration and deceleration.
The final step is to multiply by 100, to convert to a final coordinate value (0..100).
Hope this helps.
i'm doing a "Steve Reich - Clapping Music" kinda thing with "xoxoxoxx" with x as the clapping. but i want it to write the pattern while it keep going to the right. so you'd have this kinda writing:
xoxoxoxxxoxoxoxxxoxoxoxxxoxoxoxx
xoxoxoxxoxoxoxxxoxoxxoxoxxxoxoxo
so it prints the X or O and then goes a bit to the right and prints again. I hope this is clear, english isn't my first language, so i'm sorry if it's hard to understand. here is the full code for 2 lines because i'm bad at explaining:
var noise, env;
var seq = "o x o x o x o x o x o x o x x x";
var steps = seq.split(" ");
var speed = 8;
var count = 0;
var count2 = 0;
var count3=0;
var shift = 0;
var repeat = 1;
var sf, sf2, sf3;
var f;
function preload() {
sf = loadSound("./files/clap.wav");
sf2 = sf;
sf3 = sf;
}
function setup() {
createCanvas(400, 400);
env = new p5.Env(0.01, 1, 0.2, 0.1);
env2 = new p5.Env(0.1, 0.8, 0.01, 0.1);
env3 = new p5.Env(0.05, 0.9, 0.1, 0.1);
}
function hitMeSteve(when, env, loc) {
if (when == 'x' && frameCount % speed == 0) {
env.play();
}
}
function draw() {
if (frameCount % speed == 0) {
count++;
}
if (frameCount % (steps.length * speed * repeat) == 0) {
shift++;
count2=count2+2;
count3=count3+4;
}
if(shift==4){
shift=0;
count2=0;
count3=0;
}
shift = shift % steps.length;
shift2 = shift + 2;
var now = steps[count % steps.length];
hitMeSteve(now, sf, 10);
var canon = steps[(count + shift) % steps.length];
hitMeSteve(canon, sf2, width / 2 + 10);
var canon2 = steps[(count + shift+count2) % steps.length];
hitMeSteve(canon2, sf3, width / 2 + 20);
textSize(30);
//1
for (var i = 0; i < steps.length; i++) {
if (i == count % steps.length) {
fill(255, 180, 0);
} else {
fill(0);
}
text(steps[i],10+ ( + i) * 15,20);
//text(steps[i], 110 + (shift / 2 + i) * 15, height / 2);
}
//2
for (var i = 0; i < steps.length; i++) {
if (i == (count + shift) % steps.length) {
fill(255, 180, 0);
} else {
fill(0);
}
text(steps[i],10+( + i)*15,40);
//text(steps[i], 110 + (-shift / 2 + i) * 15, height / 2 + 20);
}
}
Just a proposal with setInterval, maybe this works for you.
var content = "oxoxoxoxoxoxoxxx",
target = document.getElementById('ticker'),
i = 0,
timer = setInterval(addChar, 800);
function addChar() {
if (i < content.length) {
target.innerHTML += ' ' + content[i];
i++;
} else {
target.innerHTML = '';
i=0;
}
}
<div id="ticker"></div>
I am trying to draw a between two lines but arc is not rendering properly. I am using the following code:
imageContext- Canvas's Context
x1- starting point
y1-starting point
multiPointArr- Array which contains points of line(s)
imageContext.beginPath();
imageContext.moveTo(x1,y1);
if(multiPointArr.length==2)
imageContext.lineTo(x2,y2);
if(multiPointArr.length>2){
for( var ii=1 ; ii < multiPointArr.length ; ii++ ){
imageContext.lineTo(multiPointArr[ii].x,multiPointArr[ii].y);
}
}
imageContext.stroke();
if(multiPointArr.length==3)
{
imageContext.beginPath();
var xArr=[multiPointArr[0].x,multiPointArr[1].x,multiPointArr[2].x];
var yArr=[multiPointArr[0].y,multiPointArr[1].y,multiPointArr[2].y];
var m1 = Math.abs(Math.atan((yArr[1] - yArr[0])/(xArr[1] -xArr[0]))/Math.PI*180.0);
var m2 = Math.abs(Math.atan((yArr[1] - yArr[2])/(xArr[1] -xArr[2]))/Math.PI*180.0);
if(xArr[0] >= xArr[1])
{
if(yArr[0] >= yArr[1]);
else m1 = 360 - m1;
}
else
{
if(yArr[0] >= yArr[1])m1 = 180 - m1;
else m1 = 180 + m1;
}
if(xArr[2] >= xArr[1])
{
if(yArr[2] >= yArr[1]);
else m2 = 360 - m2;
}
else
{
if(yArr[2] >= yArr[1])m2 = 180 - m2;
else m2 = 180 + m2;
}
var arr=[];
arr[0] = Math.min(m1, m2);
arr[1] = Math.abs(m1 - m2);
arr[2] = arr[1];
if(arr[1] > 180)
{
arr[0] = Math.max(m1,m2);
arr[1] = 360 - arr[1];
}
arr[0]=(arr[0]*Math.PI)/180;
arr[1]=(arr[1]*Math.PI)/180;
arr[2]=(arr[2]*Math.PI)/180;
var ArcLen = 30;
imageContext.arc((xArr[1]),(yArr[1]),Math.abs(ArcLen*2),arr[0],arr[1],false);
imageContext.stroke();
}
Using above code arc is not drawing between lines.
It looks like following:
However starting angle is drawing correct but there is a problem with endAngle. Please let me know how I can resolve this problem.
I can't figure out why the memory is increasing and it stays there each time I run this code:
easingFunction = function (t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
processFrame = function () {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
tile.percent += 4;
if (tile.percent > 0) {
var TH = Math.max(0, Math.min(TILE_HEIGHT, targetObj.height - tile.imageY));
var TW = Math.max(0, Math.min(TILE_WIDTH, targetObj.width - tile.imageX));
var SW, SH, SX, SY, amount;
draw.save();
draw.translate(tile.imageX, tile.imageY);
if (direction == "tb" || direction == "bt") {
amount = easingFunction(tile.percent, 0, TW, 100);
SW = Math.min(TW, amount);
SH = TH;
SX = 0;
SY = 0;
} else {
amount = easingFunction(tile.percent, 0, TH, 100);
SW = TW;
SH = Math.min(TH, amount);
SX = 0;
SY = 0;
}
draw.drawImage(copycanvas, tile.imageX, tile.imageY, SW, SH, SX, SY, SW, SH);
draw.restore();
}
}
var ok = true;
for (i = 0; i < tiles.length; i++) {
if (tiles[i].percent < 100) {
ok = false;
break;
}
}
if (ok) {
clearInterval(interval);
showComplete();
}
};
this.show = function (target, hideTarget) {
createTiles();
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
tile.percent = 0 - i * 10;
}
}
var intervalDelay = (config.duration * 1000) / (tiles.length * 3 + 25);
interval = setInterval(function () {
processFrame();
}, intervalDelay);
};
function Tile() {
this.imageX = 0;
this.imageY = 0;
this.percent = 0;
};
};
I left out some unimportant code. The ideea is that I call externally the show() function. The setInterval is initialized and runs processFrame() about 100 times.
I've tried to leave some code outside from processFrame, and I got to :
processFrame = function () {
for (var i = 0; i < tiles.length; i++) {
var tile = tiles[i];
tile.percent += 4;
}
var ok = true;
for (i = 0; i < tiles.length; i++) {
if (tiles[i].percent < 100) {
ok = false;
break;
}
}
if (ok) {
clearInterval(interval);
showComplete();
}
};
But the memory still increases.
Try validating your code with JSLint. http://www.jslint.com/
Right now your adding easingFunction & processFrame to the Global object (which isn't a good thing). Not that this is the cause of the problem, but I've found that mismanagement of my objects is the usual cause of memory leaks.
You'll want to do something like:
var MyObject = {};
MyObject.easingFunction = function(){};
MyObject.processFrame = function(){};
In short make sure you declare all objects with var before using them.
I found the problem. I was continuously redrawing the canvas. To resolve this problem I had to erase the canvas each time before modifying it.