Skip to main content
Jake7732
Inspiring
August 6, 2018
Answered

Select object based on its opacity and change opacity to set value.

  • August 6, 2018
  • 3 replies
  • 3827 views

I am trying to create some javascript code but cannot find any references online relating to this specific instance.

What i am attempting to do is have the script find an object(s) based on its opacity in the transparency window and then if it is 80% i want to change it to be 60%.

This would be done for every opacity value from 99% to 1% and then changing it to a specific value.

Any thoughts on where to start?

Thanks!

This topic has been closed for replies.
Correct answer Disposition_Dev

function test()

{

    var docRef = app.activeDocument;

    var layers = docRef.layers;

    var opacityRelationships = {

        "80": 60

        //put in the rest of your relationships here

        //left side is the current opacity of an object

        //right side is what you want to change it to

    }

    function updateOpacity(item)

    {

        var curOpacity = Math.floor(item.opacity).toString();

        item.opacity = opacityRelationships[curOpacity];

    }

    for(var x=0,len = layers.length;x<len;x++)

    {

        for(var y=0,yLen = layers.pageItems.length;y<yLen;y++)

        {

            updateOpacity(layers.pageItems);

        }

    }

}

test();

3 replies

Jake7732
Jake7732Author
Inspiring
August 15, 2018

Finally finished the code to select and modify both solid fill colors and gradient stops transparency from one value to another via an array.

thank you williamadowling​ for helping me get started!

For reference, in case this happens for anyone else, my final code is below: (could probably still be simplified but its working as intended)

function test() {

    var docRef = app.activeDocument;

    var layers = docRef.layers;

    var paths = docRef.pathItems;

    var items = docRef.selection;

    var opacityRelationships = {

     "100": 100, "99":70,

       // rest of relationships go here e.g.

    }

   

    for (var i=0,len = paths.length;i<len;i++) {

        if (paths.fillColor == '[GradientColor]') {

    for (var z = 0; z < paths.fillColor.gradient.gradientStops.length; z++) {

            function updateOpacityGradient(item){

                var curOpacity = Math.floor(item.opacity);

        item.opacity = opacityRelationships[curOpacity];

    }

updateOpacityGradient(paths.fillColor.gradient.gradientStops);

  }

}

   }

  

    for (var j=0,len = paths.length;j<len;j++) {

    for (var r=0,len = layers.length;r<len;r++){

if (paths.fillColor == '[SpotColor]') {

   for (var y=0,yLen = layers.pageItems.length;y<yLen;y++) {

    function updateOpacitySpot(item2){

                var curOpacity = Math.floor(item2.opacity).toString();

        item2.opacity = opacityRelationships[curOpacity];

    }

updateOpacitySpot(layers.pageItems);

   }

        }

   }

   }

}test();

Disposition_Dev
Legend
August 15, 2018

looking good. if it's working, awesome. but if you're interested in some simplifications or improvements, i'll share them here, unsolicited, and you can do with that knowledge what you will. Hopefully it may help others as well.

This code looks a LOT like code i wrote when i was at your stage of coding. But i ran into some things along the way that perhaps you may be able to avoid without learning the hard way like i did.

You're on the right track by thinking "this code could be simplified". This is almost always true. However, don't get caught in the trap of "i need to make this code as simplified as possible". There is a threshold beyond which further simplifying code will actually make it less intuitive, harder to debug and harder for another coder to read. A good goal to strive for is that your code is highly readable (for me, this means that your functions and variables are named in a very clear and unambiguous way. for example, don't use a variable name like "io" to store the opacity of a given item.. use "itemOpacity" so your variables don't need to be decoded. this is probably a poor example, but you get the point.), while also minimizing inefficiencies.

The first part of that goal looks great in your code. your variable names and functions are clearly named. The second part is where we are going to look at some improvements.

The very first thing that stood out to me when i looked at your code here, is that your function declarations occur inside of for loops. Inside nested for loops at that. This means that every time you meet the conditions of running one of these functions, you are re-declaring the function. This is inherently inefficient since the function itself is not changing. When you write a loop, ask yourself "is there anything inside the body of this loop that never changes?". If the answer is yes, that portion should not be inside the loop. Your function declarations should exist in the top-level scope of the test() function. For our purposes, I will refer to this scope as "script-global" because this scope encompasses everything we need for this script.

So, now that we've identified that the function declarations remain static throughout the execution of the script, we can pull those out to script-global scope. This will help eliminate clutter, make your code easier to read, and improve efficiency.

Another efficiency improvement could be found by taking a step back and really analyzing your "j" for loop. In this loop, you're beginning by looping every pageItem in the document (special note.. be careful any time you do this because at the document scope, pathItems is an array of every single pathItem in that document, including pathItems that are nested inside groups or compound paths. It's generally a good idea to identify a more narrow scope to process as this will help prevent extremely long execution times on larger files). Then for each pathItem in the document, you are looping every layer in the document. and for every layer, you're performing a check on the same pathItem. So if you had 10 layers in your document, you'd be checking the same pathItem 10 times to see whether it is a spot color. This is inherently inefficient. It's generally a good idea to work top down when traversing the DOM. the "larger" elements should come first in your nesting order, otherwise you risk redundancy. In this case, since your second loop (the one with the variable "r") has no relationship to it's parent loop, it's gotta go. As a fun experiment, you can get a more comprehensive view of the inefficiency i'm talking about by adding a console log message to the beginning of each of these loops. Just add something simple like $.writeln("j = " + j);Then run the script again and watch the console. I think you'll find that even in a relatively small document, there's a lot more happening than you think.

the TLDR of the above paragraph is as follows.. You start by looping every path item. then you loop the layers in the document. then you loop the pageItems of each layer. Since you're looping the pageItems of the layers, you should remove either the "j" loop, or the "y" loop.

Next, let's look at your loop structure. Surely many people will have their own opinions about this, but my opinion is that if you find yourself nesting two for loops directly inside one another, it's probably a good opportunity to create a function instead. Otherwise your loop variables can get confusing. This is an issue of readability as well as an issue of ease of debugging. I couldn't tell you how much time i spent debugging some code because i used the wrong loop variable somewhere inside a nested for loop.

I feel like i'm rambling too much here, so I'll just post what i would consider to be the cleaner and more efficient code. Then you can ask questions if it doesn't seem to make sense.

function test()

{

    var docRef = app.activeDocument;

    var layers = docRef.layers;

    var paths = docRef.pathItems;

    //this doesn't change throughout this script,

    //so let's declare it at the script-global scope.

    var pathLen = paths.length;

    var opacityRelationships = {

        "100": 100,

        "99": 70,

        // rest of relationships go here e.g. 

    }

    function updateOpacityGradient(item)

    {

        var stops = item.fillColor.gradient.gradientStops;

        var curOpacity; //it's generally agreed that when possible, variables should be declared outside of loops

        for (var x = 0, len = stops.length; x < len; x++)

        {

            curOpacity = Math.floor(stops.opacity);

            stops.opacity = opacityRelationships[curOpacity];

        }

    }

    function updateOpacitySpot(item)

    {

        var curOpacity = Math.floor(item.opacity).toString();

        item.opacity = opacityRelationships[curOpacity];

    }

    //first let's process the gradient stops

    for (var i = 0; i < pathLen; i++)

    {

        //in this first condition, you should always protect yourself against

        //runtime errors by first checking that a given path is 'filled' before

        //you try to access it's fillColor.

        if (paths.filled && paths.fillColor == "[GradientColor]")

        {

            updateOpacityGradient(paths);

        }

    }

    //now let's look at the opacity of non-gradient pathItems

    for (var i = 0; i < pathLen; i++)

    {

        //again, make sure it's filled before you check the fillColor

        if (paths.filled & paths.fillColor == "[SpotColor]")

        {

            updateOpacitySpot(paths);

        }

    }

}

test();

Jake7732
Jake7732Author
Inspiring
August 15, 2018

You have been unbelievably helpful. I cannot thank you enough for your time and dedication you have for this forum. After playing around with this script for the past week I've actually learned a lot more than i thought from trial and error and will be definitely following your advice above moving forward. A lot of that made way more sense seeing it written out rather than just staring at the code and trying to see "it".

Thank you so much again!

renél80416020
Inspiring
August 6, 2018

Salut !

// traite les tracés sélectionnés

function test() {

  var o1 = 80;

  var o2 = 60

    if (!selection.length) return;

    var sel = selection;

      function changeOpacity(items) {

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

            if (Math.round(items.opacity) == o1) {

              items.opacity = 60;

            }

          }

      }

      changeOpacity(sel);

}

if (app.documents.length) {test();}

de LR

Jake7732
Jake7732Author
Inspiring
August 6, 2018

This does what i need if the individual object is selected already which is very helpful for getting me started but I am also looking for a way to auto select the similar opacity items and then modify them with the specific value. i'll play around with this a bit and update if i find any further info as well.

Thanks!

Disposition_Dev
Disposition_DevCorrect answer
Legend
August 6, 2018

function test()

{

    var docRef = app.activeDocument;

    var layers = docRef.layers;

    var opacityRelationships = {

        "80": 60

        //put in the rest of your relationships here

        //left side is the current opacity of an object

        //right side is what you want to change it to

    }

    function updateOpacity(item)

    {

        var curOpacity = Math.floor(item.opacity).toString();

        item.opacity = opacityRelationships[curOpacity];

    }

    for(var x=0,len = layers.length;x<len;x++)

    {

        for(var y=0,yLen = layers.pageItems.length;y<yLen;y++)

        {

            updateOpacity(layers.pageItems);

        }

    }

}

test();

Jake7732
Jake7732Author
Inspiring
August 6, 2018

Getting an error on "item.opacity = opacityRelationships[curOpacity];" Says "Numeric value expected".

Edit* is it due to this code referencing itself?:

  var curOpacity = Math.floor(item.opacity).toString();

       item.opacity = opacityRelationships[curOpacity];

Disposition_Dev
Legend
August 6, 2018

that error message was because it encountered an item that had an opacity that you had not yet written into the opacityRelationships object. Therefore, it was trying to set the item.opacity to "undefined".

renél80416020's method is good, but it has numbers hard coded into the changeOpacity function which makes it not every versatile. To use that method, you'll either need to add a ton of if/else statements to handle every possibility, or employ some kind of data structure like i did.

my method did indeed reference itself, it uses the current opacity of the given object to find the new desired opacity. But that in itself won't cause an error. I put a comment to let you know where you should fill out the rest of your desired relationships.

Alternatively, instead of hard coding the relationships, we can create a mathematical relationship instead.. for example, if you want to decrement the opacity by 20%, we can just write an equation and leave out the data structure all together.