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

Today we are going to create a password submit form, that validates the user input in real time.
The inspiration comes from this submission created by ramykhuffash and looks like this:

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.
Continue reading

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

This time we are going to create a temperature slider, although it can be used for anything.
The inspiration comes from this submission created by ramykhuffash and looks like this:

Preparations

For today’s widget we will be using Vue.js, and for some animations we’ll use TweenMax. Also we will need a temperature icon, so we’ll be using the one from Font Awesome.

If you want to follow along you can fork this codepen template that already has the dependencies.

Continue reading

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

This time we are going to create a 3D widget that “looks” at the cursor as we move it through the screen.
The inspiration comes from this dribble created by Steven Hanley and looks like this:

Preparations

For today’s widget we will be using Vue.js, and for the “look around” animation we’ll use TweenMax.

If you want to follow along you can fork this codepen template that already has the dependencies.

Continue reading

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

I’ve been quite busy for a couple of weeks, preparing and giving a Vue.js course in Wizeline Academy, but I’m back with another interactive component.

Today we’ll make a list of cards that animate when hovered (or clicked in mobile), the inspiration comes from this dribble created by Kreativa Studio and looks like this:

Continue reading

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

Today’s the turn for a navigation component with four colorful icon buttons.The inspiration comes from this submission and it looks like this:

Preparations

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

We will also use FontAwesome icons, so make sure that you add this link to import them:

<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">

Continue reading

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

Today I’ll be doing a component that changes the width of a two column layout when a button is clicked. The inspiration for this widget comes from this submission and looks like this:

Preparations

Like I’ve been doing before, we will be using Vue.js for the interactions and the GSAP platform for animations. If you use something like codepen you can find them in the libraries.
Continue reading

Welcome to the second installment of the Widget of the Week series.

This time I’ll show you the process to make a submit button that transforms to a loader and then confirms your submission.

The inspiration for this widget is this and looks like this:

Preparations

For today’s widget we will be using vue.js and tweenlite for animations. Both libraries have a cdn link to be imported on any project.

The HTML structure

The markup for this widget is really simple, we just need a container where our Vue instance will mount and inside it, there will be a button and a couple of SVG circles for the loading animation:

<div id="submit-button" class="submit-container">
  <div class="submit-btn">
    <span>Submit</span>    
  </div>
  
  <!--  grey circle  -->
  <svg class="loader-svg">
    <path stroke="#CCCCCC" fill="none" stroke-width="4" d="M25,2.5A22.5,22.5 0 1 1 2.5,25A22.5,22.5 0 0 1 25,2.5"></path>
  </svg>
  
  <!--  green circle  -->
  <svg class="loader-svg">
    <path stroke="#20BF7E" fill="none" stroke-width="4" d="M25,2.5A22.5,22.5 0 1 1 2.5,25A22.5,22.5 0 0 1 25,2.5"></path>
  </svg>
</div>

Now let’s start matching the style of our button with these CSS rules:

.submit-container {
  position: relative;
  margin: 80px;
}

.submit-btn {
  width: 100px;
  color: #20BF7E;
  font-size: 20px;
  font-family: Arial;
  text-align: center;
  text-decoration: none;
  padding: 10px 20px 10px 20px;
  border: solid #20BF7E 4px;
  text-decoration: none;
  cursor: pointer;
  border-radius: 25px;
  transition: background-color .3s, color .3s;
}

.submit-btn:hover {
  background-color: #20BF7E;
  color: white;
}

We now have a button that has a hover animation that swaps colors between the green background and the white font. Also notice the 25px border radius property, it is going to be really important when we need our button to become a circle.

The interaction logic

Before we initialize the Vue instance, I’d like to check what are the states of the button. We can ignore the hover because that’s already solved by our CSS, that leaves us with three states: clicked, loading and loaded. To handle those we can start with something like this:

new Vue({
  el: '#submit-button',
  data: {
    clicked: false,    
    loading: false,
    loaded: false
  }
})

You might ask “Why three booleans and not a single string or number with the 3 values?”, and the reason is because they’re not mutually exclusive, in other words, the button can be ‘clicked’ and also ‘loading’ at the same time.

The click interaction

In preparation for the click animation we need first to create a CSS rule for the button, when it is clicked it transforms into a circle, to be precise a 50px by 50px circle (remember the 25px border radius?). The problem is that it already has a padding declared, and also we need to compensate for the border of the button, so we will need a little bit of math:

.submit-btn {
  ... other rules
  /* more CSS transitions for the props we need to animate */
  transition: width .3s, margin .3s, background-color .3s, color .3s;
}
.submit-btn.round {
  margin-left: 50px;
  border-color: #CCCCCC;
  background: white;
  
  /*  circle should be 50px width & height */
  /* borderLeft + paddingLeft + paddingRight + borderRight  */
  /* 4 + 20 + 20 + 4 = 48 + 2 = 50 */
  width: 2px; 
  /* borderTop + paddingTop + paddingBottom + borderBottom  */
  /* 4 + 10 + 10 + 4 = 28 + 22 = 50 */
  height: 22px;
}

