(Original repo and files https://github.com/DidoMarchet/starter-kit-nuxt-locomotive-scroll)
❤️ Every one loves smooth scrolling!
💤 But sometimes working with Javascript frameworks and DOM can be boring and love fades away.
📦 With this simple starter kit you can have fun with Locomotive Scroll and Nuxt without giving it a second though.
Table of content:
You can try this starter kit by cloning this repo and running:
# install dependencies
$ npm install
# run dev enviroment
$ npm run dev
# generate static project
$ npm run generate
Plugin
First of all we setup the plugin enabling Locomotive Scroll instance works globally both in our component and for your own purposes.
In /LocomotiveScroll/plugin/index.js
we create the plugin:
import LocomotiveScroll from 'locomotive-scroll'
import 'locomotive-scroll/dist/locomotive-scroll.css'
const install = (Vue) => {
Vue.prototype.LocomotiveScroll = LocomotiveScroll
}
export default install
if (typeof window !== 'undefined' && window.Vue) {
window.Vue.use(install)
if (install.installed) {
install.installed = false
}
}
After the setup, it will be used in /plugins/client.js
.
/plugins/client.js
works with mode: 'client'
in the Nuxt plugins configuration .
Component
This component is an useful wrap for our Locomotive Scroll implementation.
Below are the main steps of the implementation.
Complete code can be found here /LocomotiveScroll/component/index.js
.
Markup
<div
v-locomotive="{ options }"
class="js-locomotive"
>
<slot />
</div>
The v-locomotive
directive gets access to low-level DOM.
It takes one argument options
.
options
is a computed obtained merging the defaultOption
data property with the gettedOptions
prop.
defaultOption
and gettedOptions
contain the Locomotive Scroll instance options.
computed: {
options () {
// this.defaultOptions = { smooth: true }
// this.gettedOptions = { offset: ['30%',0], direction: 'horizontal' }
return { ...this.defaultOptions, ...this.gettedOptions }
}
}
Through the slot
element we're able to pass content to the component from each page.
Directive
directives: {
locomotive: {
inserted (el, binding, vnode) {
vnode.context.locomotive = new vnode.context.LocomotiveScroll({ el, ...binding.value.options })
vnode.context.locomotive.on('scroll', (e) => {
vnode.context.onScroll(e)
vnode.context.$emit('scroll')
})
vnode.context.$emit('init')
},
unbind (el, binding, vnode) {
vnode.context.locomotive.destroy()
vnode.context.locomotive = undefined
}
}
}
In the inserted
hook we create the new instance of Locomotive Scroll from the plugin previously created and we assign it to locomotive
data property.
The inserted
hook guarantees the parent presence.
Once initialized we listen to scroll event.
Each time scroll event is fired we call onScroll
method.
onScroll
takes as parameter the scroll instance and uses this data to fill the store (/store/app.js
) making the state of the scroll accessible and usable in all our application.
methods: {
onScroll (e) {
if (typeof this.$store._mutations['app/setScroll'] !== 'undefined') {
this.$store.commit('app/setScroll', {
isScrolling: this.locomotive.scroll.isScrolling,
limit: { ...e.limit },
...e.scroll // x, y
})
}
}
}
Implementation
Before using our component in the page we declare it globally in /plugins/both.js
.
/plugins/both.js
is called in the Nuxt plugins configuration.
Once the plugin is global we can use it in our page or components in this way (/pages/index.vue
):
<template>
<LocomotiveScroll
ref="scroller"
:getted-options="{
offset: ['30%',0],
direction: 'horizontal'
// Other options
}">
<!-- My Content:
Html elements, Components...
-->
</LocomotiveScroll>
</template>
You can access to locomotive scroll instance using this.$refs.scroller.locomotive
.
Gotchas
Reactive elements alter the state of the application and DOM's elements could change.
This changes can take place in nested components and updating Locomotive Scroll could be complex.
We can use the $nuxt
helper and emit a global event
this.$nuxt.$emit('update-locomotive')
and listen it in the mounted
hook in /LocomotiveScroll/component/index.vue
component:
mounted () {
this.$nuxt.$on('update-locomotive', () => {
this?.locomotive?.update() // ?. is the Optional Chaining operator (https://www.joshwcomeau.com/operator-lookup?match=optional-chaining)
})
}
Examples
Basic Scroll
https://starter-kit-nuxt-locomotive-scroll.netlify.app/
Horizontal Scroll
https://starter-kit-nuxt-locomotive-scroll.netlify.app/horizontal-scroll/
Gsap Scroll Trigger
https://starter-kit-nuxt-locomotive-scroll.netlify.app/scroll-trigger/
Image Loads
https://starter-kit-nuxt-locomotive-scroll.netlify.app/image-loads/
On Call Function
https://starter-kit-nuxt-locomotive-scroll.netlify.app/on-call/
Thanks
If you find this repo useful and you saved time, well... let's take a coffee together!
Top comments (16)
Thank you, thank you, thank you! I've tried to implement Locomotive Scroll in two other nuxt projects and ended up dumping it because of various errors I couldn't get past. This seems pretty straight forward. Can't wait to give it a try.
Hi, thanks! Let me know !
Unfortunately I get the same results as all the other times I've attempted to use this library. (mentioned in this issue: github.com/locomotivemtl/locomotiv...). Based on my days of searching for solutions, It seems like vue based web apps have these types of problems, that unfortunately no one has posted solutions to. ¯_(ツ)_/¯
Are you loading contents after the mounted hook?
I was fetching the content via
asyncData
in a page, then was callingthis.$nuxt.$emit('update-locomotive')
inside the mounted hook on the page to attempt to update the locomotive instance. I've tried this method of updating the instance before, and have never had any luck unfortunately. The issue is that the footer, and a few sections above the footer, are always hidden, or there is a huge amount of whitespace under the footer.Hi, If you want, share with me a simplified version of the implementation of the starter-kit and the code with the problem.
I'll check as soon as I have a moment. Have a nice day!
Thank you for this great post. I only run into one Problem:
In my nuxt layout i have the nuxt component and a footer. Now if i wrap a page in the LocomotiveScroll component it will only calculate the height of the page and leaves my footer out. The result is that im not able to scroll to my footer beacause its height is not beeing respected in the locomotive calculation.
Do you have any good solution exept for just wrapping the whole layout in the Locomotive component?
Hi,
can you share the code ?
Have a nice day,
Davide
I reproduced my Problem.
Here is the Code:
github.com/davidmarkl/locomotive-s...
And here is it in production:
jolly-murdock-e4a35b.netlify.app/
Hi,
the issue is related to the css.
If you remove the
overflow: hidden
from html and body and you set as sizes{
....
height: 100vh;
overflow: hidden;
}
to
.js-locomotive
you have the locomotive wrapper and your footer.But the page will have two scrollbar.
I think It's better use it ad a wrapper.
Have a nice day,
Davide
Cool! I've worked with Locomotive/Gsap/Nuxt for the last 2 months.. And sometimes it's not easy to get into the way they work! So, Good Job!! :)
JTK, if this is related to NuxtJs only, you should inject the LocomotiveScroll library in the plugin: nuxtjs.org/docs/2.x/directory-stru...
Hi,
thanks!
Locomotive Scroll is already injected into the plugin.
You can initialize Locomotive Scroll instances
where you want as many times as you want using:
const myLocomotive = new this.LocomotiveScroll({ el, ...options })
whereel
is your DOM element andoptions
the instance options.Is this what you were asking!?
i follow your steps. i facing something sass loader issue as below. I'm trying to solve but will be good for me if you help me solve.
× Client
Compiled with some errors in 9.24s
√ Server
Compiled successfully in 8.19s
ERROR Failed to compile with 1 errors friendly-errors 20:36:42
ERROR in ./LocomotiveScroll/component/index.vue?vue&type=style&index=0&lang=scss& friendly-errors 20:36:42
Module build failed (from ./node_modules/sass-loader/dist/cjs.js): friendly-errors 20:36:42
TypeError: this.getOptions is not a function
at Object.loader (F:\xampp_7.3.28\htdocs\jarawarefront\node_modules\sass-loader\dist\index.js:25:24)
friendly-errors 20:36:42
@ ./node_modules/vue-style-loader??ref--7-oneOf-1-0!./node_modules/css-loader/dist/cjs.js??ref--7-oneOf-1-1!./node_modules/vue-loader/lib/loaders/stylePostLoader.js!./node_modules/postcss-loader/src??ref--7-oneOf-1-2!./node_modules/sass-loader/dist/cjs.js??ref--7-oneOf-1-3!./node_modules/vue-loader/lib??vue-loader-options!./LocomotiveScroll/component/index.vue?vue&type=style&index=0&lang=scss& 4:14-382 14:3-18:5 15:22-390
@ ./LocomotiveScroll/component/index.vue?vue&type=style&index=0&lang=scss&
@ ./LocomotiveScroll/component/index.vue
@ ./plugins/both.js
@ ./.nuxt/index.js
@ ./.nuxt/client.js
@ multi eventsource-polyfill webpack-hot-middleware/client?reload=true&timeout=30000&ansiColors=&overlayStyles=&path=%2F__webpack_hmr%2Fclient&name=client ./.nuxt/client.js
friendly-errors 20:36:42
i Waiting for file changes 20:36:43
READY Server listening on localhost:3000
Hi,
unfortunately i cannot replicate your issue.
Maybe you need to update something like node version or npm version?
Have a nice day and regards,
Davide
Wow, thanks!
The exact thing I was looking for, especially the scroll-trigger integration :D
Cheers
Is there any way to import the scroll locomotive into a project I have already created or do I have to download this project and work on it?