Your prescription for increased productivity and profitability
I have had people ask why they should delve into writing scripts for animating InDesign when the application provides so much capability from within its Animation panel. My answer is that using a script provides so much more capability. And, if one already knows how to write scripts, it is often much more intuitive. Further, the animation can be tied to all of the other automation involved in a project.
For instance, let’s say an online publication produced periodically includes an animated bar chart. The chart is always the same, just the data changes. This is an example of a task that lends itself well to using a script with an InDesign document template. The data could be read in from a text file, imported as XML, or otherwise. For simplicity, the demonstration script created in this blog post will list the data at the top of the script. It also will assume that the user has a web intent document open with a rectangle named “barChart” that serves to provide the chart background. Object styles have also been set up with one named “bar” for the bar, and one named “legend” for the bar’s text. The “legend” style also defines the paragraph style “barChart” to style its text. With this, the following script can create a simple bar chart.
Important: Make sure the document is saved in its pre-script state so you can revert it back to this state as you test your script (File > Revert).
set barList to {{"Taxes", 400}, {"Insurance", 100}, {"Health care", 200}, {"Housing", 500}} tell application "Adobe InDesign CC 2017" set rectRef to rectangle "barChart" of document 1 set objStyle to object style "bar" of document 1 set textObjStyle to object style "legend" of document 1 set spreadRef to parent of rectRef copy geometric bounds of rectRef to {pTop, pLeft, py1, px1} set pBounds to geometric bounds of rectRef repeat with i from 1 to length of barList set pTop to pTop + 40 set dataItem to item i of barList my makeBar(rectRef, pTop, pLeft, dataItem, objStyle, textObjStyle) end repeat end tell on makeBar(parentObj, pTop, pLeft, dataItem, objStyle, tStyle) tell application "Adobe InDesign CC 2017" set itemText to item 1 of dataItem set barWid to item 2 of dataItem tell parentObj make text frame with properties {name:itemText, geometric bounds:{pTop, pLeft, (pTop + 30), (pLeft + 70)}, contents:itemText, applied object style:tStyle} make rectangle with properties {name:itemText, geometric bounds:{pTop, (pLeft + 70), (pTop + 30), (pLeft + barWid)}, applied object style:objStyle} end tell end tell end makeBar
Run the script to create the chart.
…Our simple Bar Chart
To add animation that will allow each bar to “grow” from left to right, the script will use the design option property to current appearance as part of the bar’s animation settings. The animation settings will also define the following:
Let’s see how these are used to set the animation settings for each bar. First, define the values for the animation settings at the top of the script.
set xScale to {{0, 100}, {47, 0}} --bar scales horizontally in 47 time frames set yScale to {{0, 100}, {47, 100}} --bar does not scale vertically set tOffsets to {0, 0.5} set dTime to 2 --duration for bar set hideIt to {false, false}
Adding the above to the Bar Chart script, we will also add a call to a handler named growIt directly after the call to the makeBar handler inside the repeat block. This will set the animation settings for each of the bars.
tell application "Adobe InDesign CC 2017" set rectRef to rectangle "barChart" of document 1 set objStyle to object style "bar" of document 1 set textObjStyle to object style "legend" of document 1 set spreadRef to parent of rectRef copy geometric bounds of rectRef to {pTop, pLeft, py1, px1} set pBounds to geometric bounds of rectRef repeat with i from 1 to length of barList set pTop to pTop + 40 set dataItem to item i of barList my makeBar(spreadRef, pTop, pLeft, dataItem, objStyle, textObjStyle) set objName to (item 1 of item i of barList) set barRef to rectangle objName of spreadRef my growIt (barRef, dTime, xScale, yScale, tOffsets, hideIt) end repeat end tell (*The handler that animates the rectangle*) on growIt(objRef, dTime, xScale, yScale, tOffsets, hideIt) tell application "Adobe InDesign CC 2017" tell animation settings of objRef set transform offsets to tOffsets set duration to dTime set design option to to current appearance set initially hidden to item 1 of hideIt set hidden after to false set scale x array to xScale set scale y array to yScale end tell end tell end growIt
Make sure you include the makeBar handler with your script.
Revert the document back to its original state. Run the script and view the result in the EPUB Interactivity Preview panel (Window > Interactive > EPUB Interactivity Preview).
Should you want to fade in the legends as well, you will need to add definitions of duration and opacity (opacity array) for the legends at the top of the script.
set textD to 0.5--duration for legend set oArray to {{0,0}, {11, 100}}--opacity array
Next create a call to a fadeIn handler to apply the opacity settings to each legend. This will be within the repeat loop before the call to growIt:
set textRef to text frame objName of spreadRef my fadeIn (textRef, textD, oArray)
The fadeIn handler only requires a couple of lines:
on fadeIn (objRef, dTime, oArray) tell application "Adobe InDesign CC 2017" tell animation settings of objRef set duration to dTime set opacity array to oArray set hidden after to false end tell end tell end fadeIn
Revert the document back to its original state. Run the script, and view the animation in the EPUB Interactivity Preview panel.
Each legend fades in just before the bar animates. This may be good enough for your purpose, but the relative speed at which the bars animate is very noticeable. The short bar animates very slowly, while the longer bars animate quickly (same amount of time, but distance is different). To make it appear as if the bars animate at the same rate, we could lengthen the time it takes the longer bars to animate or shorten the time for the shorter bars. Let’s think this over. If we allow 47 frames as the duration for the longest bar, then the other bars should take a proportionately shorter time.
To tackle this problem, first create a handler to return the length of the longest bar in the list. Since barList is a list of lists, we need to indicate the item of each list that will be used for comparison. This is the value of 2 passed as the second argument to the getLongest handler. You might want to test this in a separate script just to make sure you get it right:
set barList to {{"Taxes", 400}, {"Insurance", 100}, {"Health care", 200}, {"Housing", 500}} set longest to getLongest (barList, 2) longest --just to test (*the getLongest handler*) on getLongest(theList, theIndex) --set the first item to be the longest set longest to item theIndex of item 1 of theList --repeat through the list to see if there is an item that is longer repeat with i from 2 to length of theList set theItem to item theIndex of item i of theList if theItem > longest then set longest to theItem end if end repeat return longest end getLongest
The script will now use the value returned from the getLongest handler to determine the timeFrame at which each of the bars will reach its maximum length (100%). It will also be used to calculate the duration of time involved. Adding the statements to determine these values, the script is now written as follows:
set barList to {{"Taxes", 400}, {"Insurance", 100}, {"Health care", 200}, {"Housing", 500}} set maxFrame to 47 set tOffsets to {0, 0.5} set dTime to 2 set hideIt to {false, false} set textD to 0.5 set oArray to {{0, 0}, {11, 100}} tell application "Adobe InDesign CC 2017" set rectRef to rectangle "barChart" of document 1 set objStyle to object style "bar" of document 1 set textObjStyle to object style "legend" of document 1 set spreadRef to parent of rectRef copy geometric bounds of rectRef to {pTop, pLeft, py1, px1} set longest to my getLongest(barList, 2) repeat with i from 1 to length of barList --calculate timeFrame and duration based on longest and dTime set theDistance to item 2 of item i of barList --propTime will be a decimal value less than 1 set propTime to theDistance / longest --multiply this times the maximum time frame length set timeFrame to round (propTime * maxFrame) --use this calculation to set xScale and yScale array set xScale to {{0, 100}, {timeFrame, 0}} set yScale to {{0, 100}, {timeFrame, 100}} --use the propTime percentage to determine the time duration set relTime to dTime * propTime --create the bar and legend set pTop to pTop + 40 set dataItem to item i of barList my makeBar(spreadRef, pTop, pLeft, dataItem, objStyle, textObjStyle) set objName to (item 1 of item i of barList) --reference the text frame and bar by name set textRef to text frame objName of spreadRef my fadeIn(textRef, textD, oArray) set barRef to rectangle objName of spreadRef my growIt(barRef, relTime, xScale, yScale, tOffsets, hideIt) end repeat end tell
Make sure to include the following handlers for the script here: makeBar, growIt, fadeIn, and getLongest
Revert the document back to its original. Run the script, and view the animation in the EPUB Interactivity Preview. Ah, the beauty of mathematics; each bar appears to be animating in at the same rate of speed.
There may be one more thing you would want to add to the script to make your bar chart even more impressive: have the legends animate in at the same time its bar is animating. To do this you create a timing group for each bar and its legend. This is part of the spread’s timing settings. You may want to use the following script to investigate the current settings for the spread’s timing settings (default):
tell application "Adobe InDesign CC 2017" set rectRef to rectangle "barChart" of document 1 set spreadRef to parent of rectRef set testIt to properties of timing lists of timing settings of spreadRef try set testGroups to timing groups of timing list 1 on error set testGroups to {} end try end tell {testIt, testGroups}
Notice that timing settings for a spread is just a list of timing lists which define the event that will trigger the groups in its list. When working with a spread’s timing settings, the practice is to delete all current timing settings, and create the ones needed. When you add statements for timing settings to the Test Script it will read as below. We can use this script to verify how timing settings works. Run the following with the Chart created using the Animate Bar Chart script.
tell application "Adobe InDesign CC 2017" set rectRef to rectangle "barChart" of document 1 set spreadRef to parent of rectRef set tSettings to timing settings of spreadRef tell tSettings delete timing list 1 set loadTimingList to make timing list with properties {trigger event:on page load} tell loadTimingList set group1 to make timing group with properties {dynamic target:rectangle "Taxes" of spreadRef, delay seconds:0} tell group1 make timing target with properties {dynamic target:text frame "Taxes" of spreadRef, delay seconds:0} end tell end tell end tell end tell
Run the Test TimingGroup script. In the EPUB Interactive Preview panel, clear the Preview, and click the Play Preview button. Only the first bar animates. The text fades in so quickly that the fade in is barely noticeable.
Now that you have tested making a timing group, expand the top of the original Animate BarChart script to use the timing list and timing groups. Here is the top portion of the script. Make sure you keep the following handlers: makeBar, growIt, getLongest.
set barList to {{"Taxes", 400}, {"Insurance", 100}, {"Health care", 200}, {"Housing", 500}} set maxFrame to 47 set tOffsets to {0, 0.5} set dTime to 2 set hideIt to {false, false} set textD to 0.5 set oArray to {{0, 0}, {11, 100}} tell application "Adobe InDesign CC 2017" set rectRef to rectangle "barChart" of document 1 set objStyle to object style "bar" of document 1 set tStyle to object style "legend" of document 1 set spreadRef to parent of rectRef copy geometric bounds of rectRef to {pTop, pLeft, py1, px1} set pBounds to geometric bounds of rectRef set longest to my getLongest(barList, 2) set testList to {} repeat with i from 1 to length of barList set theDistance to item 2 of item i of barList set propTime to theDistance / longest set timeFrame to (round (propTime * maxFrame)) set xScale to {{0, 100}, {timeFrame, 0}} set yScale to {{0, 100}, {timeFrame, 100}} set relTime to dTime * propTime set end of testList to relTime set pTop to pTop + 40 set dataItem to item i of barList my makeBar(spreadRef, pTop, pLeft, dataItem, objStyle, tStyle) set objName to item 1 of dataItem set textRef to text frame objName of spreadRef my fadeIn(textRef, textD, oArray) set barRef to rectangle objName of spreadRef my growIt(barRef, relTime, xScale, yScale, tOffsets, hideIt) end repeat (*Timing settings added*) set tSettings to timing settings of spreadRef tell tSettings delete timing list 1 set loadTimingList to make timing list with properties {trigger event:on page load} tell loadTimingList repeat with j from 1 to length of barList set thisName to item 1 of item j of barList set thisGroup to make timing group with properties {dynamic target:rectangle thisName of spreadRef, delay seconds:0} tell thisGroup make timing target with properties {dynamic target:text frame thisName of spreadRef, delay seconds:0} end tell end repeat end tell end tell end tell
Again, revert the document and run the script. View the animation in the EPUB Interactivity Preview panel.
To see the final document published online, Click here. Note: For the purpose of Publish Online, we set the trigger event for the spread timing list to on page click.
This script works best with objects that are parallel to the page’s horizontal or vertical axis. To use the script with a line oriented otherwise (on an angle), create the line parallel to the page’s axis (horizontal or vertical), add animation, and then rotate. Here is a simple example.
set xScale to {{0, 100}, {47, 0}} set yScale to {{0, 100}, {47, 100}} set tOffsets to {0, 0.5} set dTime to 2 set hideIt to {false, false} tell application "Adobe InDesign CC 2017" set spreadRef to spread 1 of document 1 set swatchRef to swatch "Black" of document 1 tell spreadRef set lineRef to make graphic line with properties {name:"myLine", geometric bounds:{200, 30, 200, 500}, stroke weight:10, stroke color:swatchRef} set rotation angle of lineRef to 30 tell animation settings of lineRef set duration to 2 set transform offsets to tOffsets set design option to to current appearance set initially hidden to item 1 of hideIt set scale x array to xScale set scale y array to yScale end tell end tell end tell
Add comments to the script and each of the handlers so you will be able to recognize functionality the next time you need this script or one similar. Always try to write your scripts using handlers with the idea of being able to reuse for other projects.
Try creating a bar chart with vertical bars instead of horizontal. Optionally, set it up so that bars have different colors for fills and stroke. Have fun.
Disclaimer: Demonstration scripts are provided to give users a guide from which to create their own scripts. No representation is given as to their completeness or reliability.