Copy link to clipboard
Copied
Hi everyone,
I'm having trouble with an expression, and after trying multiple workarounds, I still can't get it to work. Maybe you have an idea or a hint?
Here's my setup:
The issue is that the position changes instantly, and I want to smooth the transition using ease or linear. But no matter what I try, I can't get it to work properly.
I've experimented with different approaches. This (below) method works for the first few seconds, but later in the timeline, the smooth transition stops working. I cant figure out to get the ease working with the right timing, the problem is (i guess) that the calculation for the ease time is wrong...but i dont know how to fix it...i also tried it like checking when the value changes and then easing and so on....nothing really worked
Any advice or solutions would be greatly appreciated!
Can someone give me a helping hand? 😄
Code:
// Create an array to store the text values and their corresponding layer indices
var textValues = [];
// Loop through all layers to get their text values
for (var i = 1; i <= thisComp.numLayers; i++) {
var layer = thisComp.layer(i);
// Check if the layer name matches the pattern "ItemXX"
if (layer.name.match(/^Item\d+$/)) {
var textValue = parseFloat(layer.effect("Value")(1).value);
if (!isNaN(textValue)) { // Ensure the text value is a number
textValues.push({value: textValue, index: i});
}
}
}
// Sort the array based on text values in descending order
textValues.sort(function(a, b) {
return b.value - a.value;
});
// Find the current layer's index in the sorted array
var currentIndex = textValues.findIndex(function(element) {
return element.index == index;
});
// Calculate the new Y position based on the sorted order
var baseY = 300; // Starting Y position
var spacing = 150; // Space between each text layer
var newY = baseY + currentIndex * spacing;
// Interpolate and ease between the current position and the new position
var currentPosition = value;
var transitionDuration = 1; // Duration of the transition in seconds
// Calculate the eased position
var t = (time - inPoint) / transitionDuration;
t = Math.min(Math.max(t, 0), 1); // Clamp t between 0 and 1
var easedY = ease(t, currentPosition[1], newY);
// Return the new position with easing
[currentPosition[0], easedY];
Adding ease to the functionality of your expression ups the complexity considerably because the expression now needs to know how long it's been since its rank has changed. In many cases, this info is not easily obtained. Your expression may have to go back in time, frame-by-frame to discover when its rank changed and subtract that time from the current time to calculate how far along it is in the ease operaion. You should only have to go back as far as transitionDuration because if it goes that
...Hey Dan thanks again for the hint and the helping hand!
This is my solution now, and it works 🙂 ... juhuuu ...
Took me quit a while!
One problem I was just figuring out at last, i was always working with "transform.position.valueAtTime(time)" so figure out what the position of the layer is right now. But it somehow always was the wrong postition value , so i had to calculated it with the last rank. Kind of simple but took me a while :D...
// Initialize variables
var textValues = [];
var rankChange
...
Copy link to clipboard
Copied
Adding ease to the functionality of your expression ups the complexity considerably because the expression now needs to know how long it's been since its rank has changed. In many cases, this info is not easily obtained. Your expression may have to go back in time, frame-by-frame to discover when its rank changed and subtract that time from the current time to calculate how far along it is in the ease operaion. You should only have to go back as far as transitionDuration because if it goes that far back and its rank hasn't changed, it can safely assume that it should be settled in at its current rank. There may be simplifications available, depending on how and when the sliders can change, but what I've described should work in the worst-case scenario. There could be additional complications though, if the layer's rank can change more than once during a single transitionDuration period.
Copy link to clipboard
Copied
Good Morning Dan,
Thanks so much for your reply and effort!
You’re absolutely right, and I’ll give your approach a try. I actually tried something similar before by checking the position one second back.
I’ll share my experiences once I’ve tested it out.
Do you happen to have a completely new approach as well? I also tried doing it without easing, just creating it myself, but the issue always comes down to calculating the time.
There’s no simple way to do something like this, right? Since the problem is with calculating the time:
if (oldPosition != newPosition) ease for one second until oldPosition == newPosition.
I’ll give it another shot and see how it goes.
Copy link to clipboard
Copied
Hey Dan thanks again for the hint and the helping hand!
This is my solution now, and it works 🙂 ... juhuuu ...
Took me quit a while!
One problem I was just figuring out at last, i was always working with "transform.position.valueAtTime(time)" so figure out what the position of the layer is right now. But it somehow always was the wrong postition value , so i had to calculated it with the last rank. Kind of simple but took me a while :D...
// Initialize variables
var textValues = [];
var rankChangeTime = 0;
var oldRank = 0; // Store the previous rank
var moverPos = thisComp.layer("MOVER").transform.position.value;
// Get transition duration from the "Controller" slider
var transitionDuration = thisComp.layer("Controller").effect("Transition Duration")(1);
// Gather all text values and their layer indices
for (var i = 1; i <= thisComp.numLayers; i++) {
var layer = thisComp.layer(i);
if (layer.name.match(/^Item\d+$/)) { // Check for matching layer names
var textValue = parseFloat(layer.effect("Value")(1).value);
if (!isNaN(textValue)) {
textValues.push({value: textValue, index: i});
}
}
}
// Sort the text values in descending order
textValues.sort(function(a, b) {
return b.value - a.value;
});
// Find the current layer's index in the sorted array
var currentIndex = textValues.findIndex(function(element) {
return element.index == index;
});
// Check for rank changes by iterating backward in time
var lastRank = currentIndex;
for (var t = time; t >= thisComp.displayStartTime; t -= thisComp.frameDuration) {
var sortedAtTime = textValues.slice(); // Clone the array
for (var j = 0; j < sortedAtTime.length; j++) {
var layerAtTime = thisComp.layer(sortedAtTime[j].index);
sortedAtTime[j].value = parseFloat(layerAtTime.effect("Value")(1).valueAtTime(t));
}
sortedAtTime.sort(function(a, b) {
return b.value - a.value;
});
var rankAtTime = sortedAtTime.findIndex(function(element) {
return element.index == index;
});
if (rankAtTime != lastRank) {
rankChangeTime = t; // Store the time of rank change
oldRank = rankAtTime; // Store the old rank based on sorting, not position
break;
}
}
// Calculate the time difference from the current time
var timeSinceChange = time - rankChangeTime;
// Calculate the Y positions based on rank, not transform position
var baseY = thisComp.layer("Controller").effect("Base Y")(1);
var spacing = thisComp.layer("Controller").effect("Spacing")(1);
var newY = baseY + currentIndex * spacing;
var oldY = baseY + oldRank * spacing; // Calculate oldY based on old rank
// Interpolate the position
var easedY = ease(Math.min(timeSinceChange / transitionDuration, 1), oldY, newY);
[moverPos[0], easedY+moverPos[1]];