Minimalistic nav menu with Vue.js and GSAP - WotW

Minimalistic nav menu with Vue.js and GSAP - WotW

Welcome to the Second Season of the Widget of the Week series, where I take gifs or videos of awesome UI/UX components, and bring them to life with code.

Welcome to the Second Season of the Widget of the Week series, where I take gifs or videos of awesome UI/UX components, and bring them to life with code.

It's been a long time since my last post. I had been busy with some projects (that includes having a baby girl), but I'm back with more knowledge and widgets.
Today we are going to create a minimalistic navigation menu, that animates when hovering an option.

The inspiration comes from this submission created by Zhenya Rynzhukand looks like this:

Who is this for?

This tutorial is aimed at front-end developers that want to level up their skills. It is recommended that you have some prior knowledge of HTML, CSS, JS.

I'll be using Vue.js to make the widget, if you're not familiar to this framework these awesome posts can help you get up to speed:

Preparations

For today's widget, we will be using Vue.js, and for some animations, we'll use TweenMax. If you want to follow along you can fork this codepen template that already has the dependencies.

Matching the look

The first thing I want to do with this component is to match the design. probably this will be the easiest part since there are no complicated graphics that will need SVG or icons.

Inside our app node, I will start by creating the markup for the menu. Since it is going to be a navigation menu we should use the semantically correct HTML nodes:

<!-- HTML -->
<div id="app">
  <nav>
    <ul>
      <li class="menu-item">About</li>
      <li class="menu-item">Works</li>
      <li class="menu-item">News/Blog</li>
      <li class="menu-item">Contact us</li>
    </ul>
  </nav>
</div>

I'm adding class="menu-item" to style the container and the items of the menu in the next step. Also id="app" will be used later to add functionality and animation with Vue.js.

The menu is looking really ugly right now, let's change that:

/* CSS */
body {
  background-color: #f1f0e9;
}

.menu-item {
  font-size: 5em;
  list-style: none;
  text-transform: uppercase;
  font-family: sans-serif;
  text-align: center;
  cursor: pointer;
}

Now we should have something like this:

Let me explain the most crucial rules...

  • list-style: none; is helping to remove the bullets from the list.
  • text-transform: uppercase; is useful when we don't want to explicitly "YELL" at users with screen readers, and we just want to show uppercase characters for design purposes.
  • cursor: pointer; makes the mouse behave like each of the elements was a link.

Set up Vue.js

Before getting into more details let's add Vue.js to the component to be able to render dynamically the menu items.

// JS
const itemsList = ['About', 'Works', 'News/Blog', 'Contact us']

new Vue({
  el: '#app',
  computed: {
    menuItems() {
      return itemsList.map((item, index) => {
        return {
          label: item
        }
      })
    }
  }
})

I'm adding the elements as computed properties of a constant, instead of data inside my component because I need to bind them to my template, but they aren't going to change over time.
Now that we have the items as a list, and they are ready to be used as a computed property, we can simplify our template to render all menu items with a v-for:

<!-- HTML - inside div > nav > ul -->
<li v-for="(item, index) in menuItems" :key="`item-${index}`" class="menu-item">
  {{item.label}}
</li>
<!-- remove all other "li" elements -->

Right now we should have the same elements, just bound to our Vue.js instance.

Hovering and animating

The menu animations in the reference can be broken down into two, the first one consists of moving the menu item to the left and the second one is the characters flipping over.

Let's start with the first one, moving the menu below the mouse cursor to the left. For that we will add a @mouseover event to the menu items that will trigger a function called selectedItem() that we haven't declared yet:

<!-- HTML inside nav > ul -->
<li
  v-for="(item, index) in menuItems"
  :key="`item-${index}`"
  @mouseover="selectItem(index)"
  class="menu-item"
>
  {{item.label}}
</li>

Now for the selectItem() method, we want to keep track of the hovered item so we will add inside the component data a selectedItem variable. This property will start at -1 for no item selected and will change it's value to the index of the selected button when hovered.

