How can one clear all the keyframes in an After Effects script? - javascript

I am writing a script for After Effects where as one of the steps I need to clear all the keyframes in . I currently have
for (highestIndex = prop.numKeys; highestIndex > 0; highestIndex--) {
prop.removeKey(highestIndex);
}
which works fine, except that it takes a few (very noticeable) seconds to run. In the GUI, there is the little stopwatch next to the property that can clear all the keyframes quickly. The property isTimeVarying (bool indicating if there are any keyframes) is read only, and I can't seem to find a setTimeVarying or similar method. Is there a way to do said thing?

You can do it using menu commands, but you've got to be very careful about what is selected and what is not, and be very sure that the comp is open in a viewer, and the viewer is active. For that, you'll need at least CS6.
function removeAllKeys(props){
var deselectAllId = app.findMenuCommandId("Deselect All");
var clearId = app.findMenuCommandId("Clear");
var comp, oldSelection, i;
// assumed: all props belong to the same comp
if (props.length===0) return;
comp = props[0].propertyGroup(props[0].propertyDepth).containingComp;
oldSelection = comp.selectedProperties;
app.beginUndoGroup("Remove All Keys");
// make sure that the comp is open in a viewer (essential, otherwise: catastrophy)
comp.openInViewer();
// deselect everything:
app.executeCommand(deselectAllId);
for (i=0; i<props.length; i++){
if (props[i].numKeys>0){
props[i].selected = true;
app.executeCommand(clearId);
app.executeCommand(deselectAllId);
};
};
for (i=0; i<oldSelection.length; i++){
oldSelection[i].selected = true;
};
app.endUndoGroup();
return;
};
removeAllKeys(app.project.activeItem.selectedProperties);

Give this a try:
var targetComp = app.project.activeItem; // Collect the active composition
var selectedLayer = targetComp.selectedLayers; // Collect the selected layers
// Identify the target parameter to be deleted
var targetParam = selectedLayer[0].transform.position; // Target the Position paramter of the first selected layer
// A. Delete the Keyframes forward from FIRST frame to LAST frame
while (targetParam.numKeys != 0) { // While there are still Keyframes, continue looping
targetParam.removeKey(1); // Delete the first Keyframe
}
// B. Delete the Keyframes backward from LAST frame to FIRST frame
for (i = targetParam.numKeys; i != 0; i--) { // Cycle through the Keyframes
targetParam.removeKey(i); // Remove the current Keyframe
}
You only need either A or B depending on whether you want to cycle forward and end up with the value of the last keyframe or cycle backward and end up with the value of the first keyframe.

As it says in the After Effects Scripting Guide page 140 :
To remove more than one keyframe, you
must start with the highest index number and work down to the lowest to ensure that the remaining indices
reference the same keyframe after each removal.
So you can't delete more than one keyframe at a time which is really a shame, but if you are looking for a fastest way, I searched and I found nothing so I think there is no better way..
But here is what you can do, you can't delete the keyframes but you can make the animation not change during all the time, you just have to add the expression valueAtTime(0)
to your property like this :
yourProperty.expression = "valueAtTime(0)";
See the After Effects Scripting Guide page 129.
I hope it helped you :)

Related

Why is my Conway's Game of Life acting strangely?

