UNPKG

smooth-text-rotator

Version:

Super simple JS library for rotating text with smooth animations

157 lines 5.81 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TextRotator = void 0; const type_guards_1 = require("./type-guards"); class TextRotator { constructor(options) { this.intervalID = null; this.started = false; this.options = Object.assign({ automaticPause: true, interval: 4000 }, options); if (this.options.element.dataset.phrases === undefined) { throw { error: new Error(`The 'phrases' data attribute is not defined in the passed element`), element: this.options.element }; } const phrases = JSON.parse(this.options.element.dataset.phrases); // Check if the parsed json is an array of string if (!(0, type_guards_1.isArrayOfString)(phrases)) { throw { error: new Error(`The 'phrases' data attribute in the passed element is not a JSON that is an array of string`), element: this.options.element }; } // TODO comment this.phraseElements = []; this.options.element.innerText = ""; phrases.forEach((phrase, index) => { const child = document.createElement('span'); if (index > 0) { child.style.position = "absolute"; child.style.top = "0"; child.style.left = "0"; } child.style.display = "inline-block"; // because the resize observer does not work properly on inline elements child.style.whiteSpace = "nowrap"; child.style.transition = 'opacity .5s cubic-bezier(.23,1,.32,1)'; child.innerText = phrase; this.options.element.appendChild(child); this.phraseElements.push(child); }); this.options.element.style.display = 'inline-block'; this.options.element.style.position = 'relative'; this.options.element.style.transition = 'width .5s cubic-bezier(.23,1,.32,1)'; this.resetPhrases(); this.setupResizeObserver(); if (this.options.automaticPause) { this.setupIntersectionObserver(); } // this.phrases = phrases; this.index = 0; } start() { this.started = true; this.index = 0; this.startInterval(); } stop() { this.started = false; this.index = 0; this.resetPhrases(); // If the rotator is paused, the interval is already stopped if (this.intervalID) { this.stopInterval(); } } pause() { this.stopInterval(); } resume() { this.startInterval(); } setupResizeObserver() { const resizeObserver = new ResizeObserver((entries) => { entries.forEach((entry) => { if (entry.target === this.phraseElements[this.index]) { this.options.element.style.width = `${entry.borderBoxSize[0].inlineSize}px`; } }); }); this.phraseElements.forEach((phraseElement) => { resizeObserver.observe(phraseElement); }); } setupIntersectionObserver() { const observer = new IntersectionObserver((entries) => { if (this.started) { if (entries[0].isIntersecting) { if (this.intervalID === null) { this.resume(); } } else { if (this.intervalID !== null) { this.pause(); } } } }); observer.observe(this.options.element); } startInterval() { if (this.intervalID !== null) { throw new Error(`Cannot start multiple ${window.setInterval.name} function`); } this.intervalID = window.setInterval(this.doAnimation.bind(this), this.options.interval); } stopInterval() { if (this.intervalID === null) { throw new Error(`Cannot stop ${window.setInterval.name} that dont exists`); } window.clearInterval(this.intervalID); this.intervalID = null; } doAnimation() { this.displayPhrase(this.index = (this.index + 1) % this.phraseElements.length); } displayPhrase(index) { // hide previously displayed element this.hidePhrase(index === 0 ? this.phraseElements.length - 1 : index - 1); // set the width of the parent span according to the width of the child to display this.setWidth(index); // display the phrase to display this.showPhrase(index); } showPhrase(index) { this.phraseElements[index].style.opacity = '1'; this.phraseElements[index].removeAttribute("aria-hidden"); this.phraseElements[index].style.pointerEvents = ''; this.phraseElements[index].style.userSelect = ''; } hidePhrase(index) { this.phraseElements[index].style.opacity = '0'; this.phraseElements[index].setAttribute("aria-hidden", "true"); this.phraseElements[index].style.pointerEvents = "none"; this.phraseElements[index].style.userSelect = "none"; } setWidth(index) { this.options.element.style.width = `${this.phraseElements[index].offsetWidth}px`; } /** * Hide all the phrases, show the first one and set the width * @private */ resetPhrases() { for (let i = 0; i < this.phraseElements.length; i++) { if (i === 0) { this.setWidth(i); this.showPhrase(i); } else { this.hidePhrase(i); } } } } exports.TextRotator = TextRotator; //# sourceMappingURL=text-rotator.js.map