UNPKG

gsap

Version:

GSAP is a framework-agnostic JavaScript animation library that turns developers into animation superheroes. Build high-performance animations that work in **every** major browser. Animate CSS, SVG, canvas, React, Vue, WebGL, colors, strings, motion paths,

322 lines (316 loc) 16.9 kB
/*! * SplitText 3.13.0 * https://gsap.com * * @license Copyright 2025, GreenSock. All rights reserved. Subject to the terms at https://gsap.com/standard-license. * @author: Jack Doyle */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.window = global.window || {})); })(this, (function (exports) { 'use strict'; let gsap, _fonts, _coreInitted, _initIfNecessary = () => _coreInitted || SplitText.register(window.gsap), _charSegmenter = typeof Intl !== "undefined" ? new Intl.Segmenter() : 0, _toArray = (r) => typeof r === "string" ? _toArray(document.querySelectorAll(r)) : "length" in r ? Array.from(r) : [r], _elements = (targets) => _toArray(targets).filter((e) => e instanceof HTMLElement), _emptyArray = [], _context = function() { }, _spacesRegEx = /\s+/g, _emojiSafeRegEx = new RegExp("\\p{RI}\\p{RI}|\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?(\\u{200D}\\p{Emoji}(\\p{EMod}|\\u{FE0F}\\u{20E3}?|[\\u{E0020}-\\u{E007E}]+\\u{E007F})?)*|.", "gu"), _emptyBounds = { left: 0, top: 0, width: 0, height: 0 }, _stretchToFitSpecialChars = (collection, specialCharsRegEx) => { if (specialCharsRegEx) { let charsFound = new Set(collection.join("").match(specialCharsRegEx) || _emptyArray), i = collection.length, slots, word, char, combined; if (charsFound.size) { while (--i > -1) { word = collection[i]; for (char of charsFound) { if (char.startsWith(word) && char.length > word.length) { slots = 0; combined = word; while (char.startsWith(combined += collection[i + ++slots]) && combined.length < char.length) { } if (slots && combined.length === char.length) { collection[i] = char; collection.splice(i + 1, slots); break; } } } } } } return collection; }, _disallowInline = (element) => window.getComputedStyle(element).display === "inline" && (element.style.display = "inline-block"), _insertNodeBefore = (newChild, parent, existingChild) => parent.insertBefore(typeof newChild === "string" ? document.createTextNode(newChild) : newChild, existingChild), _getWrapper = (type, config, collection) => { let className = config[type + "sClass"] || "", { tag = "div", aria = "auto", propIndex = false } = config, display = type === "line" ? "block" : "inline-block", incrementClass = className.indexOf("++") > -1, wrapper = (text) => { let el = document.createElement(tag), i = collection.length + 1; className && (el.className = className + (incrementClass ? " " + className + i : "")); propIndex && el.style.setProperty("--" + type, i + ""); aria !== "none" && el.setAttribute("aria-hidden", "true"); if (tag !== "span") { el.style.position = "relative"; el.style.display = display; } el.textContent = text; collection.push(el); return el; }; incrementClass && (className = className.replace("++", "")); wrapper.collection = collection; return wrapper; }, _getLineWrapper = (element, nodes, config, collection) => { let lineWrapper = _getWrapper("line", config, collection), textAlign = window.getComputedStyle(element).textAlign || "left"; return (startIndex, endIndex) => { let newLine = lineWrapper(""); newLine.style.textAlign = textAlign; element.insertBefore(newLine, nodes[startIndex]); for (; startIndex < endIndex; startIndex++) { newLine.appendChild(nodes[startIndex]); } newLine.normalize(); }; }, _splitWordsAndCharsRecursively = (element, config, wordWrapper, charWrapper, prepForCharsOnly, deepSlice, ignore, charSplitRegEx, specialCharsRegEx, isNested) => { var _a; let nodes = Array.from(element.childNodes), i = 0, { wordDelimiter, reduceWhiteSpace = true, prepareText } = config, elementBounds = element.getBoundingClientRect(), lastBounds = elementBounds, isPreformatted = !reduceWhiteSpace && window.getComputedStyle(element).whiteSpace.substring(0, 3) === "pre", ignoredPreviousSibling = 0, wordsCollection = wordWrapper.collection, wordDelimIsNotSpace, wordDelimString, wordDelimSplitter, curNode, words, curWordEl, startsWithSpace, endsWithSpace, j, bounds, curWordChars, clonedNode, curSubNode, tempSubNode, curTextContent, wordText, lastWordText, k; if (typeof wordDelimiter === "object") { wordDelimSplitter = wordDelimiter.delimiter || wordDelimiter; wordDelimString = wordDelimiter.replaceWith || ""; } else { wordDelimString = wordDelimiter === "" ? "" : wordDelimiter || " "; } wordDelimIsNotSpace = wordDelimString !== " "; for (; i < nodes.length; i++) { curNode = nodes[i]; if (curNode.nodeType === 3) { curTextContent = curNode.textContent || ""; if (reduceWhiteSpace) { curTextContent = curTextContent.replace(_spacesRegEx, " "); } else if (isPreformatted) { curTextContent = curTextContent.replace(/\n/g, wordDelimString + "\n"); } prepareText && (curTextContent = prepareText(curTextContent, element)); curNode.textContent = curTextContent; words = wordDelimString || wordDelimSplitter ? curTextContent.split(wordDelimSplitter || wordDelimString) : curTextContent.match(charSplitRegEx) || _emptyArray; lastWordText = words[words.length - 1]; endsWithSpace = wordDelimIsNotSpace ? lastWordText.slice(-1) === " " : !lastWordText; lastWordText || words.pop(); lastBounds = elementBounds; startsWithSpace = wordDelimIsNotSpace ? words[0].charAt(0) === " " : !words[0]; startsWithSpace && _insertNodeBefore(" ", element, curNode); words[0] || words.shift(); _stretchToFitSpecialChars(words, specialCharsRegEx); deepSlice && isNested || (curNode.textContent = ""); for (j = 1; j <= words.length; j++) { wordText = words[j - 1]; if (!reduceWhiteSpace && isPreformatted && wordText.charAt(0) === "\n") { (_a = curNode.previousSibling) == null ? void 0 : _a.remove(); _insertNodeBefore(document.createElement("br"), element, curNode); wordText = wordText.slice(1); } if (!reduceWhiteSpace && wordText === "") { _insertNodeBefore(wordDelimString, element, curNode); } else if (wordText === " ") { element.insertBefore(document.createTextNode(" "), curNode); } else { wordDelimIsNotSpace && wordText.charAt(0) === " " && _insertNodeBefore(" ", element, curNode); if (ignoredPreviousSibling && j === 1 && !startsWithSpace && wordsCollection.indexOf(ignoredPreviousSibling.parentNode) > -1) { curWordEl = wordsCollection[wordsCollection.length - 1]; curWordEl.appendChild(document.createTextNode(charWrapper ? "" : wordText)); } else { curWordEl = wordWrapper(charWrapper ? "" : wordText); _insertNodeBefore(curWordEl, element, curNode); ignoredPreviousSibling && j === 1 && !startsWithSpace && curWordEl.insertBefore(ignoredPreviousSibling, curWordEl.firstChild); } if (charWrapper) { curWordChars = _charSegmenter ? _stretchToFitSpecialChars([..._charSegmenter.segment(wordText)].map((s) => s.segment), specialCharsRegEx) : wordText.match(charSplitRegEx) || _emptyArray; for (k = 0; k < curWordChars.length; k++) { curWordEl.appendChild(curWordChars[k] === " " ? document.createTextNode(" ") : charWrapper(curWordChars[k])); } } if (deepSlice && isNested) { curTextContent = curNode.textContent = curTextContent.substring(wordText.length + 1, curTextContent.length); bounds = curWordEl.getBoundingClientRect(); if (bounds.top > lastBounds.top && bounds.left <= lastBounds.left) { clonedNode = element.cloneNode(); curSubNode = element.childNodes[0]; while (curSubNode && curSubNode !== curWordEl) { tempSubNode = curSubNode; curSubNode = curSubNode.nextSibling; clonedNode.appendChild(tempSubNode); } element.parentNode.insertBefore(clonedNode, element); prepForCharsOnly && _disallowInline(clonedNode); } lastBounds = bounds; } if (j < words.length || endsWithSpace) { _insertNodeBefore(j >= words.length ? " " : wordDelimIsNotSpace && wordText.slice(-1) === " " ? " " + wordDelimString : wordDelimString, element, curNode); } } } element.removeChild(curNode); ignoredPreviousSibling = 0; } else if (curNode.nodeType === 1) { if (ignore && ignore.indexOf(curNode) > -1) { wordsCollection.indexOf(curNode.previousSibling) > -1 && wordsCollection[wordsCollection.length - 1].appendChild(curNode); ignoredPreviousSibling = curNode; } else { _splitWordsAndCharsRecursively(curNode, config, wordWrapper, charWrapper, prepForCharsOnly, deepSlice, ignore, charSplitRegEx, specialCharsRegEx, true); ignoredPreviousSibling = 0; } prepForCharsOnly && _disallowInline(curNode); } } }; const _SplitText = class _SplitText { constructor(elements, config) { this.isSplit = false; _initIfNecessary(); this.elements = _elements(elements); this.chars = []; this.words = []; this.lines = []; this.masks = []; this.vars = config; this._split = () => this.isSplit && this.split(this.vars); let orig = [], timerId, checkWidths = () => { let i = orig.length, o; while (i--) { o = orig[i]; let w = o.element.offsetWidth; if (w !== o.width) { o.width = w; this._split(); return; } } }; this._data = { orig, obs: typeof ResizeObserver !== "undefined" && new ResizeObserver(() => { clearTimeout(timerId); timerId = setTimeout(checkWidths, 200); }) }; _context(this); this.split(config); } split(config) { this.isSplit && this.revert(); this.vars = config = config || this.vars || {}; let { type = "chars,words,lines", aria = "auto", deepSlice = true, smartWrap, onSplit, autoSplit = false, specialChars, mask } = this.vars, splitLines = type.indexOf("lines") > -1, splitCharacters = type.indexOf("chars") > -1, splitWords = type.indexOf("words") > -1, onlySplitCharacters = splitCharacters && !splitWords && !splitLines, specialCharsRegEx = specialChars && ("push" in specialChars ? new RegExp("(?:" + specialChars.join("|") + ")", "gu") : specialChars), finalCharSplitRegEx = specialCharsRegEx ? new RegExp(specialCharsRegEx.source + "|" + _emojiSafeRegEx.source, "gu") : _emojiSafeRegEx, ignore = !!config.ignore && _elements(config.ignore), { orig, animTime, obs } = this._data, onSplitResult; if (splitCharacters || splitWords || splitLines) { this.elements.forEach((element, index) => { orig[index] = { element, html: element.innerHTML, ariaL: element.getAttribute("aria-label"), ariaH: element.getAttribute("aria-hidden") }; aria === "auto" ? element.setAttribute("aria-label", (element.textContent || "").trim()) : aria === "hidden" && element.setAttribute("aria-hidden", "true"); let chars = [], words = [], lines = [], charWrapper = splitCharacters ? _getWrapper("char", config, chars) : null, wordWrapper = _getWrapper("word", config, words), i, curWord, smartWrapSpan, nextSibling; _splitWordsAndCharsRecursively(element, config, wordWrapper, charWrapper, onlySplitCharacters, deepSlice && (splitLines || onlySplitCharacters), ignore, finalCharSplitRegEx, specialCharsRegEx, false); if (splitLines) { let nodes = _toArray(element.childNodes), wrapLine = _getLineWrapper(element, nodes, config, lines), curNode, toRemove = [], lineStartIndex = 0, allBounds = nodes.map((n) => n.nodeType === 1 ? n.getBoundingClientRect() : _emptyBounds), lastBounds = _emptyBounds; for (i = 0; i < nodes.length; i++) { curNode = nodes[i]; if (curNode.nodeType === 1) { if (curNode.nodeName === "BR") { toRemove.push(curNode); wrapLine(lineStartIndex, i + 1); lineStartIndex = i + 1; lastBounds = allBounds[lineStartIndex]; } else { if (i && allBounds[i].top > lastBounds.top && allBounds[i].left <= lastBounds.left) { wrapLine(lineStartIndex, i); lineStartIndex = i; } lastBounds = allBounds[i]; } } } lineStartIndex < i && wrapLine(lineStartIndex, i); toRemove.forEach((el) => { var _a; return (_a = el.parentNode) == null ? void 0 : _a.removeChild(el); }); } if (!splitWords) { for (i = 0; i < words.length; i++) { curWord = words[i]; if (splitCharacters || !curWord.nextSibling || curWord.nextSibling.nodeType !== 3) { if (smartWrap && !splitLines) { smartWrapSpan = document.createElement("span"); smartWrapSpan.style.whiteSpace = "nowrap"; while (curWord.firstChild) { smartWrapSpan.appendChild(curWord.firstChild); } curWord.replaceWith(smartWrapSpan); } else { curWord.replaceWith(...curWord.childNodes); } } else { nextSibling = curWord.nextSibling; if (nextSibling && nextSibling.nodeType === 3) { nextSibling.textContent = (curWord.textContent || "") + (nextSibling.textContent || ""); curWord.remove(); } } } words.length = 0; element.normalize(); } this.lines.push(...lines); this.words.push(...words); this.chars.push(...chars); }); mask && this[mask] && this.masks.push(...this[mask].map((el) => { let maskEl = el.cloneNode(); el.replaceWith(maskEl); maskEl.appendChild(el); el.className && (maskEl.className = el.className.replace(/(\b\w+\b)/g, "$1-mask")); maskEl.style.overflow = "clip"; return maskEl; })); } this.isSplit = true; _fonts && (autoSplit ? _fonts.addEventListener("loadingdone", this._split) : _fonts.status === "loading" && console.warn("SplitText called before fonts loaded")); if ((onSplitResult = onSplit && onSplit(this)) && onSplitResult.totalTime) { this._data.anim = animTime ? onSplitResult.totalTime(animTime) : onSplitResult; } splitLines && autoSplit && this.elements.forEach((element, index) => { orig[index].width = element.offsetWidth; obs && obs.observe(element); }); return this; } revert() { var _a, _b; let { orig, anim, obs } = this._data; obs && obs.disconnect(); orig.forEach(({ element, html, ariaL, ariaH }) => { element.innerHTML = html; ariaL ? element.setAttribute("aria-label", ariaL) : element.removeAttribute("aria-label"); ariaH ? element.setAttribute("aria-hidden", ariaH) : element.removeAttribute("aria-hidden"); }); this.chars.length = this.words.length = this.lines.length = orig.length = this.masks.length = 0; this.isSplit = false; _fonts == null ? void 0 : _fonts.removeEventListener("loadingdone", this._split); if (anim) { this._data.animTime = anim.totalTime(); anim.revert(); } (_b = (_a = this.vars).onRevert) == null ? void 0 : _b.call(_a, this); return this; } static create(elements, config) { return new _SplitText(elements, config); } static register(core) { gsap = gsap || core || window.gsap; if (gsap) { _toArray = gsap.utils.toArray; _context = gsap.core.context || _context; } if (!_coreInitted && window.innerWidth > 0) { _fonts = document.fonts; _coreInitted = true; } } }; _SplitText.version = "3.13.0"; let SplitText = _SplitText; exports.SplitText = SplitText; exports.default = SplitText; Object.defineProperty(exports, '__esModule', { value: true }); }));