From 3ef961690701a7c7d2cfd801120b0ae4a387c40e Mon Sep 17 00:00:00 2001 From: Meredith Laverty Date: Wed, 30 Sep 2020 09:11:01 -0400 Subject: [PATCH] parallax element --- assets/js/parallax.js | 210 ++++++++++++++++++++++++++++++++++++++++ components/Parallax.vue | 49 ++++++++++ pages/test.vue | 12 ++- 3 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 assets/js/parallax.js create mode 100644 components/Parallax.vue diff --git a/assets/js/parallax.js b/assets/js/parallax.js new file mode 100644 index 0000000..eb261db --- /dev/null +++ b/assets/js/parallax.js @@ -0,0 +1,210 @@ +let listening = false +let targets = [] + +const defaultOptions = { + speed: 0.3, + grayscale: false, + mobilePx: 400, + startDistance: null, + stopAtEl: null +} + +class ParallaxObj { + constructor(target) { + this.mobileDisable = false + this.conditions = [] + this.active = true + this.target = target + this.accumulated = 0 + + if (typeof target === 'string') { + this.target = document.querySelector(`${target}`) + } + + if (this.target.dataset.stopAtEl) { + this.stopAtEl = this.target.dataset.stopAtEl + if (typeof this.target.dataset.stopAtEl === 'string') { + this.stopAtEl = document.querySelector(`${this.target.dataset.stopAtEl}`) + } + } + + this.speed = this.target.dataset.speed || defaultOptions.speed + this.mobilePx = this.target.dataset.mobilePx || defaultOptions.mobilePx + this.grayscale = this.target.dataset.grayscale || defaultOptions.grayscale + this.startDistance = this.target.dataset.startDistance || defaultOptions.startDistance + + resize() + } + + // API + stop() { + this.active = false + } + + start(reset=false) { + if (this.active) return + if (reset) { + this.accumulated = 0 + this.startScroll = this.startAt + } + this.active = true + } + + getSpeed() { + return this.speed + } + + changeSpeed(newSpeed) { + if (this.inWindow() && newSpeed !== this.speed) { + this.accumulated = this.getTranslation() + this.startScroll = this.scrollY() + } + this.speed = newSpeed + } + + when(condition, action) { + this.conditions.push({condition, action}) + return this + } + + // HELPERS + scrollY() { + return window.scrollY || window.pageYOffset + } + + getTranslation() { + const dist = Math.min(this.scrollY(), this.stopAt) - this.startScroll + const translation = (dist * this.speed) + this.accumulated + return translation >= 0 ? translation : 0 + } + + getGrayscale() { + if (this.scrollY() > this.stopAt) { return 100 } + + const val = (this.scrollY() - this.startScroll) / (this.stopAt - this.startScroll) * 100 + return Math.min(Math.max(val, 0), 100) + } + + getOpacity() { + return 100 - (this.getGrayscale() / 2) + } + + getRect() { + this.targetR = this.target.getBoundingClientRect() + return this.targetR + } + + inWindow() { + this.getRect() + const top = this.targetR.top + const bottom = this.targetR.bottom + + return top < this.winHeight && bottom > 0 + } + + animate() { + if (this.mobileDisable) return + this.move() + + if (this.grayscale) { + this.applyGrayscale() + } + } + + move() { + this.target + .style + .transform = `translateY(${this.getTranslation()}px)` + } + + applyGrayscale() { + this.target + .style + .filter = `grayscale(${this.getGrayscale()}%) opacity(${this.getOpacity()}%)` + } + + setConditions() { + this.winHeight = window.innerHeight + this.getRect() + + this.conditions = [] + this.startAt = this.startDistance ? this.scrollY() + this.targetR.top - parseInt(this.startDistance) : 0 + + if (this.target.dataset.stopAtEl) { + // distance to travel before stop + let distance = this.stopAtEl.getBoundingClientRect().top - this.targetR.bottom + (this.targetR.height / 2) + (this.stopAtEl.getBoundingClientRect().height / 2) + + // add to start point, and convert based on speed + this.stopAt = this.startAt + (distance / this.speed) + + } else { + this.stopAt = this.targetR.height + window.offsetHeight + } + + this.startScroll = this.startAt + this.when( + () => this.scrollY() > this.startAt && this.scrollY() < this.stopAt, + () => { + this.start(true) + } + ) + + this.when( + () => this.scrollY() < this.startAt || this.scrollY() > this.stopAt, + () => this.stop() + ) + + this.animate() + } +} + +const addListener = () => { + window.addEventListener('scroll', event => { + controller(targets) + }) + + window.addEventListener('resize', event => { + resize() + }) +} + +const controller = targets => { + requestAnimationFrame(() => { + targets.forEach(obj => { + if (obj.mobileDisable) return + obj.conditions + .forEach(({condition, action}) => { + if (condition()) action() + }) + + if (obj.active) { + obj.animate() + } + }) + }) +} + +const resize = () => { + const newSize = window.innerWidth + + targets.forEach(obj => { + obj.active = false + obj.setConditions() + if (obj.mobilePx >= newSize) { + obj.mobileDisable = true + } + }) +} + +export default (target, userOptions = {}) => { + const parallax = new ParallaxObj(target, userOptions) + targets.push(parallax) + resize() + + if (!listening) { + addListener() + listening = true + } + + return parallax +} diff --git a/components/Parallax.vue b/components/Parallax.vue new file mode 100644 index 0000000..b469096 --- /dev/null +++ b/components/Parallax.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/pages/test.vue b/pages/test.vue index 840cabd..a3f232b 100644 --- a/pages/test.vue +++ b/pages/test.vue @@ -72,6 +72,13 @@ br QuoteScroller + section#parallax + .container + parallax(:speed="0.8" is-grayscale stop-at-el="#parallax h2" :start-distance="100") + img(src="~/assets/images/fftf-logo-light.svg") + h2 Parallax + p.mt-5 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vel dapibus tortor. Nam a hendrerit turpis. Aliquam mollis porta lacus, id pellentesque massa tincidunt et. Phasellus facilisis pellentesque justo id egestas. Praesent orci augue, congue id consectetur eget, pharetra at ligula. Donec a augue ornare, vestibulum velit eget, lacinia orci. Aliquam erat volutpat. Donec molestie congue neque, non rutrum lacus maximus a. Proin sed pharetra magna. Fusce est urna, porta et laoreet non, ullamcorper id tellus. Quisque tempor odio sed orci malesuada, quis fringilla urna tristique. Proin at elementum libero. + section .container h2 Modals @@ -86,18 +93,18 @@ h2 ClickToCopy br ClickToCopy(text-to-copy="this is the text to copy") -