UNPKG

quasar-framework

Version:

Build responsive SPA, SSR, PWA, Hybrid Mobile Apps and Electron apps, all simultaneously using the same codebase

135 lines (126 loc) 3.5 kB
import { height, offset, css, cssTransform } from '../../utils/dom.js' import debounce from '../../utils/debounce.js' import frameDebounce from '../../utils/frame-debounce.js' import { getScrollTarget } from '../../utils/scroll.js' import { listenOpts } from '../../utils/event.js' export default { name: 'QParallax', props: { src: { type: String, required: true }, height: { type: Number, default: 500 }, speed: { type: Number, default: 1, validator (value) { return value >= 0 && value <= 1 } } }, data () { return { imageHasBeenLoaded: false, scrolling: false } }, watch: { src () { this.imageHasBeenLoaded = false }, height () { this.__updatePos() } }, methods: { __processImage () { this.imageHasBeenLoaded = true this.__onResize() }, __onResize () { if (!this.imageHasBeenLoaded || !this.scrollTarget) { return } if (this.scrollTarget === window) { this.viewportHeight = window.innerHeight } this.imageHeight = height(this.image) this.__updatePos() }, __updatePos () { if (!this.imageHasBeenLoaded) { return } let containerTop, containerHeight, containerBottom, top, bottom if (this.scrollTarget === window) { containerTop = 0 containerHeight = this.viewportHeight containerBottom = containerHeight } else { containerTop = offset(this.scrollTarget).top containerHeight = height(this.scrollTarget) containerBottom = containerTop + containerHeight } top = offset(this.container).top bottom = top + this.height if (bottom > containerTop && top < containerBottom) { const percentScrolled = (containerBottom - top) / (this.height + containerHeight) this.__setPos(Math.round((this.imageHeight - this.height) * percentScrolled * this.speed)) } }, __setPos (offset) { css(this.$refs.img, cssTransform(`translate3D(-50%,${offset}px, 0)`)) } }, render (h) { return h('div', { staticClass: 'q-parallax', style: { height: `${this.height}px` } }, [ h('div', { staticClass: 'q-parallax-image absolute-full' }, [ h('img', { ref: 'img', attrs: { src: this.src }, 'class': { ready: this.imageHasBeenLoaded }, on: { load: this.__processImage } }) ]), h( 'div', { staticClass: 'q-parallax-text absolute-full column flex-center' }, this.imageHasBeenLoaded ? this.$slots.default : [ this.$slots.loading ] ) ]) }, beforeMount () { this.__setPos = frameDebounce(this.__setPos) }, mounted () { this.$nextTick(() => { this.container = this.$el this.image = this.$refs.img this.scrollTarget = getScrollTarget(this.$el) this.resizeHandler = debounce(this.__onResize, 50) window.addEventListener('resize', this.resizeHandler, listenOpts.passive) this.scrollTarget.addEventListener('scroll', this.__updatePos, listenOpts.passive) this.__onResize() }) }, beforeDestroy () { window.removeEventListener('resize', this.resizeHandler, listenOpts.passive) this.scrollTarget.removeEventListener('scroll', this.__updatePos, listenOpts.passive) } }