How To Add Parallax Scrolling to Your Vue.js App

John Au-Yeung - Jan 25 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Parallax scrolling is the effect where the background image scrolls slower than the elements in the foreground, creating an illusion of depth of the page.

Websites often use this for informational pages, where you have some text in the foreground and an image in the background that scrolls more slowly to create a more interesting experience for the user.

https://www.mockplus.com/blog/post/parallax-scrolling-websites has some examples of web pages with parallax scrolling.

With React, it is quick and simple to create the parallax scrolling effect with the Vue-Parallaxy library, located at https://github.com/apertureless/vue-parallax.

In this article, we will make an app that displays a list of images in the background with tags text in the foreground. The images will be provided by the Pixabay API. You can register for an API key at Pixabay.

To start the project, we create the project by running:

npx @vue/cli create photo-app
Enter fullscreen mode Exit fullscreen mode

Then we select ‘Manually select features’ then choose to include Babel and Vue Router.

We need to install Axios to get images from the Pixabay API, BootstrapVue for styling, and Vue-Parallaxy to create the parallax scrolling effect. To install the packages run:

npm i axios bootstra-vue vue-parallaxy
Enter fullscreen mode Exit fullscreen mode

With all the packages installed, we can start building the app. To start, we add a mixin for making the HTTP requests. Create a mixins folder in the src folder, then in the folder, create requestsMixins.js . In the file, add:

const axios = require("axios");  
const APIURL = "https://pixabay.com/api";
export const requestsMixin = {  
  methods: {  
    getImages(page = 1) {  
      return axios.get(  
        `${APIURL}/?page=${page}&key=${process.env.VUE_APP_API_KEY}`  
      );  
    }  
  }  
};
Enter fullscreen mode Exit fullscreen mode

Next in Home.vue , replace the existing code with the following:

<template>  
  <div class="page">  
    <div v-for="(img, i) of images" :key="i" class="parallax-container">  
      <parallax :speed-factor="0.5" direction="down" :parallax="true">  
        <div>  
          <img :src="img.largeImageURL" :alt="img.tags" style="image" />  
          <h1 class="parallax-title">{{img.tags}}</h1>  
        </div>  
      </parallax>  
      <br />  
    </div>  
  </div>  
</template>

<script>  
// @ is an alias to /src  
import { requestsMixin } from "../mixins/requestsMixin";  
import Parallax from "vue-parallaxy";

export default {  
  name: "home",  
  mixins: [requestsMixin],  
  components: {  
    Parallax  
  },  
  data() {  
    return {  
      images: []  
    };  
  },  
  methods: {  
    async getImagesByPage() {  
      const response = await this.getImages();  
      this.images = response.data.hits;  
    }  
  },  
  beforeMount() {  
    this.getImagesByPage();  
  }  
};  
</script>

<style>  
.parallax-container {  
  position: relative;  
  height: 1000px;  
}

.parallax-title {  
  position: absolute;  
  top: 30%;  
  left: 0;  
  right: 0;  
  padding: 20px;  
  color: white;  
  text-align: center;  
}

.image {  
  height: 700px;  
}  
</style>
Enter fullscreen mode Exit fullscreen mode

We include the Vue-Parallaxy component in this component by adding Parallax in the components object. Then we get the images by calling the this.getImages function from the requestsMixin we just created. We call the getImagesByPage function in the beforeMount hook to get the images when the page loads.

In the template, we use the parallax component provided by Vue-Parallaxy to create the parallax scrolling effect. The parallax serves as the container for the parallax effect. We make the speed of the scrolling different from the foreground with the speed-factor prop, we set the direction to down so that it scrolls down. parallax prop is set to true so that we get the different scrolling speed between the foreground and background.

We change the height of the parallax-container divs to the same height of 1000px, and the images to 700px to keep the spacing consistent.

In the component, we loop through the images and show some text from the Pixbay API. We position the text inside the photo by specifying:

<style>  
.parallax-container {  
  position: relative;  
}

.parallax-title {  
  position: absolute;  
  top: 30%;  
  left: 0;  
  right: 0;  
  padding: 20px;  
  color: white;  
  text-align: center;  
}  
</style>
Enter fullscreen mode Exit fullscreen mode

We place the text in the center of the images and change the text color to white.

Next in App.vue , we replace the existing code with:

<template>  
  <div id="app">  
    <b-navbar toggleable="lg" type="dark" variant="info">  
      <b-navbar-brand href="#">Photo App</b-navbar-brand> <b-navbar-toggle target="nav-collapse"></b-navbar-toggle> <b-collapse id="nav-collapse" is-nav>  
        <b-navbar-nav>  
          <b-nav-item to="/" :active="path == '/'">Home</b-nav-item>  
        </b-navbar-nav>  
      </b-collapse>  
    </b-navbar>  
    <router-view />  
  </div>  
</template>

<script>  
export default {  
  data() {  
    return {  
      path: this.$route && this.$route.path  
    };  
  },  
  watch: {  
    $route(route) {  
      this.path = route.path;  
    }  
  }  
};  
</script>

<style lang="scss">  
.page {  
  padding: 20px;  
}  
</style>
Enter fullscreen mode Exit fullscreen mode

We add some padding to the pages with the page class, and we add the BootstrapVue navigation bar to the top of the page. Also, we have the router-view so that we see the home page.

Next in main.js , we replace the existing code with:

import Vue from 'vue'  
import App from './App.vue'  
import router from './router'  
import store from './store'  
import "bootstrap/dist/css/bootstrap.css";  
import "bootstrap-vue/dist/bootstrap-vue.css";  
import BootstrapVue from "bootstrap-vue";

Vue.config.productionTip = false  
Vue.use(BootstrapVue);

new Vue({  
  router,  
  store,  
  render: h => h(App)  
}).$mount('#app')
Enter fullscreen mode Exit fullscreen mode

to add the BootstrapVue libraries and styles to the app so we can use the code in our app and see the styling in the whole app.

Then in router.js , replace the existing code with:

import Vue from "vue";  
import Router from "vue-router";  
import Home from "./views/Home.vue";

Vue.use(Router);

export default new Router({  
  mode: "history",  
  base: process.env.BASE_URL,  
  routes: [  
    {  
      path: "/",  
      name: "home",  
      component: Home  
    }  
  ]  
});
Enter fullscreen mode Exit fullscreen mode

We added the home page route so that we can see the page we built.

Then in the root folder of the project, we add an .env file so store the API key:

VUE_APP_API_KEY='Pixabay API key'
Enter fullscreen mode Exit fullscreen mode

We can use this keep by referencing process.env.VUE_APP_API_KEY like we have in the requestsMixin.js.

Next in index.html , replace the existing code with:

<!DOCTYPE html>  
<html lang="en">  
  <head>  
    <meta charset="utf-8" />  
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />  
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />  
    <link rel="icon" href="<%= BASE_URL %>favicon.ico" />  
    <title>Photo App</title>  
  </head>  
  <body>  
    <noscript>  
      <strong  
        >We're sorry but vue-parallax-scrolling-tutorial-app doesn't work  
        properly without JavaScript enabled. Please enable it to  
        continue.</strong  
      >  
    </noscript>  
    <div id="app"></div>  
    <!-- built files will be auto injected -->  
  </body>  
</html>
Enter fullscreen mode Exit fullscreen mode

to change the title.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .