Creating an animated wishlist component for Vue Storefront

I was browsing the ASOS website the other day and noticed they have a really nice animation when you click the icons to save some items for later:

ASOS “saved items” icon animation

So I was wondering if it would be easy to replicate this in Vue Storefront, after all it’s mostly just CSS animations with some JS sprinkled on top, and it turns out it was very simple.

1. Create the component structure

There’s a wishlist component already in the Vue Storefront core, we could work directly on top of that but it’s probably a better idea to create a new one based on this because we’ll be changing quite a few things.

Let’s start by copying the contents of src/themes/default/components/core/blocks/Wishlist/AddToWishlist.vue into a new file, let’s call it AddToWishlistButton.vue and place it in the src/themes/default/components/theme/blocks/Wishlist/ folder.

We can strip a lot of the original component’s code and replace it with our custom CSS styles, initially it will look something like this:

Thanks to the magic of the internet I was able to use most of the original button’s code (like the SVG icon and animation keyframes) but also changed a few things here and there to make it look better, the main ideas so far are:

  • Using :class="{ active: isOnWishlist }" to toggle an active class on the element that will trigger the animation when the item is added to the wishlist.
  • The CSS animation is scaling the icon up and down and the custom cubic-bezier makes it look more like a “heartbeat”. I’m also adding a transition to the SVG’s fill property so the icon slowly fills up instead of changing all of a sudden like on the original.

2. Import in product grid

Now to include this in our product grid we need to find the src/themes/default/components/core/ProductTile.vue component and import our custom wishlist button there:

I removed the irrelevant parts of the code but all we’re doing here is importing our custom component and including it in the markup, as of Vue Storefront 1.7 the wishlist component can be lazy loaded so we can use a dynamic import and a named webpack bundle to split the code into its own file so performance is not badly affected.

I also added some custom CSS to position the wishlist button in the lower right portion of the product image, so far it looks like this:

Initial implementation of wishlist button

Looks pretty good as they are but they’re still missing the nicest touch, the floating hearts 💕

3. Add floating hearts animation

Looking at the original button the hearts are rendered using two <span> tags and another custom animation that changes their opacity and translate properties so they’re moved from the centre of the icon to a random point away from it which is set using CSS variables (or custom properties).

We’ll ignore the randomly generated points for now and get the animation working with fixed values. All we need for now are a couple <span> tags and some custom CSS:

What we’re doing now is styling our new <span> tags so they look like small hearts (using an SVG background) and position them in the middle of the big heart with position: absolute and opacity: 0. Then, when the active class is triggered, we use our custom animation to change the opacity and translate values based on the hard coded CSS values we set in the markup.

I tweaked the original animation because I though the original started too soon, I felt it would look nicer if it would look as if the smaller hearts were getting pushed out to the sides from the weight of the bigger heart coming down.

Will most people notice? Probably not. Was it a waste of time? Most certainly. But I thought it was a nice touch 👌🏽

Now our wishlist button looks a lot better:

Floating hearts animation

4. Randomise the floating hearts animation

Almost there! Now we need to find a way of randomising the small heart’s final position so they always move differently.

We’re already using CSS variables so it’s just a matter of changing the hardcoded values to randomly generated ones. Now, I’m not sure what the original logic for these are but I think that picking a random value from 5 to 20 pixels for both X and Y coordinates should do the trick.

We changed the hard coded coordinates with a custom method that returns a random number from 5 to 20, and also give it a 50% chance of changing the number to a negative value so the small hearts don’t always float away in the same direction.

We can now sit back and enjoy our new creation:

Randomised floating hearts animation

The best part is that since we’re leveraging strategies like code splitting and animating cheap CSS properties like opacity and transform, it makes our custom component not only look good but also perform quite well.

I created a repo with the code in GitHub, feel free to have a look and submit PRs or report any issues. 👋🏽