Created
March 15, 2019 12:13
-
-
Save jesperlandberg/382d4b7bfd0b20abd9910f03b53ec1d5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import config from '../config' | |
| import bindAll from '../utils/bindAll' | |
| import EventBus from '../utils/EventBus' | |
| import { Events as GlobalRAFEvents } from '../utils/GlobalRAF' | |
| import { Events as GlobalMouseEvents } from '../utils/GlobalMouse' | |
| class Magnetic { | |
| constructor() { | |
| bindAll(this, ['run', 'mousePos', 'checkState']) | |
| this.dom = { | |
| elems: document.querySelectorAll('.js-magnetic') | |
| } | |
| this.state = { | |
| mouse: { | |
| x: 0, | |
| y: 0 | |
| }, | |
| offset: 0 | |
| } | |
| this.states = null | |
| this.init() | |
| } | |
| getStates() { | |
| this.states = [] | |
| this.dom.elems.forEach(el => { | |
| const bounds = el.getBoundingClientRect() | |
| const children = el.querySelectorAll('[data-magnetic-child-ratio]') | |
| const state = { | |
| el: el, | |
| children: null, | |
| bounds: bounds, | |
| threshold: parseInt(el.dataset.magneticThreshold) || 100, | |
| ratio: parseInt(el.dataset.magneticRatio) || 2, | |
| max: parseInt(el.dataset.magneticMax) || 100, | |
| isHovering: false, | |
| current: { | |
| x: 0, | |
| y: 0, | |
| }, | |
| transform: { | |
| x: 0, | |
| y: 0, | |
| ease: parseInt(el.dataset.magneticEase) || 0.1 | |
| }, | |
| center: { | |
| x: bounds.left + (bounds.width / 2), | |
| y: bounds.top + (bounds.height / 2) | |
| } | |
| } | |
| if (children) { | |
| state.children = [] | |
| children.forEach(child => { | |
| state.children.push({ | |
| el: child, | |
| ratio: child.dataset.magneticChildRatio | |
| }) | |
| }) | |
| } | |
| this.states.push(state) | |
| }) | |
| } | |
| isVisible(bounds) { | |
| const current = this.state.offset | |
| const start = bounds.top - current | |
| const end = bounds.bottom - current | |
| const isVisible = start < config.height && end > 0 | |
| return isVisible | |
| } | |
| checkState(state) { | |
| const { el, bounds, threshold, center, ratio } = state | |
| const { mouse, offset } = this.state | |
| const a = Math.abs(center.x - mouse.x) | |
| const b = Math.abs(center.y - (mouse.y + offset)) | |
| const c = Math.sqrt(a * a + b * b) | |
| const isMagnetic = c < (bounds.width / 2) + threshold | |
| if (!state.isHovering && isMagnetic) { | |
| el.classList.add('is-hover') | |
| state.threshold = threshold * ratio | |
| state.isHovering = true | |
| } else if (state.isHovering && !isMagnetic) { | |
| el.classList.remove('is-hover') | |
| state.threshold = threshold / ratio | |
| state.isHovering = false | |
| } | |
| return isMagnetic | |
| } | |
| run({ smooth }) { | |
| this.state.offset = smooth | |
| this.transformElems() | |
| } | |
| transformElems() { | |
| this.states.forEach(state => { | |
| const isVisible = this.isVisible(state.bounds) | |
| if (!isVisible) return | |
| const { current, transform, max, el } = state | |
| const { mouse, offset } = this.state | |
| const { width, height } = config | |
| const isMagnetic = this.checkState(state) | |
| current.x = isMagnetic ? (mouse.x - width / 2) / (width / max) : 0 | |
| current.y = isMagnetic ? (mouse.y - height / 2) / (height / max) : 0 | |
| transform.x += (current.x - transform.x) * transform.ease | |
| transform.y += (current.y - transform.y) * transform.ease | |
| el.style.transform = ` | |
| translate3d(${transform.x.toFixed(2)}px, ${transform.y.toFixed(2)}px, 0) | |
| ` | |
| state.children && state.children.forEach(child => { | |
| child.el.style.transform = ` | |
| translate3d(${(transform.x / child.ratio).toFixed(2)}px, ${(transform.y / child.ratio).toFixed(2)}px, 0) | |
| ` | |
| }) | |
| }) | |
| } | |
| mousePos({ x, y }) { | |
| this.state.mouse.x = x | |
| this.state.mouse.y = y | |
| } | |
| addListeners() { | |
| EventBus.on(GlobalRAFEvents.TICK, this.run) | |
| EventBus.on(GlobalMouseEvents.MOVE, this.mousePos) | |
| } | |
| init() { | |
| this.addListeners() | |
| this.getStates() | |
| } | |
| } | |
| export default Magnetic |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment