Categories

Making a scrolling card list – WotW

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 will work on a list of cards that animate out as you scroll down. The inspiration for this widget comes from the first part of this dribble created by Hiwow and looks like this:

Preparations

For today’s widget we will just be using Vue.js, no animations library, that means we will heavily use Vue’s capabilities.

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

The initial markup

For our app to work we should have the main div with the app id where Vue.js will mount. Having done that, we can start creating the cards, in this case I will only create one because in a few moments we will programmatically create the rest.
Each card will have a placeholder image that will be next to a div that I’ll be calling the card content. This card content displays the title, description and rating data.

For the naming of the classes you might have noticed that I’m using BEM, it will help when styling the cards in the next step.

Styling

Right now we have an image with some ugly test, let’s change that. For starters we will have a light gray background set directly into the `body`.

body {
background-color: #FEFEFE;
}

Then for the card we will declare a predefined height, matching the image height which is 140px. Also we add some details by setting a padding, changing the font and adding a shadow to create the effect of a floating card.

We’re getting there, it’s the turn for the inner elements to be styled.

Both the card image and the card content should have a display: inline-block to be side by side. The width of the image is 100px and also has a small margin to separate it from the text, so the card content will be taking the rest of the width of the card.

The inner text of the card content needs to be aligned to the top, otherwise it won’t look the way we want to. In the case of the title, the default margin that h3 elements have is to big, so we will be setting it to 0.
The card rating container needs to be aligned to the bottom, we will use position: absolute for that to happen. Last but not least, the stars span elements will have different colors depending if a star is “active” or not.

If you have a keen eye, you might have notice a space difference just between the active stars and the inactive ones. This is cause by the space between the two span elements, and it can be removed like this:

Nice, we have a lot of cards, unfortunately the ratings and stars don’t look like we expected.

As you can notice, stars are rendering just like numbers, and the last rating is being printed with more than one decimal digit. Luckily for us, Vue.js has a something called filters that can help us to parse any data the way we want.

Let’s go back to the Vue instance and declare two filters, one will constrain the digits and the other one will convert any number to stars:

Notice that we added scrollPosition to keep track of the window.scrollY property. That will help Vue to do recalculate things when it changes.

Animating cards

In the original dribble, the cards have this disappearing effect when they start getting to the top of the screen. For that to happen we need to calculate the style of each card every time the scrollPosition updates.

The next two methods do all of the math to generate the styles. It might be a bit confusing first, but I’ll do my best to explain them.

First we set a cardHeight constant that has the value of a card including it’s padding and margin. Then taking into account the index of the card, we set to positionY the card position, the first one is 0 the second 160 then the third 320 and so on.

After that we need to know how close to the top is the card, we do it and assign the value to deltaY. We need to start animating the cards when they get to the top of the screen so we should only care when deltaY is less than 0. I clamp it between -160 and 0 because when the deltaY is less than -160 it will be already off the screen.

Finally we just create a dissapearingValue, yValue and zValue that depend on the dY value. The dissapearingValue as the name implies, will make the card fade, so we bind it to the css opacity property. The other two values will help with the transform property, making the card look like it is going behind the other cards.