I am trying to replicate Conway's Game of Life and though my code seems to be kosher it appears not to behave like it should.
This is the basic meat of it:
if (pressingSpace) {
running = true;
} else {
running = false;
};
if (running) {
for (var i=0; i<tiles.length; i++) {
var tile = tiles[i];
if (tile.alive && (tile.neighborsAlive() < 2 || tile.neighborsAlive() > 3)) {
tile.die();
};
if (!tile.alive && tile.neighborsAlive() == 3) {
tile.comeToLife();
};
};
};
for (var i=0; i<tiles.length; i++) {
var key = tiles[i];
if (!key.alive && mouseDown && key.sprite.contains([cursorX,cursorY]) && cursorX != null) {
key.comeToLife();
}
};
All the functions in play have been thoroughly tested and seem to work as expected. But when this is run, the "alive" squares seem to overrun the screen much more easily than they should. In particular when a column is made three tiles high, it vanishes on the next frame when it should, per the rules, produce two "alive" tiles on either side at the same time.
I suspect this has something to do with the order of operations. Do I need to "mark" appropriate tiles for a state change, instead of change them on the spot? I know there are a lot of implementations of Conway out there but I'm trying to develop this on my own-ish. That said, any nudges in the right direction would be greatly appreciated. Thanks!
You can see it in action here: http://www.eggborne.com/tiles
As far as I can see, it's because of you change tilemap while iterating over it.
E.g. I assume .comeToLife() method changes .alive field to true, and if .neighborsAlive() return non-cached value, but calculates .alive tiles around, you are essentially changing your playfield while iterating, and newly changed cells ruin the whole picture.
Easiest solution will be to create 'old' and 'new' tilemaps, and iterate over 'old' one, while bringing changes to 'new' only. Caching 'neighborsAlive' is essentially creating two arrays, just in different way — that way you're also creating a tilemap, with each tile holding a value of how many neighbors are alive at this moment — and you have to determine this value before everything else changes. If you don't, you will have the same issue, as you currently have.
For demonstration of your problem, make your playfield update with each tile change — you will see your problem animated :)
Hope this helps your issue.

Javascript animation with recursion, strange behavior

Im trying to do code in javascript a ruzzle solver. For now it just dig through the maze and find every possible path ( in the future I will match them against a dictionary to find the real valid words in it)
You can see it here : http://178.239.177.105/ruzzle/
I wanted to do it with an animation that show how the algorithm works on it, but im issuing a problem.
If you load it, the page just dont show anything, and my browser crash after a while.
BUT...
if you set an alert("") function, somewhere in the middle of the recursion function, you would be able to go through any step in the algorithm.
Especially if you set the browser to prevent to show any further alert messages, you'll finally see the animation working on the maze.
I was actually trying to do this via setInterval(), but is not working.
So I have two questions:
- Why do the script cause the page to crash, or not if there's an alert?
- How can I properly show the animation using some kind on wait() mechanism?
Thanks
You can see all the code by going on the page and look at the source code, however for the sake of clarity I'll paste the relevant code here:
You can also play with the code here : http://jsfiddle.net/Gcw2U/
(you will have to uncomment the last line in the to make it run)
//this matrix of chars rapresent the 4x4 puzzle
var ruzle_model = [["w","a","l","k"],["m","o","o","n"],["h","a","t","e"],["r","o","p","e"]];
// ""offsets" rapresent the four motion vector(up,down,left,right)
// used to visit the matrix
var offsets = [[1,0],[0,1],[-1,0],[0,-1]];
//recursive function to dig the maze
function path(m,i,j,paths,checkeds){
alert("SET BROWSER TO AVOID NEXT ALERTS MSGs!");
//base case, if not hitting a wall or already checked cell
if ( ! (i<=3 && i>=0 && j>=0 && j<=3) || isChecked(checkeds,i,j)){
terminal.innerHTML = terminal.innerHTML + "-"+ paths;
uncheckAllCells();
return paths;
}
//call path for every direction (up,down,left,right) stored in offsets
var tmp = [];
for (var c=0; c<offsets.length;++c){
var offset = offsets[c];
checkCells(i,j);
checkeds.push(new Array(i,j));
tmp.push(path(m,i+offset[0],j+offset[1],paths + m[i][j],copy(checkeds)));
}
return tmp;
}
//call path on every cell in the maze
function ruzzle(r){
var sol = []
for(var i=0; i<4; ++i){
for(var j=0; j<4; ++j){
var checkeds = new Array();
sol.push(path(r,i,j,'',checkeds));
}
}
terminal.innerHTML = sol;
return sol;
}
Javascript loops and recursions inhibit rendering of the page, so any changes made will stay invisible until the script stops executing, like when you spawn an alert. When a user sets "do not show alert messages", the alert still yields execution time to the underlying eventloop, which will update the page.
For as-fast-as-possible (high fps) animations, use requestAnimationFrame().
In your case, setTimeout() is the best way to go. Set a timeout on the recursive call to path.
function recursive(args) {
// do stuff to args
setTimeout(function () {
recursive(args);
}, 5);
}
Example

Multiple simultanous animations, simulation of 'animate' easing and one 'step' call for a jQuery collection

Here is the problem, I've got a tree structure of html blocks, global container is of a fixed width(X) and height(Y). When i click one of the blocks on a level, all other blocks shrink to some size, while the clicked one gets enlarged to the leftover space, and the sublevels show up on it's place.
For all the shrinking i'm using default animate function with easing effect, when shrinking 1 level, to avoid enlargement bugs i have to do something like this:
$tabs.not($obj).animate({height:32<<$obj.getVerUp().length+"px"},{duration:300,
step:function() {
$obj.height(function(){
var sum = 0;
$tabs.not($obj).each(function(){
sum += $(this).height();
});
return $obj.getCont().height()-sum+"px";
});
}
});
$tabs are all the tabs of current level, $obj - is the one tab that i want to enlarge
The main problem is:
When i open up a tab that is on a deep level, i have to animate all the tabs of higher levels to shrink a little bit more, thus the $obj X and Y would change, so the current animation has to use new values, but if i call 3 different animations on different levels i'm bound to get a bug, when one of the animations on a deeper level finishes 1 step earlier, while the one on the level above, would enlarge the object by 5-10 more pixels and that space wouldn't be used up.
The second problem is that there has to be about 50 object animating with easing at the same time, which is a little bit overkill.
And the last problem is when i call step callback on animation as shown above, i have a strange feeling that it calls the step separately for each animation of the $tabs collection, while i need 1 step for all the tabs in the list (to avoid unnecessary scripts)
There might be some other way to fix all that, but i have yet to discover all jQuery functions, so from what i see the only way is to simulate easing, and do everything in one single animation.
I don't really want to use setInterval and determining when do i need to clear it plus calculating each of the easing values, if there is a simple way doing it.
Does jQuery has some sort of empty animation easing, e.g.
$().css("height":starth+"px").animate({height:endh},{duration:300,
step:function(fn) {
// all the animation actions here via fn end value
}
});
Thanks in advance.
What I need - is not a completely working solution in code, just some enlightenment in those subjects:
Is there a legal way to call one step function for a collection of animated elements, or, maybe, it does call step once when I use one .animate on collection.
I'd be really appreciated if someone would shed some light over how does jquery handle multiple .animate, would they be used in one global function that works on .setInterval? or would they be having massive number of those .setIntervals that are equivalent to setTimeout (which most browsers can't handle in large amounts);
Is there a way to simulate 'animate' easing, some function name maybe, or a special trick to achieve that (the only thing I see is a hidden element or 'window' property to change maybe)
Or some directed pushes with functions I should study, that could help me achieve my goals
Guess i pretty much found the answer to my questions:
http://james.padolsey.com/javascript/fun-with-jquerys-animate/
Here's the empty animation from the link above with 1 step function with desired values, going to post the result later on if it all works out.
var from = {property: 0};
var to = {property: 100};
jQuery(from).animate(to, {
duration: 100,
step: function() {
console.log( 'Currently # ' + this.property );
}
});
Yes it all worked great, no desynch, and a good speed, since only 1 animate, found making one universal function for the animation - waste of resourses, so it is pretty specific, but still, here it is:
animate: function($obj) {
var T = this;
...
T.arr = new Array();
// gathering the array
$obj.each(function(i){
var size;
T.arr[i] = {obj:$(this), rest:$(this).getSibl(), cont:$(this).getCont()}
if($(this).hasClass("vert"))
{
size = "height";
T.arr[i].to = yto;
}
else
{
size = "width";
T.arr[i].to = xto;
T.arr[i].children = $(this).getChld();
}
T.arr[i].rest.each(function(){
$(this).attr("from",$(this)[size]());
});
});
// animating prop
jQuery({prop:0}).animate({prop:1}, {
duration: 300,
step: function() {
var i;
var P = this;
var newval;
var sum;
var size;
for(i = 0; i < T.arr.length; i++)
{
size = T.arr[i].obj.hasClass("vert") ? "height":"width";
sum = 0;
T.arr[i].rest.each(function(){
// new value of width/height, determined by the animation percentage
newval = parseInt($(this).attr("from")) + (T.arr[i].to-$(this).attr("from"))*P.prop;
$(this)[size](newval);
sum += newval;
});
T.arr[i].obj[size](T.arr[i].cont[size]()-sum);
}
}
});
},

