mirror of
https://github.com/locomotivemtl/locomotive-boilerplate.git
synced 2026-01-15 00:55:08 +08:00
Compare commits
5 Commits
feature/sc
...
feature/js
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ff4f2fb9c | ||
|
|
cfa899639e | ||
|
|
d916ad030d | ||
|
|
b346b043a6 | ||
|
|
99291b17f5 |
@@ -1,15 +0,0 @@
|
||||
export default function(func, wait, immediate) {
|
||||
let timeout;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
const later = function() {
|
||||
timeout = null;
|
||||
if (!immediate) func.apply(context, args);
|
||||
};
|
||||
const callNow = immediate && !timeout;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(later, wait);
|
||||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
}
|
||||
180
assets/scripts/utils/events.js
Normal file
180
assets/scripts/utils/events.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import { debounce } from './tickers'
|
||||
|
||||
/**
|
||||
* @typedef {object} RegisteredCustomEvent
|
||||
*
|
||||
* @property {EventTarget} target - The event target ({@see Window} or {@see Node}).
|
||||
* @property {string} type - The custom event name.
|
||||
*/
|
||||
/** @type {RegisteredCustomEvent[]} */
|
||||
|
||||
const REGISTERED_CUSTOM_EVENTS = []
|
||||
|
||||
/**
|
||||
* Determines if the given object is the {@see Window}.
|
||||
*
|
||||
* @param {object} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isWindow = obj => obj === window
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the given object implements {@see EventTarget}.
|
||||
*
|
||||
* @param {object} obj
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isEventTarget = obj => (obj instanceof EventTarget)
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the target already has the event attached.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The custom event name.
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const isCustomEventRegistered = (target, type) => REGISTERED_CUSTOM_EVENTS.some(e => e.target === target && e.type === type)
|
||||
|
||||
|
||||
/**
|
||||
* Registers the custom event with the given target, if not already registered.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The custom event name.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addCustomEvent = (target, type) => {
|
||||
if (!isCustomEventRegistered(target, type)) {
|
||||
REGISTERED_CUSTOM_EVENTS.push({
|
||||
target,
|
||||
type
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a custom "start" event for the given target.
|
||||
*
|
||||
* Internally, this function adds a debounced event listener on
|
||||
* the given `event` to trigger the custom `<event>start` event.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The base event name.
|
||||
* @param {number} [delay] - The number of milliseconds to wait
|
||||
* before dispatching the custom event.
|
||||
* @throws Error If the target is invalid.
|
||||
* @throws Error If the custom event is already defined.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addStartEvent = (target, type, delay = 200) => {
|
||||
|
||||
const customType = `${type}start`
|
||||
|
||||
if (!isEventTarget(target)) {
|
||||
throw new Error(`addStartEvent: target parameter must be an instance of EventTarget`)
|
||||
}
|
||||
|
||||
if (isCustomEventRegistered(target, customType)) {
|
||||
throw new Error(`addStartEvent: '${customType}' already exists for target parameter`)
|
||||
}
|
||||
|
||||
addCustomEvent(target, customType)
|
||||
const startEvent = new CustomEvent(customType)
|
||||
|
||||
target.addEventListener(type, debounce(() => {
|
||||
target.dispatchEvent(startEvent)
|
||||
}, delay, true))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a custom "end" event for the given target.
|
||||
*
|
||||
* Internally, this function adds a debounced event listener on
|
||||
* the given `event` to trigger the custom `<event>end` event.
|
||||
*
|
||||
* @param {EventTarget} target
|
||||
* @param {string} type - The base event name.
|
||||
* @param {number} [delay] - The number of milliseconds to wait
|
||||
* before dispatching the custom event.
|
||||
* @throws Error If the target is invalid.
|
||||
* @throws Error If the custom event is already defined.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addEndEvent = (target, type, delay = 200) => {
|
||||
|
||||
const customType = `${type}end`
|
||||
|
||||
if (!isEventTarget(target)) {
|
||||
throw new Error(`addEndEvent: target parameter must be an instance of EventTarget`)
|
||||
}
|
||||
|
||||
if (isCustomEventRegistered(target, customType)) {
|
||||
throw new Error(`addEndEvent: '${customType}' already exists for target parameter`)
|
||||
}
|
||||
|
||||
addCustomEvent(target, customType)
|
||||
const endEvent = new CustomEvent(customType)
|
||||
|
||||
target.addEventListener(type, debounce(() => {
|
||||
target.dispatchEvent(endEvent)
|
||||
}, delay))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds custom scroll "up" and "down" events for the given target.
|
||||
*
|
||||
* Internally, this function adds an event listener on
|
||||
* the scroll event to detect the direction and trigger
|
||||
* the custom `scrollup` and `scrolldown` events.
|
||||
*
|
||||
* @param {EventTarget} [target] - If omitted, the custom event
|
||||
* if attached to the Window.
|
||||
* @throws Error If the target is invalid.
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
const addScrollDirectionEvents = (target = window) => {
|
||||
|
||||
if (!isEventTarget(target)) {
|
||||
throw new Error(`addScrollDirectionEvents: target parameter must be an instance of EventTarget`)
|
||||
}
|
||||
|
||||
let scrollTop = target.scrollTop
|
||||
let previousScrollTop = scrollTop
|
||||
let direction = 0
|
||||
const scrollUp = new CustomEvent('scrollup')
|
||||
const scrollDown = new CustomEvent('scrolldown')
|
||||
const scrollProperty = isWindow(target) ? 'scrollY' : 'scrollTop'
|
||||
|
||||
target.addEventListener('scroll', () => {
|
||||
scrollTop = target[scrollProperty]
|
||||
// Scroll up
|
||||
if (scrollTop < previousScrollTop && direction > -1) {
|
||||
target.dispatchEvent(scrollUp)
|
||||
direction = -1
|
||||
// Scroll down
|
||||
} else if (scrollTop > previousScrollTop && direction < 1) {
|
||||
target.dispatchEvent(scrollDown)
|
||||
direction = 1
|
||||
}
|
||||
previousScrollTop = scrollTop
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
addStartEvent,
|
||||
addEndEvent,
|
||||
addScrollDirectionEvents,
|
||||
}
|
||||
78
assets/scripts/utils/tickers.js
Normal file
78
assets/scripts/utils/tickers.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* Creates a debounced function.
|
||||
*
|
||||
* A debounced function delays invoking `callback` until after
|
||||
* `delay` milliseconds have elapsed since the last time the
|
||||
* debounced function was invoked.
|
||||
*
|
||||
* Useful for behaviour that should only happen _before_ or
|
||||
* _after_ an event has stopped occurring.
|
||||
*
|
||||
* @template {function} T
|
||||
*
|
||||
* @param {T} callback - The function to debounce.
|
||||
* @param {number} delay - The number of milliseconds to wait.
|
||||
* @param {boolean} [immediate] -
|
||||
* If `true`, `callback` is invoked before `delay`.
|
||||
* If `false`, `callback` is invoked after `delay`.
|
||||
* @return {function<T>} The new debounced function.
|
||||
*/
|
||||
|
||||
const debounce = (callback, delay, immediate = false) => {
|
||||
let timeout = null
|
||||
|
||||
return (...args) => {
|
||||
clearTimeout(timeout)
|
||||
|
||||
const later = () => {
|
||||
timeout = null
|
||||
if (!immediate) {
|
||||
callback(...args)
|
||||
}
|
||||
}
|
||||
|
||||
if (immediate && !timeout) {
|
||||
callback(...args)
|
||||
}
|
||||
|
||||
timeout = setTimeout(later, delay)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a throttled function.
|
||||
*
|
||||
* A throttled function invokes `callback` at most once per every
|
||||
* `delay` milliseconds.
|
||||
*
|
||||
* Useful for rate-limiting an event that occurs in quick succession.
|
||||
*
|
||||
* @template {function} T
|
||||
*
|
||||
* @param {T} callback - The function to throttle.
|
||||
* @param {number} delay - The number of milliseconds to wait.
|
||||
* @return {function<T>} The new throttled function.
|
||||
*/
|
||||
|
||||
const throttle = (callback, delay) => {
|
||||
let timeout = false
|
||||
|
||||
return (...args) => {
|
||||
if (!timeout) {
|
||||
timeout = true
|
||||
|
||||
callback(...args)
|
||||
|
||||
setTimeout(() => {
|
||||
timeout = false
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
debounce,
|
||||
throttle
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user