drowl_paragraphs_bs-1.x-dev/js/drowl_paragraphs_bs.global.js
js/drowl_paragraphs_bs.global.js
/**
* @file
* DROWL Paragraphs frontend JavaScript
*
*/
(function ($, Drupal) {
Drupal.behaviors.drowl_paragraphs_bs_frontend = {
attach: function (context, settings) {
var didScroll = false;
var animatedParagraphs = $(".paragraph.has-animation", context);
animatedParagraphs.each(function () {
if ($(this).data("animations")) {
// It really has animations ;):
var $animatedParagraph = $(this);
var animations = $animatedParagraph.data("animations");
var animationCount = animations.length;
for (var i = 0; i < animationCount; i++) {
// Skip early if required values are missing:
if (!animations[i].event || !animations[i].animation) {
continue;
}
var animationObj = animations[i];
// We have to use an anonymous function otherwise the references are wrong (see https://stackoverflow.com/questions/7749090/how-to-use-setinterval-function-within-for-loop):
(function (i, $animatedParagraph, animationObj) {
// Animation data array variables:
var animationEvent = animationObj.event;
var animationName = animationObj.animation;
var animationOffset = animationObj.offset;
var animationDelay = animationObj.delay;
var animationDuration = animationObj.transition_duration;
// Calculated values:
var animationOffsetPx = 0;
if (animationOffset != 0) {
// Calculate - 100 to 100 vh to px
var animationOffsetPercent = (100 / animationOffset) * -1;
animationOffsetPx = Math.round(
$animatedParagraph.outerHeight() / animationOffsetPercent
);
}
// Scroll related animations
if (animationEvent === "enter-viewport") {
setInterval(function () {
if (didScroll) {
// Check if in viewport
if (
!$animatedParagraph.hasClass("in-scope") &&
verge.inViewport(
$animatedParagraph.get(0),
animationOffsetPx
)
) {
// We always have to set an indicator if the element was
// in scope to detect leave-viewport
$animatedParagraph.addClass("in-scope");
// Add the deservered animation
Drupal.behaviors.drowl_paragraphs_bs_frontend.animate(
$animatedParagraph,
animationName,
animationDelay,
animationDuration
);
} else if (
$animatedParagraph.hasClass("in-scope") &&
!verge.inViewport(
$animatedParagraph.get(0),
animationOffsetPx
)
) {
// No more in scope:
$animatedParagraph.removeClass("in-scope");
}
}
}, 100);
} else if (animationEvent === "leave-viewport") {
setInterval(function () {
if (didScroll) {
// Check if no more in viewport
if (
$animatedParagraph.hasClass(
"leave-viewport-was-in-scope"
) &&
!verge.inViewport(
$animatedParagraph.get(0),
animationOffsetPx
)
) {
// Only run if the element was in scope before
// Otherwise we'd never know if the element has not
// been visible yet.
Drupal.behaviors.drowl_paragraphs_bs_frontend.animate(
$animatedParagraph,
animationName,
animationDelay
);
$animatedParagraph.removeClass(
"leave-viewport-was-in-scope"
);
} else if (
!$animatedParagraph.hasClass(
"leave-viewport-was-in-scope"
) &&
verge.inViewport(
$animatedParagraph.get(0),
animationOffsetPx
)
) {
// No more in scope:
$animatedParagraph.addClass(
"leave-viewport-was-in-scope"
);
}
}
}, 100);
} else if (animationEvent === "hover") {
$animatedParagraph.on("mouseenter touchstart", function () {
Drupal.behaviors.drowl_paragraphs_bs_frontend.animate(
$animatedParagraph,
animationName,
animationDelay,
animationDuration
);
});
}
})(i, $animatedParagraph, animationObj);
}
}
});
// Scroll events best practice:
// https://johnresig.com/blog/learning-from-twitter/
window.addEventListener("scroll", function () {
didScroll = true;
});
setInterval(function () {
if (didScroll) {
didScroll = false;
}
}, 200);
},
animate: function (
$container,
animationName,
animationDelay,
animationDuration,
callback
) {
const entranceAnimationClasses = [
// Back entrances
"backInDown",
"backInLeft",
"backInRight",
"backInUp",
// Bouncing entrances
"bounceIn",
"bounceInDown",
"bounceInLeft",
"bounceInRight",
"bounceInUp",
// Facing entrances
"fadeIn",
"fadeInDown",
"fadeInDownBig",
"fadeInLeft",
"fadeInLeftBig",
"fadeInRight",
"fadeInRightBig",
"fadeInUp",
"fadeInUpBig",
"fadeInTopLeft",
"fadeInTopRight",
"fadeInBottomLeft",
"fadeInBottomRight",
// Flippers
"flipInX",
"flipInY",
// Lightspeed
"lightSpeedInRight",
"lightSpeedInLeft",
// Rotating entrances
"rotateIn",
"rotateInDownLeft",
"rotateInDownRight",
"rotateInUpLeft",
"rotateInUpRight",
// Specials
"jackInTheBox",
"rollIn",
// Zooming entrances
"zoomIn",
"zoomInDown",
"zoomInLeft",
"zoomInRight",
"zoomInUp",
// Sliging entrances
"slideInDown",
"slideInLeft",
"slideInRight",
"slideInUp",
];
const exitAnimationClasses = [
// Back exits
"backOutDown",
"backOutLeft",
"backOutRight",
"backOutUp",
// Bouncing exits
"bounceOut",
"bounceOutDown",
"bounceOutLeft",
"bounceOutUp",
// Fading exits
"fadeOut",
"fadeOutDown",
"fadeOutDownBig",
"fadeOutLeft",
"fadeOutLeftBig",
"fadeOutRight",
"fadeOutRightBig",
"fadeOutUp",
"fadeOutUpBig",
"fadeOutTopLeft",
"fadeOutTopRight",
"fadeOutBottomLeft",
"fadeOutBottomRight",
// Flippers
"flipOutX",
"flipOutY",
// Lightspeed
"lightSpeedOutRight",
"lightSpeedOutLeft",
// Rotating exits
"rotateOut",
"rotateOutDownLeft",
"rotateOutDownRight",
"rotateOutUpLeft",
"rotateOutUpRight",
// Specials
"rollOut",
// Zooming exits
"zoomOut",
"zoomOutDown",
"zoomOutLeft",
"zoomOutRight",
"zoomOutUp",
// Sliging exits
"slideOutDown",
"slideOutLeft",
"slideOutRight",
"slideOutUp",
];
const attentionAnimationClasses = [
// Attention seekers
"bounce",
"flash",
"pulse",
"rubberBand",
"shakeX",
"shakeY",
"headShake",
"swing",
"tada",
"wobble",
"jello",
"heartBeat",
// Flippers
"flip",
// Specials
"hinge",
];
const animationClasses = [].concat(
entranceAnimationClasses,
exitAnimationClasses,
attentionAnimationClasses
);
var animationEnd =
"webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend";
var animationDelay =
typeof animationDelay === "undefined" ? 0 : animationDelay;
var animationDurationOverridden =
typeof animationDuration === "undefined" || animationDuration == 0
? false
: true;
if (animationDurationOverridden) {
$container.css("animation-duration", animationDuration + "ms");
}
setTimeout(function () {
$container.addClass("animated " + animationName);
$container.one(animationEnd, function () {
// Dont remove the animation classes on entrance animations, so the final animation state will stay
if (!entranceAnimationClasses.includes(animationName)) {
// Remove ALL animation classes on exit animations (even entrance animation classes)
if (exitAnimationClasses.includes(animationName)) {
$(this).removeClass("animated").removeClass(animationClasses);
} else {
$(this).removeClass("animated " + animationName);
}
}
if (callback) {
callback();
}
});
}, animationDelay);
return this;
},
};
})(jQuery, Drupal);
