Reading data from the usb port in Javascript
Smooth organic animation around a specific point in javascript
I recently found a very cool example in d3 of a series of particles which cluster together and scatter when you put your mouse cursor near them. It would work well for the intro to an installation I was working on for a museum. However, the museum requested that when no one was interacting with the device, that the animation happen passively. To do so, I needed to simulate the movement of the mouse cursor on the screen. The most interesting aspect of the example was when the cursor was in the center of the particles and they encircled the cursor point. So I wanted to keep the cursor near the center of the screen - but I wanted it to swing a bit unpredictably around that center point. After some research, I found some posts about oscillators.
Normally used with sound in the form of sine and cosine waves, oscillators combine an amplitude (the amount of movement in the wave from trough to crest), and period (time to repetition) in order to create a repeating pattern. When combining sine and cosine movement for x and y axis, along with randomized amplitude and period, you get some really nice motion.
Here's what I created:
You can see a working demo of that here.
And here's how I determined the position in code.
First we need a few variables to keep track of the motion:
var idleAF; //the request animation frame for the idle animation
var idleTime; //the idle timestamp
var wcx = window.width/2;
var wcy = window.height/2;
We are going to be updating the point of motion using requestAnimationFrame. If you've never used it before, here's a decent intro.. The animation frame will be stored in `idleAF`. Next we need a variable to keep track of time. Time is going to be the thing that determines at what point in the oscillation we are each time the animation frame is called - thus the variable `idleTime`. The variables wcx and wcy simply establish the center of the screen
Now we need some variables for the oscillator itself:
var amp = 200; //base amplitude of the oscillator
var ampVariance = 50; //variance in amplitude for each animation frame
Here we're setting a base amplitude and period, and setting a limit for the amount they can vary each time we check them.
Next we put the animation frame in motion. I'm going to skip the details on how I determine whether the screen is idle or not (let me know if you want details on that).
idleAF = requestAnimationFrame(setRandomPosition);
var setRandomPosition = function(timestamp){
if (isIdle){
if (!idleTime){
idleTime = timestamp;
}
var progress = timestamp - idleTime;
idleAF = requestAnimationFrame(setRandomPosition);
}
}
Here we're starting the animation frame. Notice the parameter timestamp - this keeps track of the time each time the animation frame is called. We create the variable progress, which subtracts the last timestamp from the current to get the time elapsed. Them we call the animation frame again.
Next, we add to this function to use this value for the x and y coordinates:
var setRandomPosition = function(timestamp){
if (!idleTime){
idleTime = timestamp;
}
var progress = timestamp - idleTime;
//NEW CODE
var nextAmp = randomNumber(amp-ampVariance,amp+ampVariance);
var nextX = nextAmp * Math.sin(progress * 2 * Math.PI / period) + wcx;
var nextY = nextAmp * Math.cos(progress * 2 * Math.PI / period) + wcy;
//END NEW CODE
idleAF = requestAnimationFrame(setRandomPosition);
}
The variable nextAmp calls a random number function to generate a number that is equal to the base amplitude plus or minus the variance. Here's that random number function if you're interested:
randomNumber = function(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}
The variables nextX and nextY determine the new position. We will animate our X position with a sine wave, and our Y with a cosine wave. The 2*Math.PI helps keep the motion circular, centered around the middle of the screen (wcx and wcy).
So, that's all we need in order to create the animation point. If you wanted to apply it to the d3 example, you would then add something like this:
var setRandomPosition = function(timestamp){
if (!idleTime){
idleTime = timestamp;
}
var progress = timestamp - idleTime;
var nextAmp = randomNumber(amp-ampVariance,amp+ampVariance);
var nextX = nextAmp * Math.sin(progress * 2 * Math.PI / period) + wcx;
var nextY = nextAmp * Math.cos(progress * 2 * Math.PI / period) + wcy;
//NEW CODE:
root.px = nextX
root.py = nextY
force.resume();
//END NEW CODE
idleAF = requestAnimationFrame(setRandomPosition);
}
Here is the code in its entirety:
var idleAF; //the request animation frame for the idle animation
var idleTime; //the idle timestamp
var wcx = window.width/2;
var wcy = window.height/2;
var amp = 200; //base amplitude of the oscillator
var ampVariance = 50; //variance in amplitude for each animation frame
var setRandomPosition = function(timestamp){
if (!idleTime){
idleTime = timestamp;
}
var progress = timestamp - idleTime;
var nextAmp = randomNumber(amp-ampVariance,amp+ampVariance);
var nextX = nextAmp * Math.sin(progress * 2 * Math.PI / period) + wcx;
var nextY = nextAmp * Math.cos(progress * 2 * Math.PI / period) + wcy;
root.px = nextX
root.py = nextY
force.resume();
idleAF = requestAnimationFrame(setRandomPosition);
}
randomNumber = function(min,max){
return Math.floor(Math.random()*(max-min+1)+min);
}