April 9, 2020
Old Fashioned Car Radio – Part 2: The Code (WARNING – Major Nerd Post)
Comments
(5)
April 9, 2020
Old Fashioned Car Radio – Part 2: The Code (WARNING – Major Nerd Post)
I am currently a provider of technical training and support in the electronic manufacturing industry. My prior training and work experience as a teacher, network administrator, web design, and instructional design make me well prepared to design it, develop it, and deliver it. I am a father of five, a US Army veteran, and I enjoy playing the guitar as well as performing in local community theater. 
Legend 99 posts
Followers: 155 people
(5)

Ok everyone. Here is how I managed to pull off the car radio.

All graphics were created in PowerPoint and saved as  PNG  files. The only exception here is the orange bar to mark placement for the radio station. That is just a simple smartShape.

Rotating Knobs
Since Captivate does not offer simple sliders or rotating dials, both of the knobs are inserted as HTML5 Animations. I used some code – with the help of  Greensock – to do the heavy lifting. Here is how it looks for the tuning knob. The volume knob does the same thing for the rotation but uses different variables

<html>
<head>
  <script src=”gsap.min.js”></script>
  <script src=”Draggable.min.js”>
  gsap.registerPlugin(Draggable);
  </script>
</head>

<body>
<img id=”simKnob” src=”plainKnob.png” width=”125″ height=”125″>

<script>
setTimeout(function() {
Draggable.create(“#simKnob”, {
  type: “rotation”,
  liveSnap: gsap.utils.snap(7),
  bounds: {minRotation: -1610, maxRotation: 1610},
  onDrag: function() {
    getRotVal=this.rotation;
    window.top.station=getRotVal;
    window.top.moveLine();
    }
  });
},500);
</script>

</body>
</html>

Let’s talk about it just a bit.

Blue Portion
We start off by pointing to the Greensock (GSAP) animation libraries needed for this to work. As you can see, they are relative links because I packaged them up in the zip file so that I did not have to modify the published index.html file to go and grab them. I preferred this method because I did not have to continue making that edit whenever I wanted to publish a change and test it. These are script tags within the head tags up front.

Green Portion
Here is the body of it all. I am simply calling out the image I created in PowerPoint. This is what I will be animating with the code. Be sure to give the image an id (simKnob in this case) so that you can reference it for the animation.

Red Portion
Here is the code the does the majority of the work.
We start with a short half second setTimeout so that the inserted HTML5 animations have a chance to fully render on the screen. If they are not fully rendered, the code breaks because there is nothing there yet to animate. So within this setTimeout, let’s go line by line.