If elements are touching, split their width in half?

I'm working on a Javascript/jQuery calendar which includes a month view and a day view. Clicking the days will change the date, which will update the date variables in the day view.
The day view is split up into half hour segments from midnight to 11:00 PM. Clicking on any half hour <tr> (the day view is a table) will create an event between that time clicked and an hour in the future, as well as append a div on top of the calendar, spanning the range of time and positioned at the correct starting point (each pixel is a minute...)
There is a problem, however. If you create an "event" between a certain time span where there is already one in place, they overlap. This is the default behavior, obviously, but what I would like to happen is that if an event is created between a range of dates that is already occupied by an event, they align side by side so that they're not overlapping.
This resembles the behavior seen in the iCal app for mac:
Now my first thought to achieve such a goal was to use collision detection, but all the jQuery plugins for this are bloated or require the elements to be draggable.
Then I thought there might be a way in CSS to do this, where if two elements are overlapping, they split the width evenly.
Then I thought that's ridiculously far fetched, so I'm wondering how I can achieve this as easily as possible.
I'll post the full code in a jsFiddle, but for the most important function would be insertEvent which looks like this:
function insertEvent(start, end){
var end_minutes = new Date(end).getMinutes();
var end_border = new Date(new Date(end).setMinutes(end_minutes + 2));
//$(".day_date").html(start + "<br />" + end);
var diff = Math.abs(end_border - new Date(start));
var minutes = Math.floor((diff/1000)/60);
var start_element = $("td").find("[data-date='" + start + "']");
var offset = start_element.offset().top - $(".second").offset().top;
var this_element = $("<div class='event' style='height:" + minutes + "px;margin-top:" + offset + "px;'></div>");
$(".right").prepend(this_element);
}
This takes two parameters in the javascript new Date() format, one for the start date and one for the end date.
The fiddle: http://jsfiddle.net/charlescarver/HwdwL/
One of the the problems I see with your approach is that there isn't a structure to the storage of the data. I've built a calendar in Javascript before and it's not easy work. First, make sure you have some kind of abstraction for the calendar event. Something like:
function CalendarEvent(startDateTime, endDateTime) {
this.startDateTime = startDateTime;
this.endDateTime = endDateTime;
}
CalendarEvent.prototype.start = function() {
return this.startDateTime.getTime();
};
CalendarEvent.prototype.end = function() {
return this.endDateTime.getTime();
};
CalendarEvent.new = function(startDateTime, endDateTime) {
// This is a little factory method. It prevents calendar events
// from having end times that fall before the start time.
// USE THIS TO INSTANTIATE A NEW CALENDAR EVENT
if(endDateTime.getTime() < startDateTime.getTime()) {
throw new Error("End time falls before start time");
}
return new CalendarEvent(startDateTime, endDateTime);
};
CalendarEvent.compare = function(eventOne, eventTwo) {
// this is a class method to compare two events
// If used with sort it will sort by startDateTime
return eventOne.start() - eventTwo.start();
};
// ... add any other methods you need
Next you're going to want to sort the calendar events. I would sort by start time. Then once it is sorted you can actually re-render everything when changes are made. As long as you sort correctly, determining if a calendar event collides is as simple as this:
CalendarEvent.prototype.intersects = function(otherEvent) {
// If the other event starts after this one ends
// then they don't intersect
if(otherEvent.start() > this.end()) {
return false;
}
// If the other event ends before this one starts
// then they don't intersect
if(otherEvent.end() < this.start()) {
return false;
}
// Everything else is true
return true;
};
Because the data is sorted you know that if two or more calendar events intersect they will have to share the space. Granted, you must think about a few things when you divide the space. Do you want a naive implementation where you just share the space equally from left to right (left having the earliest start time). If so your visual representation could look like this if it had 4 events that shared a space (each block is an event):
However if your events have strange shapes they might cause your calendar to look strange. Consider the following:
In this instance event 2 takes up a lot of vertical space and all the space underneath event 1 is unused. Maybe for a better UX you don't want that kind of thing to happen. If so you should design your rendering algorithm accordingly. Just remember that it is probably easiest to re-render on every change that you encounter, but it's all about how you store the data. If you do not store the data in some kind of structure that is easily traversed then you won't be able to do this kind of thing.
But to complete the answer to your question, here is a fairly naive example. I haven't tested it so this is a pretty big assumption of it working. It is not entirely complete you will have to edit the rendering for yourself. This is merely to give you an idea of how to get it to work. It could definitely look prettier:
function renderCalendarEvents(calendarEvents) {
// Sort the calendar events (assuming calendarEvents is an array)
var sortedEvents = calendarEvents.sort(CalendarEvent.compare);
var index = 0;
// renderEvents is an anonymous function that will be called every time
// you need to render an event
// it returns it's columnDivisor.
var renderEvent = function(position) {
var currentEvent = sortedEvents[index];
var nextEvent = sortedEvents[index + 1];
// The default column divisor is determined by
// the current x-position + 1
var columnDivisor = position + 1;
// Increment before any recursion
index += 1;
// Check if nextEvent even exists
if(nextEvent) {
// If the nextEvent intersects with the current event
// then recurse
if(currentEvent.intersects(nextEvent)) {
// We need to tell the next event that it starts at the
// column position that is immediately +1 to the current event
columnDivisor = renderEvent(position + 1);
}
}
// placeEvent() is some function you can call to actually place
// the calendar event element on the page
// The position is the x-position of the current event
// The columnDivisor is a count of the amount of events sharing this column
placeEvent(currentEvent, position, columnDivisor);
return columnDivisor;
};
while(true) {
// render events until we're done
renderEvent(0);
if(index >= sortedEvents.length) {
break;
}
}
}
Essentially the idea with this particular algorithm is that if the nextEvent on the list exists and that event intersects with the currentEvent then we need to split the width of the currentEvent. It keeps on recursing until it finds no more intersections then it makes it's way back up the chain of recursive calls. I skipped the actual DOM manipulation logic because really the hard part is determining how much you need to split the actual column in order to get these events to fit. So hopefully this all makes a little bit of sense.
EDIT:
To be much more clear, in order to add this to your existing code I would replace your insertEvent function with something like this. I don't write all of the logic for you so you'll have to do some of your own writing. But that's half the fun :-).
function insertEvent(start, end) {
var newEvent = Calendar.new(start, end);
// you'll have to store the array somewhere.
// i'm just assuming some kind of global right now
eventsArray.push(newEvent);
// You'll want to destroy any event elements
destroyCurrentEventElements();
// Now run the rendering function
renderCalendarEvents(eventsArray);
}

