Plan Dashboard Animation0:00
For this experiment, I want to do a complex animation sequence. I want to compose a bunch of different animations together and create something that is more visually complex. And so the inspiration for this is going to be a dashboard on a spaceship, and it's going to be sort of 2001 A Space Odyssey inspired, and it'll be Hal blinking up in the left corner and the dashboard sort of in pandemonium as Dave chats with Hal. So that's what we're shooting for here. Let's build that out. So first, as we're bypassing the standard prompt method, we need to implement run, which is what we're going to run when we run the script.
Create Run Loop0:38
So first, as we're bypassing the standard prompt method, we need to implement run, which is what we're going to run when we run the script. So we're going to say run void, and we need to do our setup and reset. So we're going to say use setup and reset from Chewy, and we'll say this->setup, this->showDashboard, and then let's implement that method. So we'll say protected function showDashboard void. Let's do what we always do. Let's set up a keyPressListener. listener = keyPressListener for this->listenForQuit. And then we are going to be looping, so we need to import the LoopTrait from Chewy.
Listener equals key press listener for this listen for quit. And then we are going to be looping, so we need to import the LoopTrait from Chewy. Use loops, and we'll say this loop function use listener, and we're just going to say this render and listener once. So we'll listen for key presses once per render. Great. Easy enough. Our world is set up, and we're ready to start adding some loopables to animate. So I've set up some loopables ahead of time. Some of them are a little more fleshed out.
Build HAL Pulse1:52
So I've set up some loopables ahead of time. Some of them are a little more fleshed out. Some of them are bare bones, but it's just enough to get us started so you don't have to watch me type them all out. First things first, I think I want to have a little red HAL light pulsing in the upper left hand corner of the screen. So let's set that one up. So we'll have a public HAL pulse, HAL pulse, and then let's set that up in the constructor. So we'll say $this->HAL pulse = new HAL(); Let's take a look and see what's in here.
So we'll say this HALPulse equals new HALPulse. Let's take a look and see what's in here. Okay, so far there's nothing in there. It's just implementing Loopable, and it's using the TextTrait. The HALPulse is just two frames going back and forth. It'll be the filled light and the empty light. So it'll just be oscillating between those two states. So Chewy has a little helper called frames. So let's implement that. public frames, frames, and in our constructor we will say this frames equals new frames.
So let's implement that. public frames, frames, and in our constructor we will say $this->frames equals new Frames. Then let's implement onTick. public function onTick, and we'll say $this->onNthTick, so every nth tick, and we're going to say 10. We're just going to pick a number for right now. We're just going to say $this->frames->next(). And frames is a super, super simple class. You set it up, it's got a counter inside. Every time you press next, it increases the counter.
Render Header Section3:19
You set it up, it's got a counter inside. Every time you press next, it increases the counter. You can get the current frame, and if you pass an array of the available frames, so the different frames that you want to show, it will actually show whatever the next frame is and return that to you based on the counter. Great, let's go to our renderer. In our renderer, we've already set up our desired width and height available to us. And when I'm rendering a lot of different components on the page, I like to split out each section into its own method. So we'll say this renderHeader, and we'll pass along the prompt.
each section into its own method. So we'll say this renderHeader, and we'll pass along the $prompt. And then we'll say protected function renderHeader $dashboardPrompt, it's not going to return anything. And so what do we want this to be? On the left-hand side, I want a blinking red light that says good afternoon Dave. On the right-hand side, I want the current date and time, and then I just want this divider line between the header and the content below. So let's do the left half first. Left half is going to be equal to this bold, and we'll say this red.
So let's do the left half first. Left half is going to be equal to this bold, and we'll say this red. We want the light to be red, just like Hal is red. And we're going to do prompt, Hal, pulse, frames, and then get the frame based on this array. So we have the light on and the light off. And then we're just going to concat good afternoon Dave. And that will be our left half. On the right half, we are going to do this, dim, date, Y-M-D-H-I-S. Great.
On the right half, we are going to do this, dim, date, Y-M-D-H-I-S. Great. Left half, pulsing red light, good afternoon Dave. Right half, the date. So we'll say this, line. And what we want is for this to be all the way to the left and this to be all the way to the right. So let's import useAligns from Chewy, and this will give us a method called spaceBetween. And we give it a width. So we'll say this width that we've calculated up top, and we give it the left half and the
And we give it a width. So we'll say this width that we've calculated up top, and we give it the left half and the right half. And then it will space them two out, sort of like a justify-between using flex in CSS. Finally, we want the divider line. So we'll say this line, this dim, string, repeat, divider, this width. Cool. And that should render a header out for us with a blinking red light. Let's see what that looks like. If we run our app, it looks good and the date is changing, but the light is not blinking.
Let's see what that looks like. If we run our app, it looks good and the date is changing, but the light is not blinking and it's because we forgot to register our loopable. So if we come back to our app, we need to register our loopable up here. So we'll say this, registerLoopables, and we will say this, halPulse. And so now this will be part of the loop sequence and increase the frame with every loop. Let's see what it looks like. That's great. It's going a little fast. I think we should maybe slow down the loop a little bit.
It's going a little fast. I think we should maybe slow down the loop a little bit. So by default, it's at 50,000. Let's just double it and say 100,000. Nice. Okay, great. So we've got a header. This all looks good. We've got a blinking light. We've got a live date.
Add Health Display6:37
We've got a blinking light. We've got a live date. So what I'm envisioning is up here, it will show a number, a large number, that will indicate the health of the ship. And then we'll have a couple little bar graphs that will be moving around, and I want these to sort of be randomly moving all the time. On the right-hand side, we're going to have a live chat between Dave and Hal. It's going to be automated, but it'll look like Dave is typing out and Hal is responding. Let's give it a whirl. Let's register a health loopable.
Let's give it a whirl. Let's register a health loopable. So this is going to be a public, and I have a loopable set up called randomValue, and we'll take a look at that in a second. And we'll just call this health. This is going to be the health of the ship. And we'll say this health is equal to new randomValue. And let's give it some parameters here. So the lower limit is going to be 25. The upper limit is going to be 75.
So the lower limit is going to be 25. The upper limit is going to be 75. The initial value is going to be 50. And after we change, let's pause after for 10 ticks. So we're going to let 10 ticks go by before we continue randomizing the numbers. Let's take a look at random value real quick. So we pass in all these parameters. It creates an animatable value from the parameters that you pass in, and then on tick, it just keeps going to random based on the parameters you passed in. We're going to be using this a couple times with a couple different components on this.
keeps going to random based on the parameters you passed in. We're going to be using this a couple times with a couple different components on this dashboard. So we have a health loopable. Let's register it. So now this is now part of the loop. And now let's decide how we're going to render it. So first, as I was talking about before, we're going to have two columns, and I want them divided down the middle. And I want there to be a gray, dim line dividing them with a space on either side of that line
divided down the middle. And I want there to be a gray, dim line dividing them with a space on either side of that line so that the content doesn't go right up to the line. So let's set up some variables to do some calculations to figure out how wide these columns can be. So our columnSpacing is going to be 1. There's going to be 1 space on each side of that dim line. So our columnWidth, which we should set up as a property, columnWidth, is going to be equal to floor of this width divided by 2 minus our columnSpacing times 2, because there's going to be a space on either side of that divider line, minus 1 for the divider.
equal to floor of this width divided by 2 minus our column spacing times 2, because there's going to be a space on either side of that divider line, minus 1 for the divider line. So there's going to be the width divided by 2 minus the spacing minus the actual character width of the divider line. So let's render the health and assign it to a health variable. This renderHealth will pass as a prompt. void function renderHealthDashboardPrompt Oh, it's not void. It's actually going to return a collection. What is our health going to look like?
It's actually going to return a collection. What is our health going to look like? We're going to use the bigNumber method of Chewy. So let's import that. So we say use draws big numbers. And so the lines are equal to this bigNumber, and we're just going to pass it the current value of the animatableValue. So promptHealthValueCurrent. And let's put a title onto it as well. So we'll say lines prepend this bold this cyanShipHealth.
And let's put a title onto it as well. So we'll say lines prepend this bold this cyan ship health. And then let's center all of this horizontally. Return this center horizontally lines this column width because we want to center it within the column. For now, let's just go ahead and print this out. So we'll say health each this line. Okay cool. So we have our blinking light. We have the ship health sort of going back and forth through random numbers and taking.
Add Animated Stat Bars10:37
So we have our blinking light. We have the ship health sort of going back and forth through random numbers and taking a small pause between. We should add a little space here. It's a little tight, but we'll do that momentarily. Let's go to the next component. The next component is going to be three bars, sort of like gauges. And I want the gauges to be moving erratically and randomly and indicating that there's trouble on the ship. So we're going to create three bar loopable components, and we're just going to use the
on the ship. So we're going to create three bar loopable components, and we're just going to use the randomValue again. So we'll say bar1, bar2, bar3. And then let's new those up. This bar1 equals new randomValue. And we'll say the lower limit is 10 and the upper limit is 90. And when you don't pass in an initial value, it'll just randomly pick an initial value based on your lower and upper limit. So now we can say bar2, bar3, and then let's register them with the loopable.
based on your lower and upper limit. So now we can say two, three, and then let's register them with the loopable. This bar one, two, three. Alright, back in our renderer. Let's call this stats. So we'll say these are the ship stats. So we'll say this, renderStats prompt. And we can just copy this down. We're going to do the same thing, and we'll say renderStats, and we're going to return a collection as well.
We're going to do the same thing, and we'll say renderStats, and we're going to return a collection as well. So we've got three bars. Let's have labels for those bars. So we'll say labels are equal to power, shields, and let's just say weapons. These are indicators of how these different things on the ship are doing. And let's give the bars three different colors. The colors are going to be yellow, green, and blue. Great. So let's return a collection of prompt, bar1, value, and then we'll do bar2, bar
Great. So let's return a collection of prompt, barOne, value, and then we'll do barTwo, barThree, and then we're going to map over this collection, map, function, value. And what we want to do is we want to know what the percentage of the barWidth is based on the columnWidth. So we'll say round(value, current / 100 * columnWidth). So this will give us how wide the bar is based on the value versus the columnWidth, how many characters wide it is. Next, let's collect some information about each of these bars. So we'll map again.
Next, let's collect some information about each of these bars. So we'll map again. We'll say function, value, index. We'll return an array. We'll say the value is equal to the value. We'll say the color is equal to the color's index. So it'll grab the color at that index. And then we'll say the label is equal to the label's at that index. So this just cleans it up so we don't have to do it in the next method below. And we're going to map one more time.
So this just cleans it up so we don't have to do it in the next method below. And we're going to map one more time. We're going to say item is going to be equal to, and these are the lines that we're going to print out. So we're going to do this line by line. So we'll say this, bold, item, color. So we're telling it use the method associated with this color, and we'll print out the label. And then the next line is going to be the bar itself. So say this, item, color, and we're just going to str_repeat( the bar character as many times as the item value.
So say this, item, color, and we're just going to string repeat the bar character as many times as the item value. And then we're going to insert one line below that. And then now we've got an array of arrays. Let's just flatten that down to be a flat array. So this should do it. We're collecting the values. We are figuring out how wide the bar is based on the column width. We then extract the color and label information from our above arrays. And then we sort of just print out each of these to the terminal.
We then extract the color and label information from our above arrays. And then we sort of just print out each of these to the terminal. Whoops, I did this one twice. Okay, so it's just label, bar, empty line. So a space between each of them. Okay, so let's come back up and actually render this out. So we have health and stats. Now I would like to put some spacing, maybe two spaces above health, two spaces between health and stats, just to make it breathe a little bit. So let's do that.
health and stats, just to make it breathe a little bit. So let's do that. We have spacing equal to collect, and we'll just do two empty lines. And then we'll say spacing, merge, health, merge, spacing, merge, stats. And then we can say each this line. So we're saying put some spacing, next merge the health lines, another set of spacing, next merge the stats, and then print them out. Let's see what this does for us. Okay, cool. This is starting to come together.
Okay, cool. This is starting to come together. We have a lot of things animating individually right now. And this is good. We have a little bit of space here, which is nice. And these are going well. I want to change one thing about this just because I think it would be cool. I think when it starts to get low, maybe below, say, 40%, I think we should make it red so it indicates sort of a warning. This would just be fun.
it indicates sort of a warning. This would just be fun. So if we come back here and we come back to the color, we can come over here and say if the value is less than this column width times 0.4, then let's just say red. Otherwise use the color defined. Let's see what that looks like. See power starts out red and then it becomes yellow again. Let's see. Oh, okay. So we can see like, oh, there's a warning here and then it'll come back to normal as
Oh, okay. So we can see like, oh, there's a warning here and then it'll come back to normal as it recovers. So this is cool. I like this a lot. So now we've got our left column set up. Next let's set up our chat column.
