Basics
FunText is a lightweight and versatile npm library designed for text animation.
About
The aim of this project is to offer straightforward and readily accessible animation choices for text-based elements. It empowers users to craft entirely custom animations while maintaining complete control over them. This is achieved through the incorporation of newly standardized Shadow DOM technology.
Instalation
Element Plus can run on browsers that support ES2018.
Package manager
Using a package manager is recomended to allow the use of bundlers.
# NPM
$ npm install funtext --save
# Yarn
$ yarn add funtext
# PNPM
$ pnpm install funtext
Import in Browser
Import Element Plus through browser HTML tags directly, and use global variable FunText.
// unpkg
<script src="//unpkg.com/funtext"></script>
// jsdelivr
<script src="https://cdn.jsdelivr.net/npm/funtext"></script>
Examples
Setup
To init a funtext instance we pass it a container and the list of animations.
Example:
<div id="funtext">This text is animated</div>
import { FunText, type InputAnimation } from "funtext";
// Retrives the container
const container = document.getElementById(
"funtext"
);
// Defines the animations
const animations: InputAnimation[] = [
{
scope: "letter",
property: "translate",
duration: 1.5,
steps: "0 10px",
iteration: "infinite",
direction: "alternate",
},
];
// Defines options
const options: InputOptions = {
defaults: {
iteration: "infinite",
direction: "alternate"
}
}
if (container) {
// Builds the funtext instance
const funtext = new FunText(
container,
animations,
options
);
// Replaces the content of the container with the funtext instance
funtext.mount();
}
Result:
Scope
A scope is an animation property that determines how the text is divided into individual elements.
Build-in
There are two build-in scopes, word and letter.
Example:
<div id="word">Scoped into words</div>
<div id="letter">Scoped into letters</div>
const word_container = document.getElementById(
"word"
);
const word_animations = [
{
scope: "word",
property: "translate",
duration: 2,
steps: "0 10px",
iteration: "infinite",
direction: "alternate",
offset: 0.5,
},
{
scope: "letter",
property: "color",
duration: 9,
steps: ["", "yellow", "orange", "red", "lime", "green", "blue", "purple"],
iteration: "infinite",
},
];
const funtext_word = new FunText(word_container, word_animations)
funtext_word.mount()
const letter_container = document.getElementById("letter");
const letter_animations: InputAnimation[] = [
{
scope: "letter",
property: "translate",
duration: 1.5,
steps: "0 10px",
iteration: "infinite",
direction: "alternate",
},
];
const funtext_letter = new FunText(letter_container, letter_animations)
funtext_word.mount()
Result:
Custom
It is also possible to define a custom scope. A scope has two properties, split and priority. Split is either a string or a regex defining how the text is divided and the priority is a number that determines in what order the scope will be applied. Lower scopes get applied first (letter has priority 3 and word has priority 1).
Example:
const animations = [
{
scope: {
split: "A",
priority: 0,
},
property: "translate",
duration: 1,
steps: {
0.1: "0 -5px",
100: "0 5px",
},
iteration: "infinite",
direction: "alternate",
offset: 1,
},
{
scope: {
split: "B",
priority: 4,
},
property: "color",
duration: 1,
steps: "red",
iteration: "infinite",
direction: "alternate",
offset: 1,
},
];
Result:
Steps
Steps determine the keyframes of the animation. Passing a string gives the final value, passign an array gives a list of values at equal intervals and passing an object gives a list of values at specefied intervals.
Example:
const string_animations = [
{
scope: "letter",
property: "color",
duration: 10,
steps: "red",
fill: "forwards",
},
];
const array_animations = [
{
scope: "letter",
property: "color",
duration: 10,
steps: ["blue", "green", "yellow", "red"],
fill: "forwards",
},
];
const object_animations = [
{
scope: "letter",
property: "color",
duration: 10,
steps: {
70: "blue",
85: "green",
95: "yellow",
100: "red",
},
fill: "forwards",
},
];
Result:
Animations
Animations have all the standard properties regular css animations have like: duration, delay, iteration-count, direction, timing-function, fill-mode and play-state, a property that defines the property that will be animated, as well as some custom properties like offset and sync.
Offset
Offset defines the delay between animations in the same scope.
Example:
const offset_none = [
{
scope: "letter",
property: "opacity",
duration: 5,
steps: "0",
fill: "forwards",
offset: 0,
},
];
const offset_small = [
{
scope: "letter",
property: "opacity",
duration: 5,
steps: "0",
fill: "forwards",
offset: 0.1, // Default
},
];
const offset_large = [
{
scope: "letter",
property: "opacity",
duration: 5,
steps: "0",
fill: "forwards",
offset: 0.5,
},
];
const offset_custom = [
{
scope: "letter",
property: "opacity",
duration: 5,
steps: "0",
fill: "forwards",
offset: (index, priority) => {
return index * index * 0.05;
},
},
];
Result:
Sync
Sync moves the animation to fit a specefied time frame. It has to parameters: duration, which specefies a new duration where the animation will be placed, and location, which specefies where in that new location the animation will be placed.
Example:
const sync_none = [
{
scope: "letter",
property: "background",
duration: 6,
steps: "red",
},
];
const sync_start = [
{
scope: "letter",
property: "background",
duration: 3,
steps: "red",
sync: {
duration: 6,
location: 0,
},
},
];
const sync_middle = [
{
scope: "letter",
property: "background",
duration: 3,
steps: "red",
sync: {
duration: 6,
location: 50,
},
},
];
const sync_end = [
{
scope: "letter",
property: "background",
duration: 3,
steps: "red",
sync: {
duration: 6,
location: 100,
},
},
];
Result:
Transform and filter
There are two animated properties that control multiple other properties. These are In the realm of CSS animations, transform and filter are pivotal properties, each capable of orchestrating a variety of sub-properties. Managing these intricate configurations manually can be challenging, prompting the implementation of custom animations tailored specifically for these compound properties."
Example:
const transform = [
{
scope: "letter",
type: "transform",
animations: [
{
property: "translateY",
duration: 5,
unit: "px",
steps: "-5",
},
{
property: "rotate",
duration: 5,
unit: "deg",
steps: { 50: "180" },
},
],
fill: "forwards",
},
];
const filter = [
{
scope: "letter",
type: "filter",
animations: [
{
property: "opacity",
duration: 5,
unit: "",
steps: [1, 0],
},
{
property: "blur",
duration: 4,
unit: "px",
steps: 2,
},
],
fill: "forwards",
},
];
Result:
Options
Options are an optional third parameter that provide additional customization, accessibility support and help shorten initialization.
Text
Uses given text for animation instead on the one in the container.
Example:
<div id="funtext">This text will be overwriten</div>
const container = document.getElementById("funtext");
const options = {
text: "New text provided in options"
}
const funtext = new FunText(container, [], options);
fun_options_text.mount();
Result:
Defaults
Provides default values to animation properties.
Example:
const animations = [
{
scope: "letter",
property: "color",
duration: 3,
steps: "rgb(81, 150, 173)",
},
];
const options = {
defaults: {
iteration: "infinite",
direction: "alternate",
},
};
Result:
Tags
Tags determine the html tags used for specific element in the creating of the funtext dom structure. There are container (div), text (p) and break (br).
Example:
const options = {
tags: {
text: "span",
container: "section"
}
}
Css
Can be used for custom styling. Has the following parameters: global, for all classes, root, for the root element, container, for the container elements, text, for the text elements, break, for the newline elements and raw, for insering raw css. For dark mode, there is dark, which takes the above elements and redefines them under the media rule prefers-color-scheme: dark. For layouts with specific breakpoints, any number can be inserted and all above elements are redefined under the media rule max-width: "number"px.
Example:
<div id="funtext1">This has a bigger font</div>
<div id="funtext2">his is blue on light mode and red on dark mode</div>
<div id="funtext3">This has a underline on mobile and a dash on desktop</div>
const user_css = {
css: {
root: "font-size: 1.5rem;",
},
};
const user_css_dark = {
css: {
container: "color: blue;",
dark: {
container: "color: red;",
},
},
};
const user_css_layout = {
css: {
text: "text-decoration: dashed;",
768: {
text: "text-decoration: underline;",
},
},
};
Result:
Attributes
Sets the attribute and value of the root element.
Example:
const options = {
attributes: {
id: "funtext",
data: "some_data"
}
}
Accessibility
Accessibility provides support for screen-readers and people with poor eyesight. This is done with aira, which adds a label for screen readers and hides the animated element, prefersContrast, which adds artificial contrast to elements and prefersReducedMotion, which prevents animated text from moving.
const options = {
accessibility: {
aria: true, // Set to true by default
prefersContrast: 0.25, // Set to 0.15 by default
prefersReducedMotion: true, // Set to false by default
}
}
Mode
Toggles the mode property of the shadow dom. If set to true allows outside javascript to access the funtext element.
Example:
const options = {
mode: true, // Set to false by default
}
Global
So far all settings have effected individual elements but we can also change ioptions globally to apply to all elements. These global options can overwriten by the elements local settings.
Example:
// alternative FunText.setOptions({ ... })
FunText.options = {
...
}
Controls
Controls allow for modification and manual control of the animated text.
Build
The mount function replaces container text with animates text and unmount does the opposite.
Example:
<div id="funtext">
<p id="click_display">Clicks: 0</p>
<br>
<button id="click_button">Click</button>
</div>
<div>
<button id="mount_button">Mount</button>
<button id="unmount_button">Unmount</button>
</div>
const click_display = document.getElementById("click_display");
let clicks = 0;
const click_button = document.getElementById("click_button");
click_button.addEventListener("click", () => {
clicks += 1;
click_display.innerHTML = "Clicks: " + clicks;
});
const funtext_conatiner = document.getElementById("funtext");
const funtext_animations] = [
{
scope: "letter",
property: "translate",
steps: "0 4px",
duration: 0.5,
iteration: "infinite",
direction: "alternate",
offset: 0.2
},
];
const funtext_options = {
text: "Loading...",
};
const funtext = new FunText(
fun_controls_build_container,
fun_controls_build_animations,
fun_controls_build_options
);
const mount_button = document.getElementById("mount_button");
mount_button.addEventListener("click", () => funtext.mount());
const unmount_button = document.getElementById("unmount_button");
unmount_button.addEventListener("click", () => funtext.unmount());
Result:
Clicks: 0
Setters
Parameters of the funtext object can be altered after construction. The container, animations and options can all be manually chnaged.
Example:
const container_start = document.getElementById("container_start");
const container_new = document.getElementById("container_new");
const animations_start = [
{
scope: "letter",
property: "color",
steps: ["lime", "green", "lime"],
duration: 3,
iteration: "infinite",
sync: {
duration: 6,
location: 0,
},
},
];
const animations_new = [
{
scope: "word",
property: "translate",
steps: "10px 0",
duration: 4,
iteration: "infinite",
direction: "alternate",
},
];
const options_start = {
css: {
text: "font-size: 1.5rem;",
},
};
const options_new = {
css: {
text: "font-size: 1.1rem;",
},
};
const funtext = new FunText(
container_start,
animations_start,
options_start
);
funtext.mount();
const container_button = document.getElementById("set_container");
container_button.addEventListener("click", () => {
funtext.container = container_new;
});
const animations_button = document.getElementById("set_animations");
animations_button.addEventListener("click", () => {
funtext.animations = animations_new;
});
const options_button = document.getElementById("set_options");
options_button.addEventListener("click", () => {
funtext.options = options_new;
});
Result:
Playstate
The state of an individual animation can be changes manually. It can be paused, played or toggled. This can be applied to an indiviudal animation or all of them.
Example:
const container = document.getElementById("funtext");
const animations: InputAnimation[] = [
{
scope: "letter",
property: "background",
steps: ["yellow", "orange", "red", "lime", "green", "blue", "purple"],
duration: 7,
iteration: "infinite",
direction: "alternate",
},
{
scope: "letter",
property: "color",
steps: ["purple", "blue", "green", "lime", "red", "orange", "yellow"],
duration: 7,
iteration: "infinite",
direction: "alternate",
},
];
const funtext = new FunText(
container,
animations
);
funtext.mount()?.pauseAll();
const first_id: AnimationId = {
property: "background",
scope: "letter",
};
const status_any = document.getElementById("status_any");
const status_all = document.getElementById("status_all");
function updateStatus() {
status_any.innerText = "Playing any: " + funtext.isPlayingAny();
status_all.innerText = "Playing all: " + funtext.isPlayingAll();
}
const play_first = document.getElementById("play_first");
play_first.addEventListener("click", () => {
funtext.play(first_id);
updateStatus();
});
const pause_first = document.getElementById("pause_first");
pause_first.addEventListener("click", () => {
funtext.pause(first_id);
updateStatus();
});
const toggle_first = document.getElementById("toggle_first");
toggle_first.addEventListener("click", () => {
funtext.toggle(first_id);
updateStatus();
});
const play_all = document.getElementById("play_all");
play_all.addEventListener("click", () => {
funtext.playAll();
updateStatus();
});
const pause_all = document.getElementById("pause_all");
pause_all.addEventListener("click", () => {
funtext.pauseAll();
updateStatus();
});
const toggle_all = document.getElementById("toggle_all");
toggle_all.addEventListener("click", () => {
funtext.toggleAll();
updateStatus();
});
Result:
Status
Playing any: false
Playing all: false
Documentation
Animations
Options
Controls
An alternative to getters and setters is to use funtext.container, funtext.options, funtext.animations directly.