Why does my my gravity work in this?

http://davzy.com/gameA/
I can't figure out a smart way to get gravity. Now with this it detects which block the character is over but it does't drop to that block!
Is there a better way to do gravity? I'd like to do this without a game library.
I don't know what you mean by "get gravity"; your question is unclear. I assume that if you can detect when the block is over, you can use the following formula:
s(t) = ut + 1/2at2
Where s is the distance at time t, u is the initial velocity (which in your case would be zero), and a is the acceleration (on Earth this is 9.8m/s2). Essentially you would be adjusting the top position of your object based on the value you get at time t (so original top position of object + s(t)). I would imagine you would use some sort of animation loop. Perhaps a setInterval. Maybe others with more experience in Javascript animation can chime in about the best way to implement this. However, this would be the formula that you would be using to figure out where the object is at time t, if it falls.
Basically gravity in a platformer goes like this:
var currentGrav = 0.0;
var gravAdd = 0.5; // add this every iteration of the game loop to currentGrav
var maxGrav = 4.0; // this caps currentGrav
var charPosY = getCharPosY(); // vertical position of the character, in this case the lower end
var colPosY = getColDow(); // some way to get the vertical position of the next "collision"
for(var i = 0; i < Math.abs(Math.ceil(currentGrav)); i++) { // make sure we have "full pixel" values
if (charPosY == colPosY) {
onGround = true;
break; // we hit the ground
}
onGround = false;
charPosY++;
}
Now to jump one could simply do this:
if (jumpKeyPressed && onGround) {
currentGrav = -5.0; //
}
You can, if you want(and understand C), check out my game for a basic platformer(with moving platforms) here:
http://github.com/BonsaiDen/Norum/blob/master/sources/character.c

Categories

Resources