Creating an animated sidebar component with Vue and Vuex
In this tutorial, we’ll use a Vuex action to trigger a state change in an application, causing a sidebar to animate in and out of view.
If you want to just dive right into the code, here is the repo for this tutorial.
You can also see a live example here.
I start pretty much every project using the Vue CLI to create a new application with the webpack template. If you don’t have the CLI installed, well then install it, silly.
$ npm install -g vue-cli
After installing the CLI, you can create a new project using the webpack template.
$ vue init webpack <project-name>
$ npm install --save vuex gsap
Cool, we’re setup. Next, lets make a very simple Vuex store with a UI module.
First, lets create a Vuex module to keep all of the actions and mutations in one place. Modules are a great organizational tool for applications that have to manage a lot of state. You might be thinking why create a module for just one action? Well, because I want to show off. Also, I think its good practice to use them because adding actions and mutations to modules is incrementally easier.
The UI module is pretty simple. We set the initial
sidebarOpen state to false. This will be the boolean value that will change, triggering the Sidebar to toggle between open and closed. We have a
toggleSidebar action which we will dispatch when a Sidebar toggle button is clicked. When
toggleSidebar is dispatched, it will commit a mutation which will change the
sidebarOpen state from true to false, or from false to true.
Next, we’ll create the actual store.
You can see we import the UI module and add it as a module in the exported
Vuex.Store object. Now, all we have to do is add it to our Vue instance.
Easy peasy. Now, we’ll have access to the
$store in our Vue components so we can dispatch the
toggleSidebar action and also read the
App.vue looks like this:
Pretty straight forward — I’ve created two components, the
Sidebar and the
SidebarToggle. Lets take a look at the toggle first.
We have a computed property,
open, which is read from the
$store . Remember, this is initially set to false. We also have a method
handleClick which is called whenever the button is clicked. This dispatches the
toggleSidebar action which in turn mutates the Vuex store and changes
sidebarOpen to true.
You’ll also notice that the button has an array of classes. If
open is true, it gets the active class and the button class. The active class rotates the button 45 degrees and changes the buttons background color. Note: I’ve excluded the style tag from the above Gist for brevity’s sake.
Simple enough. The last piece of the puzzle is the actual Sidebar component.
When the sidebar is mounted, we use GSAP’s
Set method to translate the x property of the sidebar to equal the width of the sidebar itself. This pushes the sidebar out of view. Note that we give it an initial x transformation and transform the x to 0 to show the sidebar. This gives use a slight performance increase, based on something called the FLIP principle.
Again, just like in the
SidebarToggle component we see a computed value which reads the
sidebarOpen value out of our store.
Finally, we have a watcher. The watcher does exactly what it sounds like it should do — it watches a specific value and then does something when that value changes. In this case, our watcher is watching the computed
open value, which is tied to our application store. When the user clicks the
SidebarToggle button, it triggers a state mutation which the
Sidebar component is watching. When the value changes, we used GSAP’s TweenMax to either animate the x translation value to 0 (show the Sidebar) or to the Sidebar’s width (hide the sidebar).
That’s it! Huzzah, we have a working sidebar.
Using Vuex to manage UI state makes it easy to handle UI transitions reliably. The more UI elements that transition in and out, the more valuable Vuex becomes. Imagine if you have a modal component that, when opened, requires all sidebars or drawers to be closed. All you need to do is write your mutation for that action to set the state of all the other elements to be closed, and each component will be notified of the state change and animate appropriately.