I'm trying to record a user's mouse and key inputs, then "play them back" to the user by triggering events at chronologically the same time they occurred. That sentence is a little confusing so I'll explain my code:
var $dancerContainer = $('.dancerContainer');
var count = 3;
var captured;
var countInterval;
$dancerContainer is the element I want to animate, which is a div.
count is the duration of the 'recording' phase.
captured will eventually be an object that holds the events, keyed by the elapsed time in milliseconds they occurred since the start of the recording.
function captureInput() {
var mouseCapture = [];
var keyCapture = [];
var start = new Date().getTime();
$(document).on('mousemove.capture', function(event) {
event.t = new Date().getTime() - start;
mouseCapture.push(event);
});
$(document).on('keyup.capture', function(event) {
event.t = new Date().getTime() - start;
keyCapture.push(event);
});
setTimeout(function() {
$(document).off('.capture');
captured = chronoCaptures(mouseCapture, keyCapture);
}, 3000);
}
The captureInput func tags an elapsed time on the event before pushing them to mouse and keyup arrays, mouseCapture and keyCapture, then unbinds the listeners.
function chronoCaptures(mouse, keyboard) {
var greater = (mouse.length > keyboard.length) ? mouse.length : keyboard.length;
var chrono = {};
var j = 0;
var k = 0;
for (var i = 0; i < greater; i++) {
if (keyboard[k] == undefined) {
chrono[mouse[j].t] = mouse[j];
j++;
} else if (mouse[j] == undefined) {
chrono[keyboard[k].t] = keyboard[k];
k++;
} else {
if (mouse[j].t < keyboard[k].t) {
chrono[mouse[j].t] = mouse[j];
j++;
} else {
chrono[keyboard[k].t] = keyboard[k];
k++;
}
}
}
return chrono;
}
chronoOrderCaptures then takes the two arrays of events and returns an object whose keys are the times each event occurred. Now that I look at this code again, since I'm putting the events into an object anyway, it doesn't matter what order they get put in. I might overwrite an event with another, in which case I want the key event to take precedence (going to refactor this, but this is beside the point).
function replayDance(captured, duration) {
var d = duration * 1000;
var start = new Date().getTime();
var elapsed = 0;
while (elapsed <= d) {
elapsed = new Date().getTime() - start;
var c = captured[elapsed];
if (c) {
$dancerContainer.trigger(c)
}
}
}
Finally, replayDance waits for the duration of the recording and checks how much time has elapsed. If the captured object contains an entry # that amount of elapsed time, I trigger the event on the document.
WHEW. Thank you if you've gotten this far. Now to the problem!! (what?) The problem I'm having is the mouse events all get played back 'at once'. There's no pausing occurring, I don't see them executed in sequence as if I was actually moving the mouse, although it seems like they ought to be getting triggered at roughly the same time they were recorded.
Finally here is the handler for mousemove events:
$(document).on('mousemove', followMouse);
function followMouse(event) {
var width = $(window).width();
var height = $(window).height();
var mouseX = event.pageX - (width * 0.25);
var mouseY = event.pageY - (height * 0.25);
var angleX = (mouseY / height) * 45;
var angleY = (mouseX / width) * 45;
dancer.style.webkitTransform = "rotateX(" + angleX + "deg) rotateY(" + angleY + "deg)";
}
Related
I want to use this code that i have seen on github, but I don't know how to apply this code on my HTML, to have an scrolling effect.
The point is, I don't know how to run use this code
source https://gist.github.com/andjosh/6764939
document.getElementsByTagName('button')[0].onclick = function () {
scrollTo(document.body, 0, 1250);
}
function scrollTo(element, to, duration) {
var start = element.scrollTop,
change = to - start,
currentTime = 0,
increment = 20;
var animateScroll = function(){
currentTime += increment;
var val = Math.easeInOutQuad(currentTime, start, change, duration);
element.scrollTop = val;
if(currentTime < duration) {
setTimeout(animateScroll, increment);
}
};
animateScroll();
}
//t = current time
//b = start value
//c = change in value
//d = duration
Math.easeInOutQuad = function (t, b, c, d) {
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
First you have to replace document.body with document.documentElement, as document.body.scrollTop() has been deprecated.
Edit: it seems that I was not completely right about document.body.scrollTop() being deprecated. The best solution to support multiple browsers is to check for both cases.
Second, you need to set a value > 0 for the 'to' parameter, as Quantastical already pointed out.
Also make sure you have a <button> element. It should work then.
I have a class that takes some coordinate and duration data. I want to use it to animate an svg. In more explicit terms, I want to use that data to change svg attributes over a time frame.
I'm using a step function and requestAnimationFrame outside the class:
function step(timestamp) {
if (!start) start = timestamp
var progress = timestamp - start;
var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
var moveX = distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
document.querySelector('#start').setAttribute('cx', currentX + moveX);
if (progress < circleMove.duration) {
window.requestAnimationFrame(step);
}
}
var circleMove = new SingleLineAnimation(3000, startXY, endXY)
var start = null
function runProgram() {
window.requestAnimationFrame(step);
}
I can make it a method, replacing the circleLine with this. That works fine for the first run through, but when it calls the this.step callback a second time, well, we're in a callback black hole and the reference to this is broken. Doing the old self = this won't work either, once we jump into the callback this is undefined(I'm not sure why). Here it is as a method:
step(timestamp) {
var self = this;
if (!start) start = timestamp
var progress = timestamp - start;
var currentX = parseInt(document.querySelector('#start').getAttribute('cx'));
var moveX = distancePerFrame(self.totalFrames(), self.xLine);
document.querySelector('#start').setAttribute('cx', currentX + moveX);
if (progress < self.duration) {
window.requestAnimationFrame(self.step);
}
}
Any ideas on how to keep the "wiring" inside the Object?
Here's the code that more or less works with the step function defined outside the class.
class SingleLineAnimation {
constructor(duration, startXY, endXY) {
this.duration = duration;
this.xLine = [ startXY[0], endXY[0] ];
this.yLine = [ startXY[1], endXY[1] ];
}
totalFrames(framerate = 60) { // Default to 60htz ie, 60 frames per second
return Math.floor(this.duration * framerate / 1000);
}
frame(progress) {
return this.totalFrames() - Math.floor((this.duration - progress) / 17 );
}
}
This will also be inserted into the Class, for now it's just a helper function:
function distancePerFrame(totalFrames, startEndPoints) {
return totalFrames > 0 ? Math.floor(Math.abs(startEndPoints[0] - startEndPoints[1]) / totalFrames) : 0;
}
And click a button to...
function runProgram() {
window.requestAnimationFrame(step);
}
You need to bind the requestAnimationFrame callback function to a context. The canonical way of doing this is like this:
window.requestAnimationFrame(this.step.bind(this))
but it's not ideal because you're repeatedly calling .bind and creating a new function reference over and over, once per frame.
If you had a locally scoped variable set to this.step.bind(this) you could pass that and avoid that continual rebinding.
An alternative is this:
function animate() {
var start = performance.now();
el = document.querySelector('#start');
// use var self = this if you need to refer to `this` inside `frame()`
function frame(timestamp) {
var progress = timestamp - start;
var currentX = parseInt(el.getAttribute('cx'));
var moveX = distancePerFrame(circleMove.totalFrames(), circleMove.xLine);
el.setAttribute('cx', currentX + moveX);
if (progress < circleMove.duration) {
window.requestAnimationFrame(frame);
}
}
window.requestAnimationFrame(frame);
}
i.e. you're setting up the initial state, and then doing the animation within a purely locally scoped function that's called pseudo-recursively by requestAnimationFrame.
NB: either version of the code will interact badly if you inadvertently call another function that initiates an animation at the same time.
i was trying to implement the A* algorithm and followed the wikipedia pseudo code to make this.
when i pass a predefined object pixel to the a funtion getG() it says that the object is null. I'm sorry if i am not pointing to a specific problem but i am not even sure how to really specify the problem by name. i have tried commenting out the code to increase readability.
git repository link of the whole project - https://github.com/NirobNabil/WhirlWind
(things are a little messy here because i didn't use github at first and i uploaded it just a little ago for posting the problem)
[ i'm actually making this to use a* to find path for my bot which is powered by arduino. thats why i'm using involt. ]
here goes the code,
$(function() {
// define the height, width and bot size in centemeter
total_width = 200;
total_height = 200;
bot_size = 20;
total_box = (total_height / bot_size) * (total_width / bot_size);
box_in_x = total_width / bot_size;
box_in_y = total_height / bot_size;
//populating the pixels array
populate(total_width / bot_size, total_height / bot_size, "UND");
pathfind(pixels, pixels[13], pixels[pixels.length - 1]);
})
var pixels = []; //an array to hold all the objects(the grid)
var obstacles = []; //array to hold the obstacles
function pixel(X, Y, obs) {
this.X_co_ordinate = X;
this.Y_co_ordinate = Y;
this.state = obs; //availale states OPN, UND, OBS, DIS, NULL
this.g = 0;
this.h = 0;
this.f = 0;
this.last = null;
} //every block in the grid is a pixel
//01719372596
function populate(height, width, obs_val = "UND") {
pixels[0] = new pixel(0, 10, obs_val);
for (h = height, i = 0; h >= 0; h--) {
for (w = 0; w < width; w++, i++) {
var temp_obs = new pixel(w, h, obs_val);
temp_obs.last = pixels[0];
pixels[i] = temp_obs; //saving temp_pixel object to pixels array
}
}
} //populating the grid AKA pixels with pixel objects or blocks
// this funtion is where the problem shows up
function getG(current, start) {
let g = 1;
while (current != start && current.last != start && current) {
current = current.last;
g++;
}
return g;
} //get the g val(cost to come to this pixel from the start) of the current pixel
function getH(current, end) {
let I = Math.abs(current.X_co_ordinate - end.X_co_ordinate) + Math.abs(current.Y_co_ordinate - end.Y_co_ordinate);
return I;
} //get the h val(heuristic) of the current pixel
function getF(start, current, end) {
let G = getG(current, start);
let H = getH(current, end);
return G + H;
} //get the f val(total) of the current pixel
function lowFinArray(arr, start, end) {
// here arr is the grid/pixel
let current_low = arr[0];
for (let i = 0; i < arr.length; i++) {
let getF1 = getF(start, current_low, end);
let getF2 = getF(start, arr[i], end);
if (getF1 < getF2) {
current_low = arr[i];
}
}
console.log("current low");
console.log(current_low);
return current_low;
}
function getneighbours(grid, current) {
let neightbours = [];
neightbours.push(grid[getIndex(current.X_co_ordinate - 1, current.Y_co_ordinate)]);
neightbours.push(grid[getIndex(current.X_co_ordinate + 1, current.Y_co_ordinate)]);
neightbours.push(grid[getIndex(current.X_co_ordinate, current.Y_co_ordinate - 1)]);
neightbours.push(grid[getIndex(current.X_co_ordinate, current.Y_co_ordinate + 1)]);
/*
for(i=0; i<neightbours.length; i++){
neightbours[i].last = current;
}*/
console.log("neightbours");
console.log(neightbours);
return neightbours;
} //get the neighbour pixels of the current pixel
//main algo
function pathfind(grid, start, end) {
let closedSet = [];
let openSet = [];
openSet.push(start);
let current = start;
//trying to debug
console.log("low F in arr");
console.log(lowFinArray(grid, start, end));
console.log(start);
console.log(current);
console.log(end);
console.log(grid);
let x = 0;
while (openSet.length > 0) {
//trying to debug
console.log("executed " + (x++));
console.log("openset");
console.log(openSet);
current = lowFinArray(grid, start, end); //assigning the pixel with lowest f val to current
console.log("current");
console.log(current);
if (current == end) {
console.log(getPath(current));
}
let neighbours = getneighbours(grid, current);
for (let i = 0; i < neighbours.length; i++) {
let neighbour = neighbours[i];
if (closedSet.includes(neighbour)) {
continue;
}
if (!openSet.includes(neighbours)) {
openSet.push(neighbours);
}
//console.log(current);
let getg = getG(current, start);
let geth = getH(current, end);
//console.log(getg);
let tGscore = getg + geth; //here getH is being used as a distance funtion
if (tGscore >= getg) {
continue;
}
neighbour.last = current;
neighbour.g = tGscore;
neighbour.f = getF(neighbour);
}
if (x > 10) {
return 0;
}; //the loop was running forever so i tried this to stop the loop after 10 iterations
}
}
function getPath(current) {
let path = [current];
while (current.last != null) {
path.push(current.last);
}
return path;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
and here is what the console says,
Uncaught TypeError: Cannot read property 'last' of null
at getG (app.js:226)
at getF (app.js:241)
at lowFinArray (app.js:249)
at pathfind (app.js:292)
at HTMLDocument.<anonymous> (app.js:92)
at mightThrow (jquery-3.1.1.js:3570)
at process (jquery-3.1.1.js:3638)
You're doing your checks in the wrong order:
while (current != start && current.last != start && current) {
There's no point in using && current after you've already used current.last.
Perhaps changing the order would solve the problem. It will at least get rid of your current error:
while (current && current != start && current.last != start) {
Regarding the title of this question:
In javascript, after i pass a non null object to a funtion it says the object is null
It may very well be non-null 100% of the time you pass it into the function, but you are repeatedly overwriting its parameter within the function, so all bets are off.
For the sake of learning I am prototyping an animate function for all HTMLElements, inspired by jQuery. The animation starts up just fine, but I want it to stop after the requestAnimationFrame's time = the duration given in the function. I am using cancelAnimationFrame inside the animation loop, but it doesn't stop the loop.
HTMLElement.prototype.animate = function(properties,duration){
for(prop in properties){
var last = 0,
fps = 60;
function ani(time){
requestAnimationFrame(ani);
if ((time - last) > fps ){
last = time
console.log(time)
if(time >= (duration*1000)){
window.cancelAnimationFrame(aniLoop)
}
}
}
var aniLoop = requestAnimationFrame(ani)
}
}
The function is called like this
c.animate({"property":"value"},1)
At the core of the problem lies that fact that you're only getting the ID of the first animation frame (the var aniLoop = (...) line) and that's what you're trying to cancel - except that each call to requestAnimationFrame has a different ID, thus you'd need to store the return value of the last call and cancel that instead:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
prop,
last = 0,
fps = 60;
for (prop in properties) {
function ani(time) {
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
console.log(time);
if (time >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
aniLoop = requestAnimationFrame(ani);
}
};
There are, however, several other problems with your code that need to be tackled as well, otherwise your function will blow up rather thoroughly:
:1 Function declaration in a loop
I would recommend reading about differences between function declaration and expression to get a better picture, but the problem here is that you're doing function declaration in a loop, which is considered undefined behaviour (some engines will replace the functions, some will not, some will blow up). Given that the animations have only single duration given and thus, are probably synchronised, it'd be a better option to iterate over properties to animate inside of a single animation function, like so:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
last = 0,
fps = 60;
function ani(time) {
var prop;
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
for (prop in properties) {
console.log(prop + ': ' + time);
}
if (time >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
aniLoop = requestAnimationFrame(ani);
}
:2 Animation timestamping
As it looks currently, your animation function will probably not run more than one frame anyway - if you look at requestAnimationFrame documentation on MDN, you'll notice that the callback given to requestAnimationFrame is given a timestamp, i.e. value in milliseconds from the beginning of UNIX epoch (1st of January 1970) - therefore the condition of time >= (duration * 1000) will always be true. Instead of that, register the starting time when you kick the animation off and compare the timestamp within the callback to it, like so:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
start,
last = 0,
fps = 60;
function ani(time) {
var prop,
progress;
aniLoop = requestAnimationFrame(ani);
if ((time - last) > fps) {
last = time;
progress = time - start;
for (prop in properties) {
console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
}
// This is where we get a difference between current and starting time
if (progress >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
}
start = Date.now();
aniLoop = requestAnimationFrame(ani);
}
:3 Animation throttling
This one is not as crucial, but still worth a consideration - requestAnimationFrame is intended to be automatically throttled and regulated by the browser, thus you don't need to apply your own conditions on whether animation should run (it won't go over 60FPS anyway, as that's the specification's ceiling). Instead, it should simply work on difference of current time from starting time, to make sure your animation still ends up in correct place even if for some reason, there is a lag in animation:
HTMLElement.prototype.animate = function(properties,duration) {
"use strict";
var aniLoop,
start;
function ani(time) {
var prop,
progress;
aniLoop = requestAnimationFrame(ani);
progress = time - start;
for (prop in properties) {
console.log(prop + ': ' + progress + ' out of ' + (duration * 1000));
}
// This is where we get a difference between current and starting time
if (progress >= (duration * 1000)) {
cancelAnimationFrame(aniLoop);
}
}
start = Date.now();
aniLoop = requestAnimationFrame(ani);
}
I am trying to simulate shaking by changing the position of img tags within a div.
I have it working for one img at a time.
Is there a way to change the style of all the img tags within a div at once?
This is what I am currently doing:
bird = document.createElement('img');
bird.setAttribute('src',birdie);
bird.setAttribute('class', 'birdie');
bird.setAttribute('id', id);
bird.setAttribute('onLoad', 'shakeAll()');
map.appendChild(bird);
birds++;
if(birdmap[0] == 0){
birdmap = [id];
}else{
birdmap+=[,id];
}
this ShakeAll function is also at onLoad of body:
function shakeAll(){
if (birdmap[0] == 0) return;
i = 1;
while(i <= birds){
shakeIt(birdmap[i]);
i++;
}
setTimeout("shakeAll()",initialSpeed);
}
Note: the imgs are absolute
function shakeIt(id){
shake = document.getElementById(id);
j=1;
while(j<4){
if (j==1){
shake.style.top=parseInt(shake.style.top)+distance+"px";
}
else if (j==2){
shake.style.left=parseInt(shake.style.left)+distance+"px";
}
else if (j==3){
shake.style.top=parseInt(shake.style.top)-distance+"px";
}
else{
shake.style.left=parseInt(shake.style.left)-distance+"px";
}
j++;
}
//setTimeout("shakeIt(id)", 50);
}
I couldn't really tell what you were trying to do in your code. Here's some code that shows the basics of moving some images back and forth:
// quadratic easing in/out - acceleration until halfway, then deceleration
// t = time into the animation
// d = duration of the total animation
// b = base value
// c = max change from base value (range)
var easeInOutQuad = function (t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
};
var linearTween = function (t, b, c, d) {
return c*t/d + b;
};
// cubic easing in/out - acceleration until halfway, then deceleration
var easeInOutCubic = function (t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
};
function shakeAll() {
var container = document.getElementById("container");
var imgs = container.getElementsByTagName("img");
// save away original position
for (var i = 0; i < imgs.length; i++) {
imgs[i].basePos = parseInt(imgs[i].style.left, 10);
}
var numShakes = 0;
var maxShakes = 10;
var range = 100;
var direction = 1;
var duration = 300; // ms
var startTime = (new Date()).getTime();
var deltas = [];
function shakeImgs() {
var now = (new Date()).getTime();
var elapsed = Math.min(now - startTime, duration);
var delta = Math.round(easeInOutQuad(elapsed, 0, range, duration));
delta *= direction;
for (var i = 0; i < imgs.length; i++) {
var basePos = imgs[i].basePos;
if (direction < 0) {
basePos += range;
}
imgs[i].style.left = (basePos + delta) + "px";
}
if (now - startTime >= duration) {
startTime = now;
direction *= -1;
++numShakes;
}
if (numShakes < maxShakes) {
setTimeout(shakeImgs, 10);
}
}
shakeImgs();
}
Working demo and HTML shown here: http://jsfiddle.net/jfriend00/ED5yA/
And, here's a fun one that adds some random shakiness (jitter) to the animation: http://jsfiddle.net/jfriend00/jM8jx/.
The basics of obtaining the list of images to operate on is this part:
var container = document.getElementById("container");
var imgs = container.getElementsByTagName("img");
This gets the container object and then gets all image objects in that container. You can see the corresponding HTML in the jsFiddle. This code implements a positioning scheme that slows the velocity down at the edges and goes the fastest in the middle of the range. The rest of the motion is controlled by the intial value of the variables declared in shakeAll(). These can be edited or can be changed to be passed into the function.
give a common class name to all imgs that you wanna shake. and then, user getElementsByClassName() instead of getElementById() to return an array of the elements which have the specific class name. then use a loop to animate each.
but if you want ALL imgs to be animated, use element.getElementsByTagName() or document.getElementsByTagName() instead.