13 Replies Latest reply on Jun 14, 2018 2:01 PM by davidn92468579

    Select Next Keyframe (in selected property) script

    oliverdead

      I find myself constantly crafting individual keyframes manually, so being able to automatically select the next/previous keyframe on a selected property with a shortcut (assigned to a script, since there's no such functionality in After Effects, being "Select Previous/Next Keyframes" the closest thing) instead of locating the keyframe and clicking on it, would save me, and everyone, hours of pointing and clicking.

       

      After some research I thought It was not possible but after some more (looking at scripts like this AE ENHANCERS • View topic - Exponential Scaling and Re: How can I access the layer keyframes?) , I've started to wonder if it is scriptable.

       

      Hope it is clear what I'm trying to achieve;

      Basically I'd like that pressing Shift+K would take me to the next keyframe in the selected property AND select it (Shift+J for the previous one) and

      pressing Ctrl+Shift+K would select the next keyframe in the selected property without actually moving the Current Time Indicator (Ctrl+Shift+J for the previous one).

       

      (I think it makes sense and it is consistent with the almighty J and K shortcuts but I didn't know how to write the code or ask for this functionality. Haven't been lucky asking to fellow animators either).

       

      Thanks,

       

      Oliver

        • 1. Re: Select Next Keyframe (in selected property) script
          UQg Level 4

          Hi Oliver,

           

          i think you should define precisely what you expect to happen in special situations like:

          • the layer has several keyframes selected
          • the last key is selected and you ask to select the next one (select no key? select only the last? do nothing ???)
          • same with first key already selected and you ask to select the previous.
          • Finally what to do in case several layers are selected and the key times dont match ? In particular if you ask the CTI to jump to next key time, where should it go ?

           

          It's probably because of all these amibiguities that those shortcut do not exist.

          If your "key" column is activated in the timeline panel, you can jump to prev/next key easily with the two buttons, and the action is always well-defined and unambiguous.

           

          If you have a precise idea of what should happen in various situations, the code to achieve it through a UI button  is actually not difficult. Through a shortcut it is another story (probably possible with a Mac, with Windows i dont think so).

           

          Xavier.

          • 2. Re: Select Next Keyframe (in selected property) script
            oliverdead Level 1

            Hey Xavier!

             

            Well as you can imagine, it would mainly be select next/previous keyframe of the active property (a keyframe should be selected), or else, do nothing. For example:

            • If there are several keyframes selected, determine which of the selected keyframes has a higher time value and then select the next one (higher time value, closest in time, not selected) in the property. (And viceversa for the 'Previous' shortcut).
            • If there are no unselected keyframes to jump to, do nothing.
            • If multiple properties/layers are selected, either do nothing or, ideally, determine the keyframe with higher time value and select the next one (closest in time, not selected) in any of the selected layers/property. (I can see how this one gets complicated, jumping between layers/properties).
            • If no keyframe is selected, of course, do nothing.

             

            My priority would be the select the next keyframe, rather the select AND jump to because, like you say, J and K do that somehow (without the selecting part but hey, better than nothing).

            And I guess if this works as a script (or button), you could assign it a shortcut, like you can do with your first scripts if I'm correct?

             

            And thankss a lot! (as you can see, I have no idea of how to script this:$ ).

             

            Oliver

            • 3. Re: Re: Select Next Keyframe (in selected property) script
              UQg Level 4

              Hi again,

              i don't know how to reference a new short cut. As i said it might be somehow possible with an AppleScript but i don't know how that works, and it won't work on Windows anyway.

              Here is another "solution", it works and is actually quite useful:

               

              1. Open After Effects.
              2. Open the script editor (File>Scripts>Open Script File)
              3. Copy paste the code below
              4. Save it in your Scripts folder (not the ScriptUI Panels folder, there is no UI) under a name for which you are sure it will be the first in your scripts collections (for the test, i used $01_SelectCurrentKeys.jsx)
              5. Close After Effects.
              6. Then Open your After Effects Shortcuts.txt (see this thread for details on where to find it, how to edit it: Re: Custom hotkeys)
              7. Choose a shortCut for "ExecuteScriptMenuItem01" (that's the hard part all combinations are taken already)
              8. Save and relaunch AE:

               

              What the shorcut will do: select the keyframe right under the CTI for all selected properties. Any other key is deselected.

              It's not exactly what you asked but... i think it's better: Use J/K to navigate and that new shortcut to actually select.

              If you want to stick to your original idea, it gives you a base to start with.

               

              Note: a script cannot know if a property is revealed or not in the timeline panel, so you have to actually select properties for it to work.

               

              Xavier.

               

              $.global.script_doSelectCurrentKeys || (script_doSelectCurrentKeys = function script_doSelectCurrentKeys(){
                  var comp = app.project.activeItem, props, n, p, idx, DT;
                  if (comp instanceof CompItem){
                      DT = Math.min(0.01, comp.frameDuration/10.0);
                      props = comp.selectedProperties;
                      for (n=0; n<props.length; n++){
                          p = props[n];
                          if (!p.numKeys) continue;
                          idx = p.nearestKeyIndex(comp.time);
                          if (Math.abs(p.keyTime(idx)-comp.time)<DT){
                              if (p.selectedKeys.length>0) p.selected = false;
                              p.setSelectedAtKey(idx, true);
                              }
                          else if (p.selectedKeys.length>5){
                              p.selected = false;
                              p.setSelectedAtKey(1, true);
                              p.setSelectedAtKey(1, false);
                              }
                          else{
                              while (p.selectedKeys.length) p.setSelectedAtKey(p.selectedKeys[0], false);
                              };
                          };
                      };
                  return;
                  });
              script_doSelectCurrentKeys();
              
              
              
              
              • 4. Re: Re: Select Next Keyframe (in selected property) script
                oliverdead Level 1

                Woohoo! Almost there!!

                 

                To me it would be perfect if you could select the next keyframe without having to navigate to it first. Like executing the script and the next keyframe is selected even if the CTI stays on the previous one (now deselected).

                Is that possible (I can't even tell which variable in the script is related to the CTI position)? That way you wouldn't have to navigate to it to change the keyframe, just use this shortcut then press Ctrl+Shift+K for example to enter its speed/influence values, (making the process even faster).

                 

                It's super cool that the script works with the property selected even if the layer is "folded", and that it works across properties in different layers!


                And you're right about the shortcuts; almost everything is taken but, luckily, the most perfect ones for this script (Shift+K for selecting the next keyframe, and Shift+J for selecting the previous) are not (at least on v11).

                Btw silly question, but what value should be inverted so it works for the previous keyframe?


                Ah my scripting igorance makes me feel stupid but again, thanks a lot dude!!


                Oliver

                • 5. Re: Re: Re: Select Next Keyframe (in selected property) script
                  UQg Level 4

                  I think your select prev/next key stuff will be more a source of trouble than time saving, especially if the properties are folded, but well...

                  You will need 2 scripts like the one above, one for each shortcut (Shift+J / Shift+K) and even 2 more if you stick to your original idea with (Ctrl+Shift+J/K).
                  Give them names so that they always are in the same position in your script collection and bind those scripts.

                   

                  For the code, it is very similar to the example above. For instance to select the next key:

                   

                  $.global.script_doSelectNextKeys || (script_doSelectNextKeys = function script_doSelectNextKeys(){
                      var comp = app.project.activeItem, props, n, p, idx;
                      if (comp instanceof CompItem){
                          props = comp.selectedProperties;
                          for (n=0; n<props.length; n++){
                              p = props[n];
                              // if the property has no key, or no selected key, skip
                              if (!p.numKeys || p.selectedKeys.length<1) continue;
                              // idx: the index of the last selected keys (safe, that array is always sorted)
                              idx = p.selectedKeys[p.selectedKeys.length-1];
                              // if the index of not the index of the last key, proceed
                              if (idx < p.numKeys){
                                  // deselect the property to deselect all of its keys
                                  p.selected = false;
                                  // reselect the next key
                                  p.setSelectedAtKey(idx+1, true);
                                  }
                              // in any other situation, deselect all keys
                              else if (p.selectedKeys.length>5){
                                  // faster way to deselect plenty of keys
                                  p.selected = false;
                                  p.setSelectedAtKey(1, true);
                                  p.setSelectedAtKey(1, false);
                                  }
                              else{
                                  // default way to deselect keys
                                  while (p.selectedKeys.length) p.setSelectedAtKey(p.selectedKeys[0], false);
                                  };
                              };
                          };
                      return;
                      });
                  script_doSelectNextKeys();
                  

                   

                  To select the previous key, replace

                              idx = p.selectedKeys[p.selectedKeys.length-1];

                              // if the index of not the index of the last key, proceed

                              if (idx < p.numKeys){

                                  // deselect the property to deselect all of its keys

                                  p.selected = false;

                                  // reselect the next key

                                  p.setSelectedAtKey(idx+1, true);

                                  }               

                  by

                              idx = p.selectedKeys[0];

                              if (1<idx){

                                  p.selected = false;

                                  p.setSelectedAtKey(idx-1, true);

                                 }

                    The rest is exactly the same,   give another name to the function  (script_doSelectNextKeys ---> script_doSelectPrevKeys) , to the script, etc.

                   

                  Xavier.     

                  • 6. Re: Re: Re: Select Next Keyframe (in selected property) script
                    oliverdead Level 1

                    Man this is beautiful!

                     

                    I see what you mean, but it was originally thought to speed up changing key values through a single property, and if you have to aim at a time so the script uses the key under the current CTI position well, that's another extra mouse step and you could as well just click on the keyframe. However that version might be ideal when you want to select all keys in the properties at that given time. For sure I'll keep it and I'm going to see if it can have another shortcut, (the similar ones seem already taken, but maybe it could be Shift+U, Shift+I, or something).

                     

                    Needs practical testing but I'd say this version is done! One more question though: can you make it not deselect the last one if there are no more 'next' keys? That way if you press the shortcut a lot of times to get you to the last keyframe fast you don't have to worry about getting "out" of the keys and having to click on the property again. (Don't know if that would be another condition before the else if of maybe remove one of the existing ones?).

                     

                    Forever Thanks.

                     

                    Oliver

                     

                    (Here's the draft for the SelectPrevKeys one).

                    • 7. Re: Select Next Keyframe (in selected property) script
                      UQg Level 4

                      To keep the last key in all situations, the code is actually shorter


                      $.global.script_doSelectNextKeys || (script_doSelectNextKeys = function script_doSelectNextKeys(){  
                          var comp = app.project.activeItem, props, n, p, idx;  
                          if (comp instanceof CompItem){  
                              props = comp.selectedProperties;  
                              for (n=0; n<props.length; n++){  
                                  p = props[n];
                                  if (!p.numKeys || p.selectedKeys.length<1) continue;    
                                  idx = p.selectedKeys[p.selectedKeys.length-1];     //(*) 
                                  p.selected = false;     //(*)   
                                  p.setSelectedAtKey(Math.min(idx+1, p.numKeys), true);     //(*)     
                                  };  
                              };  
                          return;  
                          });  
                      script_doSelectNextKeys(); 
                      

                       

                      For the prev key function, replace the lines marked with a (*) by

                                  idx = p.selectedKeys[0]; 

                                  p.selected = false;

                                  p.setSelectedAtKey(Math.max(idx-1, 1), true);

                       

                      Xavier

                      • 8. Re: Select Next Keyframe (in selected property) script
                        oliverdead Level 1

                        BOOOOOMM! Dude you nailed it!!

                         

                        I'm so happy right now just toggling through the keyframes XD This would have saved me hours, and I'm sure it will!

                        It's a little crazy with a lot of keyframes spread in time in several layers but even then it still works like a charm, (plus it's intended for fewer properties or more evenly distributed sets of keyframes anyway).

                         

                        On last question (I promise), for the other option (the one with the CTI jump as when pressing J/K), how can you make the CTI jump to the selected keyframe (if multiple, I guess it should jump to the one with higher time value)?

                         

                        Thanks again for all you patience, (I know you're probably super busy with work and this bordering on abuse already), you're the man!

                         

                        Oliver

                        • 9. Re: Re: Select Next Keyframe (in selected property) script
                          UQg Level 4

                          To jump to the last selected key:

                           

                          $.global.script_doJumpToLastSelectedKey || (script_doJumpToLastSelectedKey = function script_doJumpToLastSelectedKey(){  
                              var comp = app.project.activeItem, props, n, p, max=-Infinity;  
                              if (comp instanceof CompItem){  
                                  props = comp.selectedProperties;  
                                  for (n=0; n<props.length; n++){  
                                      p = props[n];
                                      if (p.numKeys && p.selectedKeys.length>0) max=Math.max(max, p.keyTime(p.selectedKeys[p.selectedKeys.length-1]));
                                      };
                                  if (-Infinity<max) comp.time = max;
                                  };  
                              return;  
                              });  
                          script_doJumpToLastSelectedKey();
                          
                          

                           

                          For the first key instead, replace

                          1. max= -Infinity by min=Infinity
                          2. the line if (p.numKeys && ... by  if (p.numKeys && p.selectedKeys.length>0) min=Math.min(min, p.keyTime(p.selectedKeys[0]));
                          3. the line if (-Infinity<max) comp.time = max; by if (min<Infinity) comp.time = min;

                           

                          That's it. One last thing, lots of things in this post now, it starts to be a little bit messy, so please quote answered, and preferably the one for select current time keys, it might be of interest to other users. The other ones i'm not sure

                           

                          Xavier.

                          • 10. Re: Re: Select Next Keyframe (in selected property) script
                            oliverdead Level 1

                            Yup, you're right! Marked as answered!

                             

                            That last one is not working though, or it is, but what I meant is for it to select the next key And jump he CTI. I tried combining the last two versions but: Result: undefined.


                            $.global.script_doSelectNextKeys || (script_doSelectNextKeys = function script_doSelectNextKeys(){  
                                var comp = app.project.activeItem, props, n, p, idx, max=-Infinity;  
                                if (comp instanceof CompItem){  
                                    props = comp.selectedProperties;  
                                    for (n=0; n<props.length; n++){  
                                        p = props[n];
                                        if (p.numKeys && p.selectedKeys.length>0) max=Math.max(max, p.keyTime(p.selectedKeys[p.selectedKeys.length-1])); 
                                        idx = p.selectedKeys[p.selectedKeys.length-1];     //(*) 
                                        p.selected = false;     //(*)   
                                        p.setSelectedAtKey(Math.min(idx+1, p.numKeys), true);     //(*)     
                                        };
                                     if (-Infinity<max) comp.time = max;
                                    };   
                                return;  
                                });  
                            script_doSelectNextKeys();
                            

                             

                            Oliver


                            • 11. Re: Select Next Keyframe (in selected property) script
                              UQg Level 4

                              Sorry to insist, but i think your shortcuts are super confusing!!! Should be better with elementary operations like J/K and select current imo...

                               

                              $.global.script_doSelectNextKeys || (script_doSelectNextKeys = function script_doSelectNextKeys(){   

                                  var comp = app.project.activeItem, props, n, p, idx, max=-Infinity;   

                                  if (comp instanceof CompItem){   

                                      props = comp.selectedProperties;   

                                      for (n=0; n<props.length; n++){   

                                          p = props[n]; 

                                          if (p.numKeys && p.selectedKeys.length>0){

                                              // get index of the last selected key

                                              idx = p.selectedKeys[p.selectedKeys.length-1];

                                              // change to next key

                                              if (idx<p.numKeys) ++idx;

                                              // select only that key

                                              p.selected = false;

                                              p.setSelectedAtKey(idx, true);

                                              // update the time

                                              max=Math.max(max, p.keyTime(idx);

                                              };

                                          };

                                      // this sets the composition time (CTI) to max

                                       if (-Infinity<max) comp.time = max; 

                                      };    

                                  return;   

                                  });   

                              script_doSelectNextKeys();

                               

                              Try to figure out the prev case!!! The first key has index 1, and the first selected key is p.selectedKeys[0]...

                               

                              Xavier

                              • 12. Re: Re: Select Next Keyframe (in selected property) script
                                oliverdead Level 1

                                Here here!

                                 

                                SelectJumpNextKey:

                                In a selected keyframe/keyframes, will select the next one in time AND jump the CTI to the time of that keyframe or, if multiple selected, the last of the selected keyframes.

                                (If you don't want the time jump but only the selection, just remove the second $.global.script_doJumpToLastSelectedKey script).

                                 

                                $.global.script_doSelectNextKeys || (script_doSelectNextKeys = function script_doSelectNextKeys(){   
                                    var comp = app.project.activeItem, props, n, p, idx, max=-Infinity;   
                                    if (comp instanceof CompItem){   
                                        props = comp.selectedProperties;   
                                        for (n=0; n<props.length; n++){   
                                            p = props[n]; 
                                            if (p.numKeys && p.selectedKeys.length>0){
                                                // get index of the last selected key
                                                idx = p.selectedKeys[p.selectedKeys.length-1];
                                                // change to next key
                                                if (idx<p.numKeys) ++idx;
                                                // select only that key
                                                p.selected = false;
                                                p.setSelectedAtKey(idx, true);
                                                // update the time
                                                max=Math.max(max, p.keyTime(idx));
                                                };
                                            };
                                         // this sets the composition time (CTI) to max
                                         if (-Infinity<max) comp.time = max; 
                                        };
                                    return
                                    });
                                script_doSelectNextKeys();
                                
                                
                                $.global.script_doJumpToLastSelectedKey || (script_doJumpToLastSelectedKey = function script_doJumpToLastSelectedKey(){    
                                    var comp = app.project.activeItem, props, n, p, max=-Infinity;    
                                    if (comp instanceof CompItem){    
                                        props = comp.selectedProperties;    
                                        for (n=0; n<props.length; n++){    
                                            p = props[n];  
                                            if (p.numKeys && p.selectedKeys.length>0) max=Math.max(max, p.keyTime(p.selectedKeys[p.selectedKeys.length-1]));  
                                            };  
                                        if (-Infinity<max) comp.time = max;  
                                        };    
                                    return;    
                                    });    
                                script_doJumpToLastSelectedKey();
                                

                                 

                                And this (nevermind the more than possible coding aberrations on my part) is what I originally aimed for. I think that not only it is simply amazing ("It just works") but so intuitive (specially using Shift+J/K) that should be an After Effects default.

                                Your version (select keyframes under current time in the selected properties) is super great too, but it serves a whole different purpose.

                                 

                                Now, for the SelectJumpPrevKey, I just have to figure how to make this bit:

                                 

                                $.global.script_doJumpToFirstSelectedKey || (script_doJumpToFirstSelectedKey = function script_doJumpToFirstSelectedKey(){    
                                    var comp = app.project.activeItem, props, n, p, max=-Infinity;    
                                    if (comp instanceof CompItem){    
                                        props = comp.selectedProperties;    
                                        for (n=0; n<props.length; n++){    
                                            p = props[n];  
                                            if (p.numKeys && p.selectedKeys.length>0) max=Math.max(max, p.keyTime(p.selectedKeys[p.selectedKeys.length-1]));  
                                            };  
                                        if (-Infinity<max) comp.time = max;  
                                        };    
                                    return;    
                                    });    
                                script_doJumpToFirstSelectedKey();
                                

                                 

                                actually go to the first keyframe selected and not the last (which, as simple as it may seem, to me is like watching the Matrix code and pushing random buttons in russian to see what happens).

                                 

                                Sorry I couldn't make your last code work and insted just stitched to scripts together! for some reason, the CTI wasn't jumping.

                                 

                                Mega thanks!!

                                 

                                Oliver

                                • 13. Re: Select Next Keyframe (in selected property) script
                                  davidn92468579 Level 1

                                  This is great, but is there any way to also select every property that has a key assigned to it before this runs? Thanks!