// JS
new Vue({
  el: '#app',
  data: {
    selectedItem: -1
  },
  methods: {
    selectItem(id) {
      this.selectedItem = id;
    }
  },
  // ... the rest of our component

To see the selectedItem changing you can add in your template the next line:

<!-- HTML after </nav> -->
<p>
  selectedItem: {{ selectedItem }}
</p>

Knowing which button is currently selected will let us add a class to "move" our button. To do this we can add a selected property to our computed menuItems like this:

// JS inside computed
menuItems () {
  return itemsList.map((item, index) => {
    const isSelected = this.selectedItem === index;
    return {
      label: item,
      selected: isSelected
    }
  })
}

and in the HTML make use of that new property:

<!-- HTML inside nav > ul -->
<li
  v-for="(item, index) in menuItems"
  :key="`item-${index}`"
  @mouseover="selectItem(index)"
  :class="{'selected': item.selected}"
  class="menu-item"
>
  {{item.label}}
</li>

Notice there are two class attributes, they concatenate instead of overriding each other. When "item.selected" is "true", that menu item will have both the "menu-item" and the "selected" classes.
Let's add the CSS class to handle the movement:

/* CSS */
.menu-item {
  /* ... previous styles */
  transition: margin-left 0.5s ease-out, opacity 0.5s ease-out;
}

.selected {
  margin-left: -90px;
}

The transition attribute indicates that any change to that value should be animated.
We are almost done with this part, there's something missing tho. After moving our mouse outside all of the elements, the last one stays selected, and we don't want that. To fix that we can use the @mouseleave event:

<!-- HTML inside nav > ul -->
<li
  v-for="(item, index) in menuItems"
  :key="`item-${index}`"
  @mouseover="selectItem(index)"
  @mouseleave="selectItem(-1)"
  :class="{'selected': item.selected}"
  class="menu-item"
>
  {{item.label}}
</li>

Adding the arrow and dim other items

Before getting into the animation of the characters there are still a couple of details, there should be an arrow appearing for the selected item, and also non-selected items should be dimmed or semi-transparent.

Let's quickly add an arrow character to our menu items.

<!-- HTML inside nav > ul -->
<li
  v-for="(item, index) in menuItems"
  :key="`item-${index}`"
  @mouseover="selectItem(index)"
  @mouseleave="selectItem(-1)"
  :class="{'selected': item.selected}"
  class="menu-item"
>
  {{item.label}}
  <span class="arrow">βž”</span>
</li>

The arrow looks a little different from the reference since it is a regular βž” character instead of a vector that matches exactly the design, but it is good enough for our purposes.
We want to hide all the arrows unless they are a child of a selected item, we can do this with CSS rules similar to what we did before with the selected items:

/* CSS */
.menu-item > .arrow {
  opacity: 0;
  transition: opacity 0.7s ease-out;
}

.selected > .arrow {
  opacity: 1;
}

Now that the arrows appear and disappear, let's dim the unselected items. We can compute the dimmed items just as we did with the selected one:

// JS inside computed
menuItems () {
  return itemsList.map((item, index) => {
    const isSelected = this.selectedItem === index;
    const otherButtonIsSelected = this.selectedItem !== -1
    return {
      label: item,
      selected: isSelected,
      dimmed: !isSelected && otherButtonIsSelected
    }
  })
}

... add the .dimmed class to the items in our HTML:

<!-- HTML inside nav > ul -->
<li
  v-for="(item, index) in menuItems"
  :key="`item-${index}`"
  @mouseover="selectItem(index)"
  @mouseleave="selectItem(-1)"
  :class="{
      'selected': item.selected,
      'dimmed': item.dimmed
    }"
  class="menu-item"
>
  {{item.label}}
  <span class="arrow">βž”</span>
</li>

... and last but not least, create the .dimmed CSS rule:

/* CSS */
.dimmed {
  opacity: 0.3;
}

We are getting closer to the final product.

Character flipping animations

Flipping each of the menu item characters is probably the most interesting and complicated part in this widget. We can't just flip over the whole menu, each one of the characters should be individually flipped over horizontally (scaleX: -1).

To be able to "control" each and every character we need to split the menu characters:

// JS inside computed
menuItems () {
  return itemsList.map((item, index) => {
    const isSelected = this.selectedItem === index;
    const otherButtonIsSelected = this.selectedItem !== -1
    return {
      label: item,
      selected: isSelected,
      dimmed: !isSelected && otherButtonIsSelected,
      chars: item.split('')
    }
  })
}

...with the chars property we can now render each character inside a <span> node:

<!-- HTML inside nav > ul -->
<li
  v-for="(item, index) in menuItems"
  :key="`item-${index}`"
  @mouseover="selectItem(index)"
  @mouseleave="selectItem(-1)"
  :class="{
      'selected': item.selected,
      'dimmed': item.dimmed
    }"
  class="menu-item"
