triggle
Version:
A lightweight CSS animation trigger library that animates elements on click, hover, enter and more.
272 lines (271 loc) • 11.4 kB
JavaScript
(function(factory) {
typeof define === "function" && define.amd ? define(factory) : factory();
})(function() {
"use strict";
(function(factory) {
typeof define === "function" && define.amd ? define(factory) : factory();
})(function() {
(function() {
const PASSIVE_EVENTS = ["touchstart", "touchend", "scroll"];
const targetCache = /* @__PURE__ */ new WeakMap();
const observerMap = /* @__PURE__ */ new WeakMap();
const eventListenerMap = /* @__PURE__ */ new WeakMap();
const animationPresets = {
fadeIn: "ca__fx-fadeIn",
fadeInUp: "ca__fx-fadeInUp",
fadeInDown: "ca__fx-fadeInDown",
zoomIn: "ca__fx-zoomIn",
zoomOut: "ca__fx-zoomOut",
bounceIn: "ca__fx-bounceIn",
bounceOut: "ca__fx-bounceOut",
slideInLeft: "ca__fx-slideInLeft",
slideInRight: "ca__fx-slideInRight",
flipInX: "ca__fx-flipInX",
flipInY: "ca__fx-flipInY"
};
function applyAnimation(el, animationClass, reset = false, delay, duration, toggle = false, onComplete) {
if (window.__trg_TRIGGER_DISABLED) return;
if (!animationClass) return;
if (delay) el.style.animationDelay = delay;
if (duration) el.style.animationDuration = duration;
const classList = animationClass.split(" ").filter(Boolean);
if (toggle) {
classList.forEach((cls) => el.classList.toggle(cls));
} else {
classList.forEach((cls) => el.classList.add(cls));
}
const clearStyles = () => {
el.style.animationDelay = "";
el.style.animationDuration = "";
};
if (reset && !toggle) {
const onAnimationEnd = () => {
classList.forEach((cls) => el.classList.remove(cls));
clearStyles();
el.removeEventListener("animationend", onAnimationEnd);
el.dispatchEvent(
new CustomEvent("triggle:animationEnd", {
detail: { class: animationClass }
})
);
if (typeof onComplete === "function") onComplete();
};
el.addEventListener("animationend", onAnimationEnd);
} else {
clearStyles();
if (typeof onComplete === "function") onComplete();
}
}
function matchesKeyFilter(keyFilter, event) {
const rules = keyFilter.split(",").map((k) => k.trim().toLowerCase());
return rules.some((rule) => {
const parts = rule.split("+");
const requiredKey = parts.pop();
const requiredMods = parts;
const matchesKey = requiredKey === event.key.toLowerCase();
const matchesMods = requiredMods.every((mod) => {
return mod === "ctrl" && event.ctrlKey || mod === "shift" && event.shiftKey || mod === "alt" && event.altKey;
});
return matchesKey && (requiredMods.length === 0 || matchesMods);
});
}
function getTargetElements(triggerEl, selector) {
if (!selector) return [triggerEl];
if (targetCache.has(triggerEl)) return targetCache.get(triggerEl);
const targets = selector.split(",").map((s) => s.trim()).flatMap((sel) => {
try {
return [
...triggerEl.querySelectorAll(sel),
triggerEl.closest(sel),
...document.querySelectorAll(sel)
].filter(Boolean);
} catch {
return [];
}
});
if (targets.length > 0) targetCache.set(triggerEl, targets);
return targets;
}
function triggerGroup(groupName, staggerValue = 0) {
const groupEls = document.querySelectorAll(
`[data-triggle-group="${groupName}"]`
);
groupEls.forEach((groupEl, i) => {
const gClass = groupEl.getAttribute("data-triggle-class");
const gReset = groupEl.getAttribute("data-triggle-reset") === "true";
const gDelay = groupEl.getAttribute("data-triggle-delay");
const gDuration = groupEl.getAttribute("data-triggle-duration");
const gToggle = groupEl.getAttribute("data-triggle-toggle") === "true";
const totalDelay = staggerValue > 0 ? `${i * staggerValue}ms` : gDelay;
applyAnimation(groupEl, gClass, gReset, totalDelay, gDuration, gToggle);
});
}
function triggerAnimation(el, originEl = null) {
const targetSelector = el.getAttribute("data-triggle-target");
const targetElements = getTargetElements(el, targetSelector);
const nextSelector = el.getAttribute("data-triggle-next");
const chainDelay = parseInt(el.getAttribute("data-triggle-chain-delay"), 10) || 0;
const chainLoop = el.getAttribute("data-triggle-chain-loop") === "true";
const groupName = el.getAttribute("data-triggle-group");
const staggerValue = parseInt(el.getAttribute("data-triggle-stagger"), 10) || 0;
const animateNext = () => {
if (nextSelector) {
const nextEl = document.querySelector(nextSelector);
if (nextEl) {
const nextClass = nextEl.getAttribute("data-triggle-class");
const nextReset = nextEl.getAttribute("data-triggle-reset") === "true";
const nextDelay = nextEl.getAttribute("data-triggle-delay");
const nextDuration = nextEl.getAttribute("data-triggle-duration");
const nextToggle = nextEl.getAttribute("data-triggle-toggle") === "true";
setTimeout(() => {
applyAnimation(
nextEl,
nextClass,
nextReset,
nextDelay,
nextDuration,
nextToggle,
() => {
if (chainLoop && originEl) {
setTimeout(() => {
triggerAnimation(originEl, originEl);
}, chainDelay);
} else if (chainLoop && !originEl) {
triggerAnimation(el, el);
}
}
);
}, chainDelay);
}
}
if (groupName) {
triggerGroup(groupName, staggerValue);
}
};
const targets = targetElements && targetElements.length > 0 ? targetElements : [el];
let remaining = targets.length;
targets.forEach((targetEl) => {
const preset = targetEl.getAttribute("data-triggle-preset");
let animationClass = targetEl.getAttribute("data-triggle-class");
if (!animationClass && preset && animationPresets[preset]) {
animationClass = animationPresets[preset];
}
if (!animationClass) {
remaining--;
if (remaining === 0) animateNext();
return;
}
const reset = targetEl.getAttribute("data-triggle-reset") === "true";
const delay = targetEl.getAttribute("data-triggle-delay");
const duration = targetEl.getAttribute("data-triggle-duration");
const toggle = targetEl.getAttribute("data-triggle-toggle") === "true";
applyAnimation(
targetEl,
animationClass,
reset,
delay,
duration,
toggle,
() => {
remaining--;
if (remaining === 0) animateNext();
}
);
});
}
function initTriggerAnimations() {
const elements = document.querySelectorAll("[data-triggle]");
elements.forEach((el) => {
const triggers = el.getAttribute("data-triggle").split(",").map((t) => t.trim());
const keyFilter = el.getAttribute("data-triggle-key");
const once = el.getAttribute("data-triggle-once") === "true";
const scrollTrigger = el.getAttribute("data-triggle-scroll") === "true";
const scrollThreshold = parseFloat(el.getAttribute("data-triggle-threshold")) || 0.5;
const groupName = el.getAttribute("data-triggle-group");
const staggerValue = parseInt(el.getAttribute("data-triggle-stagger"), 10) || 0;
const handler = (event) => {
if ((event.type === "keydown" || event.type === "keyup") && keyFilter) {
if (!matchesKeyFilter(keyFilter, event)) return;
}
triggerAnimation(el);
if (once) el.removeEventListener(event.type, handler);
};
const registeredEvents = [];
triggers.forEach((trigger) => {
if (scrollTrigger && trigger === "scroll") {
const observer = new IntersectionObserver(
(entries, obs) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (groupName) {
triggerGroup(groupName, staggerValue);
} else {
triggerAnimation(el);
}
if (once) obs.unobserve(entry.target);
}
});
},
{ threshold: scrollThreshold }
);
observer.observe(el);
observerMap.set(el, observer);
} else {
const options = PASSIVE_EVENTS.includes(trigger) ? { passive: true } : false;
el.addEventListener(trigger, handler, options);
registeredEvents.push({ type: trigger, handler, options });
}
});
if (registeredEvents.length > 0) {
eventListenerMap.set(el, registeredEvents);
}
});
}
function destroyTriggerAnimations() {
document.querySelectorAll("[data-triggle]").forEach((el) => {
const observer = observerMap.get(el);
if (observer) {
observer.disconnect();
observerMap.delete(el);
}
const listeners = eventListenerMap.get(el);
if (listeners) {
listeners.forEach(({ type, handler, options }) => {
el.removeEventListener(type, handler, options);
});
eventListenerMap.delete(el);
}
});
}
function observeMutations() {
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
mutation.addedNodes.forEach((node) => {
var _a, _b;
if (node.nodeType !== 1) return;
if (((_a = node.matches) == null ? void 0 : _a.call(node, "[data-triggle]")) || ((_b = node.querySelector) == null ? void 0 : _b.call(node, "[data-triggle]"))) {
triggle.init();
}
});
}
});
observer.observe(document.body, { childList: true, subtree: true });
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
initTriggerAnimations();
observeMutations();
});
} else {
initTriggerAnimations();
observeMutations();
}
window.triggle = {
init: initTriggerAnimations,
destroy: destroyTriggerAnimations,
apply: applyAnimation
};
})();
});
});
//# sourceMappingURL=triggle.js.map