import "../../css/components/_marquee.scss";
import gsap from "gsap";
import $ from "jquery";
/**
* Marquee options
* @typedef {Object} Marquee#MarqueeOptions
* @property {number} speed
* @property {('left'|'right')} direction
* @property {boolean} pauseOnHover
*/
/**
* Default marquee options
* @type Marquee#MarqueeOptions
* @defaultvalue
* @private
*/
const _defaults = {
speed: 1,
direction: "left",
pauseOnHover: false,
};
/**
* @class
* @example
* const marquee = new Marquee($(".marquee"), {});
* // or
* const marquee = new Marquee(document.querySelector(".marquee")), {});
*/
class Marquee {
/**
* @constructor
* @param {jQuery|HTMLElement} el - jQuery object or HTMLElement instance of the marquee wrapper
* @param {Marquee#MarqueeOptions} [options = {}] - Options for the marquee
*/
constructor(el, options = {}) {
this.$el = $(el);
this.options = { ..._defaults, ...options };
if (this.options.pauseOnHover) {
this.$el.hover(
() => {
this.tl.pause();
},
() => {
this.tl.resume();
},
);
}
this.tl = gsap.timeline({
repeat: -1,
defaults: { ease: "none" },
onReverseComplete: () =>
this.tl.totalTime(this.tl.rawTime() + this.tl.duration() * 100),
reversed: this.options.direction === "right",
});
this.refresh();
if (this.options.direction === "right") {
this.tl.vars.onReverseComplete();
this.tl.reverse();
}
}
/**
* Refresh the marquee
* @example
* const marquee = new Marquee($(".marquee"), {});
* marquee.refresh();
*/
refresh() {
if (!this.$el.length) return;
this.$items = this.$el.children();
gsap.set(this.$items, { x: 0 });
const progress = this.tl.progress();
// Kill and remove styles
this.tl.revert();
this.tl.clear();
this.length = this.$items.length;
this.startX = this.$items.eq(0).offset().left;
this.widths = [];
this.pixelsPerSecond = (this.options.speed || 1) * 100;
const _this = this;
let $item, i, distanceToStart, distanceToLoop;
this.$items.map(function (i) {
_this.widths[i] = $(this).outerWidth();
});
this.totalWidth =
this.$items.eq(this.length - 1).offset().left -
this.startX +
this.$items.eq(this.length - 1).outerWidth();
for (i = 0; i < this.length; i++) {
$item = $(this.$items[i]);
distanceToStart = $item.offset().left - this.startX;
distanceToLoop = distanceToStart + this.widths[i];
this.tl
.to(
$item,
{
xPercent: (-distanceToLoop / this.widths[i]) * 100,
duration: distanceToLoop / this.pixelsPerSecond,
},
0,
)
.fromTo(
$item,
{
xPercent:
((-distanceToLoop + this.totalWidth) / this.widths[i]) * 100,
},
{
xPercent: 0,
duration:
(-distanceToLoop + this.totalWidth) / this.pixelsPerSecond,
immediateRender: false,
},
distanceToLoop / this.pixelsPerSecond,
);
}
// pre-render for performance
this.tl.progress(1, true).progress(progress, true);
}
/**
* Kill the marquee
* @example
* const marquee = new Marquee($(".marquee"), {});
* marquee.kill();
*/
kill() {
this.tl.revert();
this.tl = null;
gsap.set(this.$items, { clearProps: "all" });
}
}
export { Marquee as default };
Source