UNPKG

react-teffex

Version:

Text Effects: Stateful React text effects for amazing websites

248 lines (246 loc) 11.8 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import React from "react"; import { randomise } from "../utils"; var Typewriter = /** @class */ (function (_super) { __extends(Typewriter, _super); function Typewriter(props) { var _a, _b, _c, _d, _e, _f; var _this = _super.call(this, props) || this; // This function adds a delay (or stutter) while typing based on the chance that a random value with a maximum of 10 is smaller than the prescribed chance variable (default is 1) _this.shouldIStutter = function () { var _a; var chance = ((_a = _this.props.typeSettings) === null || _a === void 0 ? void 0 : _a.stutterChance) ? _this.props.typeSettings.stutterChance : 2; // Chance if (randomise(10) < chance) { _this.stutterInterval = _this.stutterTime; } else { _this.stutterInterval = 0; } }; // This function appends the next letter to the text state and runs the should I stutter function _this.type = function () { if (_this.isActive.current) { var newText = []; if (_this.state.reverseType) { newText = _this.state.text; newText.pop(); } else { newText = __spreadArray(__spreadArray([], _this.state.text, true), [_this.text.split("")[_this.index]], false); } _this.setState({ text: newText, typing: true, cursor: true, }); _this.index++; if (_this.state.reverseType) { _this.stutterInterval = 0; } else { _this.shouldIStutter(); } } }; _this.handleCycle = function () { if (_this.isActive.current) { // If the cycle index is larger than the array length, loop ? set index to 0 : updateState if (_this.cycleIndex > _this.props.cycle.length - 1) { if (_this.props.loop) { _this.cycleIndex = 0; } else { _this.isActive.current && _this.setState({ cycleEnded: true }); } } // If it is the first index, do not pause, continue typing if (!_this.state.hasCycled) { _this.text = _this.props.cycle[0]; _this.index = 0; _this.isActive.current && _this.setState({ hasCycled: true }); _this.cycleTimer = 0; } else { // If not the first value if (window.performance.now() - _this.cycleTimer >= _this.cycleDelay) { _this.cycleTimer = 0; _this.index = 0; _this.text = _this.props.cycle[_this.cycleIndex]; if (_this.state.reverseType) { _this.isActive.current && _this.setState({ reverseType: false }); if (_this.cycleIndex === _this.props.cycle.length - 1 && !_this.props.loop) _this.cycleIndex++; } else { _this.cycleIndex++; _this.isActive.current && _this.setState({ reverseType: true }); } } } } }; _this.animate = function () { if (_this.isActive.current) { // Type if (window.performance.now() - _this.typeTimer >= _this.typeDelay + _this.stutterInterval + _this.initialDelay) { if (_this.initialDelay) _this.initialDelay = 0; _this.typeTimer = window.performance.now(); // If the current text is not done typing, continue typing if (_this.index <= _this.text.length - 1) { _this.type(); } else { if (_this.cycleTimer === 0) { // this.setState({ reverseType: false }); _this.cycleTimer = window.performance.now(); } /* If the current text is done typing: 1) Check for cycled components - False : Blink (setState({typing:false})) - True : if the delay time is larger than the cursor blink time -- blink -- then run cycler */ // Catch the first time that the type rotation has completed _this.props.cycle && !_this.state.cycleEnded && _this.handleCycle(); // We should not be typing whenm the index exceeds the length of the string if (_this.state.typing) { _this.isActive.current && _this.setState({ typing: false }); } } } // Cursor blink if (window.performance.now() - _this.cursorBlinkTimer >= _this.cursorBlinkSpeed) { _this.cursorBlinkTimer = window.performance.now(); if (!_this.state.typing) { _this.isActive.current && _this.setState({ cursor: !_this.state.cursor }); } } _this.isActive.current && requestAnimationFrame(_this.animate); } }; _this.state = { text: [], cursor: true, typing: false, reverseType: false, hasCycled: false, cycleEnded: false, }; // Error checking: checkPropsForErrors(_this.props); // isMounted check _this.isActive = React.createRef(); // Type settings _this.index = 0; _this.stutterTime = ((_a = _this.props.typeSettings) === null || _a === void 0 ? void 0 : _a.stutterTime) ? _this.props.typeSettings.stutterTime : 0; _this.typeTimer = window.performance.now(); _this.stutterInterval = 0; _this.text = _this.props.text ? _this.props.text : ""; _this.typeDelay = ((_b = _this.props.typeSettings) === null || _b === void 0 ? void 0 : _b.typeDelay) ? _this.props.typeSettings.typeDelay : 100; _this.initialDelay = ((_c = _this.props.typeSettings) === null || _c === void 0 ? void 0 : _c.initialDelay) ? _this.props.typeSettings.initialDelay : 0; // Cycle settings _this.cycleIndex = 0; _this.cycleDelay = _this.props.cycleDelay ? _this.props.cycleDelay : 1000; _this.cycleTimer = 0; // Cursor settings _this.cursorBlinkTimer = window.performance.now(); _this.cursorBlinkSpeed = ((_d = _this.props.cursorSettings) === null || _d === void 0 ? void 0 : _d.cursorBlinkSpeed) ? _this.props.cursorSettings.cursorBlinkSpeed : 500; // Cursor styles _this.cursorInvisibleStyle = { opacity: "0", overflow: "hidden", }; _this.cursorNormalStyle = { backgroundColor: ((_e = _this.props.cursorSettings) === null || _e === void 0 ? void 0 : _e.color) ? _this.props.cursorSettings.color : "black", color: ((_f = _this.props.cursorSettings) === null || _f === void 0 ? void 0 : _f.color) ? _this.props.cursorSettings.color : "black", overflow: "hidden", }; return _this; } Typewriter.prototype.componentDidMount = function () { this.isActive.current = true; this.animate(); }; Typewriter.prototype.componentWillUnmount = function () { this.isActive.current = false; }; Typewriter.prototype.render = function () { return (_jsxs("span", __assign({ onClick: this.props.onClick && this.props.onClick, onMouseEnter: this.props.onMouseEnter && this.props.onMouseEnter, onMouseLeave: this.props.onMouseLeave && this.props.onMouseLeave, id: this.props.id && this.props.id, style: this.props.style && this.props.style }, { children: [this.state.text.join(""), this.state.cursor ? (_jsx("span", __assign({ style: this.cursorNormalStyle, onSelect: function (e) { e.preventDefault(); } }, { children: "\u200F\u200F\u200E \u200E" }), void 0)) : (_jsx("span", __assign({ style: this.cursorInvisibleStyle, onSelect: function (e) { e.preventDefault(); } }, { children: "\u200F\u200F\u200E \u200E" }), void 0))] }), void 0)); }; return Typewriter; }(React.Component)); export default Typewriter; function checkPropsForErrors(props) { if (props.cycle && !Array.isArray(props.cycle)) throw new TypeError("Exected an array for 'cycle' prop"); if (props.text && typeof props.text !== "string") throw new TypeError("Exected a string for 'text' prop"); if (props.text && typeof props.text !== "string") throw new TypeError("Exected a string for 'text' prop"); if (props.onMouseLeave && typeof props.onMouseLeave !== "function") throw new TypeError("Exected a function for 'onMouseLeave' prop"); if (props.onMouseEnter && typeof props.onMouseEnter !== "function") throw new TypeError("Exected a function for 'onMouseEnter' prop"); if (props.onClick && typeof props.onClick !== "function") throw new TypeError("Exected a function for 'onClick' prop"); if (props.loop && !props.cycle) throw new TypeError("Cannot loop when no cycle is provided"); }