>
  <span
    class="char"
    v-for="(char, charIndex) in item.chars"
    :key="`char-${charIndex}`"
    :ref="`char-${index}-${charIndex}`"
    >{{char}}</span
  >
  <span class="arrow">βž”</span>
</li>

Take note of the "ref" attribute, it will help us to "reference" all of those characters using the "index" of the menu item along with the "charIndex".
At this moment nothing should have changed visually, but we should have just separated the menu into characters.

We will add a couple of constants that will help us to read better the next part of our code

// JS below const itemList declaration
const LEFT = -1
const RIGHT = 1

After that, whenever the selectedItem changes, we want to start animating correctly all of the characters. We will loop through each menuItem and depending on if they're selected or not we will flip them LEFT or RIGHT:

// JS inside methods
selectItem(id) {
  this.selectedItem = id;

  this.menuItems.forEach((item, index) => {
    const direction = item.selected ? LEFT : RIGHT;
    this.animateChars(index, item.label.length, direction);
  })
},

The animateChars() method is not declared yet, but now it shouldn't be hard to create since we have the index of the item, the number of characters that the item has and the direction to flip the letters:

// JS inside methods
animateChars (id, charLength, direction) {
  for(let c=0;c < charLength; c++){
    const refId = `char-${id}-${c}`;
    const char = this.$refs[refId];
    TweenMax.killTweensOf(char);
    TweenMax.to(char, 0.5, {scaleX: direction});
  }
}

In this method we are basically getting the reference for each of the characters, killing the animation the currently have (if any) and then flipping the character to the direction we previously calculated.
It seems that everything should be looking just like the reference, but it doesn't 😰.

If we look into the console we will see that the characters are being transformed correctly, TweenMax is changing their transform matrix just as I expected:

After scratching my head for a while and looking for any bug I found out that the <span> nodes we are animating, by default have their displayproperty set to inline. Transforms don't seem to work with that type of display property so let's take advantage of the .char class that we added to those elements to fix this:

.char {
  display: inline-block;
  min-width: 0.3em;
}

The "min-width" property was added to make the text look correctly, when adding the "inline-block" display property, the characters like spaces lose their width.
And now the final result!

I know there is room for improvement, sometimes animations don't run smoothly because of all the different elements rendering. If you have any ideas on how to improve this I'd love to read them in the comments.

And that’s it for this Widget of the Week.

If you're hungry for more you can check other WotW:

What are the differences between the various JavaScript frameworks? E.g. Vue.js, Angular.js, React.js

What are the differences? Do they each have specific use contexts?

What are the differences? Do they each have specific use contexts?

Ember.js vs Vue.js - Which is JavaScript Framework Works Better for You

Ember.js vs Vue.js - Which is JavaScript Framework Works Better for You

In this article we will discuss full details and comparison of both Ember.js and Vue.js

JavaScript was initially created to work for web applications. But today they have become the favorite of mobile app developers. Most of the developers prefer to work with frameworks based on JavaScript. It simplifies coding. You can use JavaScript with almost any framework.