Draggable.create(“#simKnob”, {

This is GSAP syntax – but it is very simple compared to making a rotating knob from scratch. So you see the #simKnob in quotes there? I am simply saying create a draggable object out of that element. You’ll notice I am referencing the ID of my knob graphic from the green portion above.

type: “rotation”,

Next we are telling the object that it will have a rotating behavior when we drag on it.  (Nice!)

liveSnap: gsap.utils.snap(7),

This is a GSAP utility that allows the dragged object to snap to every 7 degrees of rotation. Perhaps that seems like an odd value to you… let me explain it because you may very well need to use a different value here in your own project. In order to do that – let me bring in the next line…

bounds: {minRotation: -1610, maxRotation: 1610},

This sets the boundaries for my ability to rotate the knob. A total range of 3220 degrees. 1610 to the left and 1610 to the right based on the placement of the orange bar in the center of my radio as a starting point. Ok. Now. My radio station range in pixels on the Captivate stage is a total of 460 pixels. I determined this by placing the orange bar at the furthest position I wanted it to travel on each end and looked at the difference in X values.

OK so that means my tuning knob with 3220 degrees of range needs to correspond with 460 pixels of distance. Doing the math we find that 3220/460 = 7  and that is why I have the snap value set to every 7 degrees.

If you thought that math was scary – wait until we cover the onEnter JavaScript. The math is scarier than the code!

onDrag: function() {

Next we set up a function that is called as we drag (or rotate) the knob. This function will perform three things for every 7 degree turn of that knob. (See how this is playing out?)

getRotVal=this.rotation;

First, the function will grab the current rotation value, in degrees of the knob. It will be a value between -1610 and +1610. We are assigning that value to a variable I am calling  getRotVal  for get rotation value.

window.top.station=getRotVal;

Next, I am basically sending that value out of the animation and to the Captivate stage (window.top) to another variable called station.

window.top.moveLine();

Finally, I am calling a function called  moveLine  which does what you might expect. It moves the orange line on the radio. Notice I am using window.top again. This function is defined in the onEnter action of the slide.

Then we just close everything up and use the value of 500 for the setTimeout which is a half second. This means that when the setTimeout is executed it will wait a half second to perform all the stuff inside.

Last we close up the body and html tags.

Whew!

OK – so this html file, my knob image, and the js libraries get zipped up and inserted as an HTML5 Animation.

onEnter Code
Now here is a little extra to make this work and it is perhaps a little bit scarier if you are not a fan of algebra but let’s put on our 8th grade sweater vests again and dodge a few spit balls OK?

cpCmndVolume=20;

function moveLine() {
station=Math.floor(station);
linePos=((station*1+3563)/7)-509;
$(“#myLinec”).animate({left: linePos},10);

if (linePos==121) {
document.getElementById(“preset1”).click();
}

if ((linePos<119) || (linePos>123)) {
document.getElementById(“preset1Stop”).click();
}

}

function adjVol() {
volume=Math.floor(volume);
cpCmndVolume=(Math.floor(((2*volume)/9)+20));
}

Let’s investigate. Basically we have a short statement to set the volume at 20 followed by two functions. The first function is the scarier one. This is the function that moves the line. Let’s break that into three parts.

function moveLine() {
station=Math.floor(station);
linePos=((station*1+3563)/7)-509;
$(“#myLinec”).animate({left: linePos},10);

In this first part we name the function moveLine. Remember this is called by every 7 degrees of turn on this knob. Our second line takes the station variable and chops off all the decimal places using Math.floor. We are doing this because the knob grabs the rotation value out to a boat load of decimal places and that was too messy.

The third line is scary and is where the algebra comes in but it was really necessary to create a smooth moving line as you rotate the knob. The key is in using the slope-intercept formula of a line. ( y=mx+b )  Don’t throw up on me. Hang in there. I had found the relationship of the knob range to the line range which was 7 degrees turn for every pixel of line range but now I needed to translate that as a full set of points like you would have on that line between (279, -1610) and (739, 1610).

So I can figure out the slope of this line using rise over run – change in y over change in x – or in this case 7 over 1. My  X  value (run) is my left and right of the line which makes my  Y  value (rise) the degrees of turn. Remember? We have 7 degrees turn for each pixel horizontally.  So the slope of the line is 7/1 or just 7.

Now we have y=7x+b. Recall that b is equal to the y-intercept which is whatever Y equals when  X  is at 0. We can solve for b by plugging one of the known (x,y) values. Let’s use the positive numbers.

1610 = 7(739) + b

1610 = 5173 + b

-3563 = b

Awesome! Now I know my complete equation.  y = 7x – 3563

Remember this because we need it to calculate the animation of the line now. Let’s look at it.

linePos=((station*1+3563)/7)-509;

Here I have a variable called  linePos  which will basically track the x value of the object on the screen and move it to that position based on the rotation of the knob.

In this equation linePos is our  X  and station is our  Y.  Solve our equation for  X.

y = 7x – 3563

y + 3563 = 7x

x = (y + 3563)/7    (notice if you solve for x when  y = 0  that   x = 509)

We need a start value of zero for the line so we can travel left and right from there. So we take the station variable and sub it for  Y  (that was the rotation value with decimal points cut off) and multiply it by 1 so that it can be used in number form rather than string form and add our 3563 and divide by 7 as you see in the solved equation. At the end I am subtracting 509 so that my x value becomes 0 which is the starting reference for the line movement.

OK – breathe now.

$(“#myLinec”).animate({left: linePos},10);

Now we go ahead and move the line to the calculated value for the  linePos variable in 10 milliseconds. Every 7 degree snap is a single pixel and it moves that pixel in only 10 milliseconds so it helps it to look pretty smooth and realistic. At least I think it does.

Ok – A couple of if statements now.

if (linePos==121) {
document.getElementById(“preset1”).click();
}

if ((linePos<119) || (linePos>123)) {
document.getElementById(“preset1Stop”).click();
}

The first one is where our line position is 121 pixels to the right of the starting position. This is where I guessed that 103.7 would roughly be. When we get there, I use the code to click an invisible button to play the audio. That button simply has a play audio event on it. In the next if statement, I give a few pixels of range before shutting that audio off because you are too far from the station. Again, I just use an invisible button to stop the audio. That is an old school trick but it was the easiest to implement in this scenario.

function adjVol() {
volume=Math.floor(volume);
cpCmndVolume=(Math.floor(((2*volume)/9)+20));
}

The final function is for tracking the volume range of 0 to 100 across the rotation range I have set up on that knob which was just short of a turn and a half but I won’t put you through that one unless you’re really interested.

I hope you found this interesting.

Feel free to ask any questions you might have.

5 Comments
2020-04-09 12:58:57
2020-04-09 12:58:57

Hi Greg,

Thanks for the tip on using GreenSock, I look forward to exploring their site and learning more about it!

Like
(2)
(1)
>
Anonymous
's comment
2020-04-09 13:22:41
2020-04-09 13:22:41
>
Anonymous
's comment

I first heard of GreenSock here in the forums myself when someone asked a question about it. At some point down the road I decided to investigate it. So glad I did.

I incorporated some GreenSock stuff into a Captivate project for work as a proof of concept and my company has agreed to purchase a business license for it so we can sell the end products I make with it to our customers.

Very responsive, helpful, and nice folks in those forums as well – big plus.

Like
(1)
2020-04-09 11:12:05
2020-04-09 11:12:05

Too bad I’ve finished my box of aspirine after a virtual “apéritif” with some friends yesterday !… I would need some more !… I’m sure you could code a Javascript Anti-Covid-19 ??!!… Anyway…

Thanks again to prove us that Captivate could be very very captivating !…

Stay safe !… 😉

Like
(2)
(2)
>
Ludovic Mercier
's comment
2020-04-09 13:17:59
2020-04-09 13:17:59
>
Ludovic Mercier
's comment

Thanks, Ludovic

I would argue that Captivate has, by far, the most potential of all the authoring tools out there.

Like
(1)
>
Greg Stager
's comment
2020-04-15 18:00:39
2020-04-15 18:00:39
>
Greg Stager
's comment

I’d lean more towards Adobe Animate in the way of power and flexibility. It does take a little longer to develop in Adobe Animate compared to Captivate. Plus you do need to know how to effectively code and how to use the timeline well.

Of the big three eLearning tools I would fully agree with you Greg that Captivate has the most potential.

Like
()
Add Comment