It's trickier than it seems if you want to do the whole thing from scratch.
//first make some shortcuts
//choose the property to drive it - could be any property on any layer
myproperty=transform.position
//choose the keys that define the movement - in AE key indexes start at 1, not 0
key1=myproperty.key(1);
key2=myproperty.key(2);
//more shortcuts
pos1=key1.value
pos2=key2.value
//period is the length of the cycle
period=key2.time-key1.time
//phase is the point in the cycle we're at
phase= Math.abs(Math.abs(time - key1.time) % (period*2) -period);
//avoid divide-by-zero errors
if ( period == 0 ) { //this shouldn't happen
pos1;
} else {
(pos1*(Math.abs(period-phase))+pos2* phase )/period;
}
Now what we're doing with phase
is creating a value that goes from 0 to period and back again, and then making a weighted average of the two keyframes' values. It's easier to understand if you say there are 4 frames in the cycle and the first keyframe is at zero.
- At frame 1, phase is abs(abs(4-1) % 8 - 4) = 1 (the % is the modulo
operator, aka the remainder). When we substitute it into the last expression, the result is
(pos1 * (4-1) +pos2 * 1)/4
or to expand it 3/4*pos1 + 1/4*pos2
- so basically pos1
is influencing the position 3 times as much as pos2
.
- At frame2:
phase
=2
and it works out to 2/4*pos1 + 2/4*pos2
, so half and half, so it will be the average of the two keyframes.
- At frame 3
phase
=3, so it's 1/4*pos1 + 3/4*pos2
The second expression is basically doing the job of the built-in linear() interpolation method. Once time > period, then the abs() function kicks in and phase heads back to the original value.
You could also use the phase variable with the valueAtTime property of the position property: transform.position.valueAtTime(phase)
, if you wanted a complex motion path. that way it would shuttle backward and forward along the motion path between the two keyframes.
If you don't want no steenkin' keyframes the first few lines could be replaced thus:
pos1=[123,456]; //or for a 3d layer [123,456,789]
pos2=[987,654];
period=123;
startTime=12;
phase = Math.abs(Math.abs(time - startTime) % (period*2) -period);
The benefit of using keyframes is that you couple the power of expressions with the ease of the GUI.
I can see the value in this as a learning exercise (and a couple of glasses of Islay single malt this evening made it extra challenging), but one has to ask: why not use the built-in loopOut() expression? It probably will run much faster.
while(time < 50) { [value[0]+1,value[1]+2] ) }