Autoplay
In Vue Horizontal, you can programmatically navigate horizontal content with the exposed methods. Although there isn't a built-in capability for autoplay, Vue Horizontal is created to support autoplay by exposing useful methods, events and props to accomplish that. You could use mounted to trigger the autoplay, but a more reliable method would be to trigger them as it enter into the viewport. This can be achieved with IntersectionObserver but for simplicity, you can use vue-observe-visibility as well.
Vue Horizontal methods
$refs.horizontal.next()
$refs.horizontal.prev()
$refs.horizontal.scrollToIndex(1)
$refs.horizontal.scrollToLeft(1000)
Vue Horizontal events
<vue-horizontal @scroll="({...}) => ">
<vue-horizontal @scroll-debounce="({...}) => ">
Vue Horizontal props
:button="false"
to hide nav button.:displacement="0.5"
to control displacement of prev/next clicks relative to the width of the content.snap="start|center|end|none"
to change the snapping settings
Autoplay
Mounted
You can programmatically move to any position of the horizontal content in Vue Horizontal. This allows you to easily move to preset initial position at mounted.
<template>
<vue-horizontal responsive ref="horizontal">
<placeholder-component v-for="i in [0,1,2,3,4,5,6,7,8,9]" :key="i">
{{i}}
</placeholder-component>
</vue-horizontal>
</template>
<script>
export default {
mounted() {
this.$refs.horizontal.scrollToIndex(1);
}
}
</script>
Peeking
When Vue Horizontal is visible, move the content slightly to the right for a few seconds to indicate there are more content on the right. For simplicity, you can use vue-observe-visibility.
<template>
<vue-horizontal responsive ref="horizontal" :snap="snap" :button="peeked">
<placeholder-component v-for="i in [0,1,2,3,4,5,6,7,8,9]" :key="i">
{{ i }}
</placeholder-component>
</vue-horizontal>
</template>
<script>
export default {
data() {
return {
peeked: false,
timeout: null,
}
},
computed: {
snap() {
return this.peeked ? 'snap' : 'none'
}
},
mounted() {
// Custom observe visibility is below
// Much easier way: https://www.npmjs.com/package/vue-observe-visibility
observeVisibility(this.$refs.horizontal.$el, this.peeking)
},
destroyed() {
if (this.timeout) {
clearTimeout(this.timeout)
}
},
methods: {
peeking(visible) {
if (!visible) {
return
}
this.peeked = false
this.timeout = setTimeout(() => {
this.$refs.horizontal.scrollToLeft(48)
this.timeout = setTimeout(() => {
this.$refs.horizontal.scrollToLeft(0)
this.timeout = setTimeout(() => {
this.peeked = true
}, 1000)
}, 400)
}, 1000)
},
}
}
/**
* Custom function, much easier way: https://www.npmjs.com/package/vue-observe-visibility
*
* @param element to track visibility
* @param callback: function(boolean) when visibility change
*/
function observeVisibility(element, callback) {
const observer = new IntersectionObserver((records) => {
callback(records.find(record => record.isIntersecting))
}, {rootMargin: '10% 0% 10% 0%', threshold: 1.0});
observer.observe(element);
}
</script>
Sequences
Forward and reverse
<template>
<vue-horizontal responsive ref="horizontal" @scroll-debounce="onScrollDebounce">
<placeholder-component v-for="i in items" :key="i">
{{ i }}
</placeholder-component>
</vue-horizontal>
</template>
<script>
export default {
data() {
return {
items: [...Array(30).keys()],
hasPrev: false,
hasNext: false,
interval: null,
forward: true,
}
},
mounted() {
// Custom observe visibility is below
// Much easier way: https://www.npmjs.com/package/vue-observe-visibility
observeVisibility(this.$refs.horizontal.$el, (visible) => {
if (visible) {
this.interval = setInterval(this.play, 3000)
} else {
clearInterval(this.interval)
}
})
},
destroyed() {
clearInterval(this.interval)
},
methods: {
onScrollDebounce({hasNext, hasPrev}) {
this.hasPrev = hasPrev
this.hasNext = hasNext
},
play() {
// Check if direction need to be reversed
if (this.hasNext !== this.hasPrev) {
this.forward = !this.forward
}
if (this.forward && this.hasNext) {
this.$refs.horizontal.next()
} else if (!this.forward && this.hasPrev) {
this.$refs.horizontal.prev()
}
}
}
}
/**
* Custom function, much easier way: https://www.npmjs.com/package/vue-observe-visibility
*
* @param element to track visibility
* @param callback: function(boolean) when visibility change
*/
function observeVisibility(element, callback) {
const observer = new IntersectionObserver((records) => {
callback(records.find(record => record.isIntersecting))
}, {rootMargin: '10% 0% 10% 0%', threshold: 1.0});
observer.observe(element);
}
</script>
Forward and reset
<template>
<vue-horizontal responsive ref="horizontal" @scroll-debounce="onScrollDebounce" :displacement="displacement">
<placeholder-component v-for="i in items" :key="i">
{{ i }}
</placeholder-component>
</vue-horizontal>
</template>
<script>
export default {
data() {
return {
items: [...Array(30).keys()],
hasPrev: false,
hasNext: false,
interval: null,
// relative width to move when next/prev is clicked.
displacement: 1.0,
}
},
mounted() {
// Custom observe visibility is below
// Much easier way: https://www.npmjs.com/package/vue-observe-visibility
observeVisibility(this.$refs.horizontal.$el, (visible) => {
if (visible) {
this.interval = setInterval(this.play, 3000)
} else {
clearInterval(this.interval)
}
})
},
destroyed() {
clearInterval(this.interval)
},
methods: {
onScrollDebounce({hasNext, hasPrev}) {
this.hasPrev = hasPrev
this.hasNext = hasNext
},
play() {
if (!this.hasNext && this.hasPrev) {
this.$refs.horizontal.scrollToIndex(0)
this.displacement = 1.0
return
}
if (this.hasNext) {
this.$refs.horizontal.next()
// After first nav, change displacement window to just 60%
this.displacement = 0.6
}
}
}
}
/**
* Custom function, much easier way: https://www.npmjs.com/package/vue-observe-visibility
*
* @param element to track visibility
* @param callback: function(boolean) when visibility change
*/
function observeVisibility(element, callback) {
const observer = new IntersectionObserver((records) => {
callback(records.find(record => record.isIntersecting))
}, {rootMargin: '10% 0% 10% 0%', threshold: 1.0});
observer.observe(element);
}
</script>
Programmatically
Tracking
Track another scrollbar or vue horizontal and move it to the same position. In this example, B tracking the position of A.
<template>
<div>
<h3>Header A</h3>
<vue-horizontal responsive @scroll="onScroll">
<placeholder-component v-for="i in items" :key="i">
a{{ i }}
</placeholder-component>
</vue-horizontal>
<h3 style="margin-top: 24px">Header B</h3>
<vue-horizontal responsive ref="b" snap="none" class="horizontal">
<placeholder-component v-for="i in items" :key="i">
b{{ i }}
</placeholder-component>
</vue-horizontal>
</div>
</template>
<script>
export default {
data() {
return {
items: [...Array(30).keys()],
}
},
methods: {
onScroll({left}) {
this.$refs.b.scrollToLeft(left, "auto")
},
}
}
</script>
<style scoped>
.horizontal >>> .v-hl-container {
scroll-behavior: initial;
}
</style>
Mirror
Mirror another vue horizontal and move it to the same index. A and B mirroring each other position, updated after scroll debounce.
<template>
<div>
<h3>Header A</h3>
<vue-horizontal responsive ref="a" @scroll-debounce="onScrollDebounce">
<placeholder-component v-for="i in items" :key="i">
a{{ i }}
</placeholder-component>
</vue-horizontal>
<h3 style="margin-top: 24px">Header B</h3>
<vue-horizontal responsive ref="b" @scroll-debounce="onScrollDebounce">
<placeholder-component v-for="i in items" :key="i">
b{{ i }}
</placeholder-component>
</vue-horizontal>
</div>
</template>
<script>
export default {
data() {
return {
items: [...Array(30).keys()],
}
},
methods: {
onScrollDebounce({left}) {
this.$refs.a.scrollToLeft(left)
this.$refs.b.scrollToLeft(left)
},
}
}
</script>