react-teffex
Version:
Text Effects: Stateful React text effects for amazing websites
248 lines (246 loc) • 11.8 kB
JavaScript
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");
}