Now we can start binding the button to Vue, we will first bind the click to a method, the round class to a computed property, and also the submit text needs to disappear when we click the button:

...
<div 
  @click="clickedSubmit" 
  :class="buttonClass"
  class="submit-btn">
    <span v-show="!clicked">Submit</span>  
...
```
Then in our js:
``` js
...
methods: {
  clickedSubmit () {
    this.clicked = true
  }
},
computed: {
  buttonClass () {
    if (this.clicked) {
      return 'round'
    }
    return ''
  }
}

Pretty simple right? Now comes the tricky part.

The loading

Just after our button transforms into a circle we need to put on top our SVG circles, why?, because HTML borders can’t be animated the way we need them to, but SVG can!
Now let’s match the position of the circles with this CSS:

.loader-svg {
  pointer-events: none;
  position: absolute;
  top: 0px;
  left: 50px;
  width: 50px; 
  height: 50px; 
  transform-origin: 25px 25px 25px;
}

and then, both SVG circles will have this vue binding, to make them appear when the button starts loading:

  <!--  grey circle  -->
  <svg v-if="loading" class="loader-svg">
    ...
  </svg>
  
  <!--  green circle  -->
  <svg v-if="loading" class="loader-svg">
    ...
  </svg>

We need to know when the button animation ends so we can start the loading animation, according to MDN web docs we can use the ‘transitionend’ event.
To add a listener to that event in Vue, we need to have a reference to the submit button, let’s add this line to our button HTML:

<div class="submit-btn" 
  ref="submit-btn"
  ...
>

Now we can reference it in our clickedSubmit method like this:

...
clickedSubmit () {
this.clicked = true
this.$refs['submit-btn']
.addEventListener("transitionend", this.animateLoader, false);
}
...

this will trigger the animateLoader method when the animation finishes, so let’s create the method:

animateLoader () {
  this.loading = true
  this.$refs['submit-btn']
    .removeEventListener("transitionend", this.animateLoader, false);
  // TODO animate circles
}

That will set the loading flag to true and remove the previously added listener.

Animating the green circle

For the next part we will use a SVG animation trick using the stroke-dasharray and stroke-dashoffset properties.
For the trick to work, the stroke-dasharray must have as a value the circumference of the circle, to calculate it we can go back to our geometry class notebook and see that the formula is pi times the diameter of the circle.
Ours is 50px width, so it will be 3.1416 * 50 = ~157. Also we will bind the stroke-dashoffset to a new Vue data variable:

...
<!--  green circle  -->
<svg v-if="loading" class="loader-svg">
  <path stroke="#20BF7E" fill="none" stroke-width="4" d="M25,2.5A22.5,22.5 0 1 1 2.5,25A22.5,22.5 0 0 1 25,2.5" 
  stroke-dasharray="157" :stroke-dashoffset="loaderOffset">
  </path>
</svg>
...

Now in our Vue instance we will declare, inside the data object, the loaderOffset property and initialize it with the same value 157:

data: {
  clicked: false,
  loading: false,
  loaded: false,
  loaderOffset: 157
}

After doing that, we can start animating the loader with TweenLite.
We use the TweenLite.to() method to interpolate the loaderOffset property from its initial value to zero in two seconds.
When it finishes animating, the onComplete hook will execute the completeLoading method where we set the loading and loaded properties:

...
animateLoader () {
  this.loading = true
  this.$refs['submit-btn']
    .removeEventListener("transitionend", this.animateLoader, false);
      
  // animate the loaderOffset property,
  // on production this should be replaced 
  // with the real loading progress
  TweenLite.to(this, 2, {
    loaderOffset: 0, // animate from 157 to 0
    ease: Power4.easeInOut,
    onComplete: this.completeLoading // execute this method when animation ends
  })
},
completeLoading () {
  this.loading = false
  this.loaded = true
}
...

The loaded state

For the last part we just need to create another CSS rule that will be used when our button is loaded:

.submit-btn.loaded {
  color: white;
  background-color: #20BF7E;
}

and then the buttonClass computed property should handle that case too:

...
buttonClass () {
  if (this.loaded) {
    return 'loaded'
  }
      
  if (this.clicked) {
    return 'round'
  }
     
  return ''
}
...

We already declared the CSS transitions for those properties so we don’t need to do anything else.

And now the final result!

See the Pen Submit button w/confirmation by Eder Díaz (@ederdiaz) on CodePen.0

 

That’s it for the second Widget of the Week.

If you haven’t checked the previous one, here it is.

Also if you want to see a specific widget for next week, post it in the comments section.

This is the first post of a series called Widget of the Week.

As the name implies I’ll be creating every 7 days a widget based mostly on dribbles from UI Movement.

Transforming a gif to a full working HTML control is not as easy as it sounds, so I’ll be also writing about the process and explaining the reason behind the solution for the tricky parts.

Without further ado, this week widget is this toggle control:

Preparing the elements

First we need to identify the parts of the widget, at first sight it looks like we need a container for the control, a background, the ball, and the faces inside the ball.

Continue reading