The use of a particular framework will decide how easy and fast it is to create the app. So, you must choose the best one suited for the app that you are planning to build. You must make a wise choice so that you benefit in the end. Among the crowded market, two of the frameworks stand out. We will make a comparison between Ember.js and Vue.js.

Why Do You Select A Particular Framework?

Before we start comparing the two frameworks, we should understand the factors that lead to the choice of a framework. Each developer chooses a framework before he or she goes to work on an app. Let us see the reasons for the selection.

● The codes must be easy to understand and transparent.

● The framework should give the maximum power with the least amount of coding.

● The framework should provide a well laid out structure to work on.

● Does the framework support an in-built router or an external plug-in router?

● The framework should be able to transfer more data on a full page-load so that it becomes a single-page app. A single-page app is more beneficial for the application.

● In single page architectures if there is a need for users to share links to sub-screens within the interface, then the framework should have the capacity to route based on the URL.

● A tighter template option can help in enabling two-way binding.

● The framework should not conflict any third-party library.

● Testing the codes inside the framework should be easy.

● The framework should provide the HTTP client service for AJAX calls

● The documentation is essential. It should be complete and up-to-date.

● The framework should be compatible with the latest version of the browser.

● The framework has to fulfill the above conditions for easy construction of the app. You must ensure that the framework you choose meets the conditions.

Vue.js Explained

Developers are always looking at new frameworks to build their apps. The main requirements are speed and low cost. The framework should be easy to use by even new developers. You should be able to use it at low cost. Other considerations are about simple coding, proper documentation, etc.

Vue.js combines a lot of good when it comes to software language for web app development. The architecture of Vue.js is easy to put in use. The apps developed using Vue.js are easy to integrate with new apps.

Vue.js is a very lightweight framework. It makes it fast to download. It is also much faster than other frameworks. The single-file component nature of the framework is also beneficial. The size has made it very popular.

You can further decrease weight. With Vue.js you can separate the template-to-virtual DOM and compiler. You can only deploy the minified and zipped interpreter which is only 12 KB. You can compile the templates in your machine.

Another significant advantage of Vue.js is that it can integrate easily with existing applications created with JavaScript. It will make it easy for using this framework to make changes to applications already present.

Vue.js also integrates easily with other front-end libraries. You can plug in another library and make up for any deficiency in this framework. This feature makes this tool a versatile one.

Vue.js uses the method of rendering on the streaming-side server. You can render your component and get a readable stream. You can then send this to the HTTP server. It makes the server highly responsive. Your users will get the rendered content very quickly.

Vue.js is very SEO friendly. As the framework supports server-side rendering, the views are rendered directly on the server. The search engines list these.

But the most important thing for you is the ease with which you can learn Vue.js. The structure is elementary. Even new developers will find it easy to use it to build their apps. This framework helps in developing both small and large templates. It helps to save a lot of time.

You can go back and check your errors very easily. You can travel back and inspect all the states apart from testing your components. It is another important feature as far as any developer is concerned.

Vue.js also has very detailed documentation. It helps in writing your applications very quickly. You can build a web page or app with the basic knowledge of HTML or JavaScript.

● Vue.js has pure architecture. It helps in integration with other apps

● Vue.js is lightweight and fast. It can be made lighter by deploying only the interpreter

● You can separate the compiler and the template-to-virtual DOM.

● Due to smooth integration, you can use this to make changes to existing apps

● To make up for any shortfall, you can plug-in any library and makeup.

● As Vue.js uses streaming-side server rendering, your users can get quick responses.

● The server-side rendering also helps in being ranked higher by search engines.

● It has a simple structure. Easy to use for any new developer

● You can go back and check and correct your errors.

● You can check all the existing states.

● Detail documentation also helps build the web page or application very quickly.

Ember.js Decoded

Ember.js is an MVVM model framework. It is open-source software. This platform is mostly used for creating complex multi-page applications. It maintains up-to-date features without discarding any of the old features.

With this framework, you have to follow the architecture of the framework strictly. The JS framework is very tightly organized. It reduces the flexibility that other frameworks might offer.

There is a very refined and developed control system for its platforms and tools. You can integrate it with the new version with the tools provided. There is strict guidance about avoiding outdated APIs.

You can understand Ember’s APIs easily. They are also easy to work. You can make use of highly complex functionalities simply and straightforwardly.

The performance is better as similar jobs are processed together. It creates batches of similar bindings and DOM updates to improve the performance. It means that the browser needs to process them in one go. It will avoid recomputing for each task, wasting a lot of time.

You can write the codes in a simple manner and modules. You can use any of Ember’s APIs. It is possible due to the presence of Promises everywhere.

Ember comes with a well-written guide. The API is recorded in a useful manner. It is a front-end framework that is loaded. Ember has a router, pipeline, services, etc. of its own.

The basis for views, controllers, models, and framework is the Ember Object Model. All components come from the same objects. The framework is firm and steady. The reason is that all elements have similar jobs and characteristics.

Ember has made the general application, organization, and structure clear so that you don’t make any mistakes. You will have no chance to complicate the application unnecessarily. If you have to go out of the defined limits, you will have to force your way out.

The language used for templating in Embers is Handlebars. This language helps Embers to keep its logic out of view. The clean syntax of Handlebars makes it easy for you to read and understand the templates. Handlebar templates are faster to load.

Another advantage you gain from Handlebar is that you don’t have to update your template every time you add or remove data from the page. It will be done automatically by the language itself.

A community that is continually improving the framework supports Ember. They are updating the framework with the latest technology. They also make sure that backward compatibility is possible.

● Ember.js is an open-source MVVM model framework suitable for complex multiple-page applications.

● It offers both the latest and old features.

● It has a very tightly structured framework which doesn’t offer much flexibility

● A very refined control system helps you to integrate with new versions without any problem.

● There is strict guidance about avoiding outdated API versions.

● Ember’s APIs help you to use complex functionalities in a simple manner

● There is no recomputing for each task as the framework allows the browser to do similar functions together.

● Promises allow you to write modular and straightforward code using any API of Ember.js.

● Ember.js is a fully loaded, front-end framework.

● The framework is stable because all components have the same functionalities and properties.

● It has well-defined limitations which will prevent your complicating your application

● Handlebars, the language used by Ember.js allows you to read and understand templates easily. It also helps to load the templates faster.

● Handlebars will ensure to update the template every time you add or remove data.

● Ember.js has an active community that updates the framework regularly and facilitates backward compatibility.

A Comparison Between Ember.js And Vue.js

This article intends to compare the features of both frameworks. Let us see how the characteristics of these frameworks compare. It will help you to make use of the right framework for your web application.

When you need a modern engine for an old application, it is Vue.js which will help you. It combines the best properties of other frameworks. Vue.js is a developing framework. A ready-to-use library of interface elements does not exist. However, many third-party libraries can help you.

Ember.js offers you a well-organized and trustworthy framework. When the development team is big, this is the framework that suits best. It allows everyone to understand the written code and contribute to a common project. The technology will be up-to-date, and the platform will be stable.

Vue.js can help you use the syntax of different kinds. It helps in writing the codes with ease. It is also an SEO friendly framework. Ember is a fully loaded front-end framework and can help you develop the applications very fast. But it is not suitable for developing small projects.

It is not easy to say this is better than that. It will depend on what kind of project you have undertaken. Both have their pluses and minuses. The below table will help in a better comparison.

Final Thoughts

It is not easy to conclude as to which is better. It all depends on the application that you want to develop. Both frameworks are developing. Both are getting updates. Both the communities are working on the frameworks.

While Vue.js is more comfortable for writing codes, Ember is a full-stack framework allowing the development of apps very fast. It is suitable for big projects. It is too complicated to be used for smaller projects.

We hope you had a great time reading this article. If you’ve any questions or suggestions related to this blog, then feel free to ask them in the comment section. Thank You.!