fanyucomponents
Version:
一款以 純邏輯為核心、無樣式綁定 的 React 元件套件
85 lines (84 loc) • 3.13 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import React, { useEffect, useRef, useState } from "react";
export const TypeWriterText = React.forwardRef(({ children, speed = 20, // 每秒字元數
startDelay = 0, pause = false, cursor = "|", className = "", cursorStyle, onComplete, shouldDelete = false, deleteDelay = 1000, loop = false, onTyping, ...rest }, ref) => {
const [index, setIndex] = useState(null);
const [action, setAction] = useState("typing");
const timeoutRef = useRef(null);
// 當文字變化時重置狀態
useEffect(() => {
setIndex(null);
}, [children]);
useEffect(() => {
if (pause)
return;
const interval = 1000 / speed;
if (index === null) {
// 等待指定時間後開始打字
timeoutRef.current = setTimeout(() => {
setIndex(0);
setAction("typing");
}, startDelay);
return;
}
if (action === "typing") {
if (index < children.length) {
timeoutRef.current = setTimeout(() => {
setIndex((prev) => (prev !== null && prev !== void 0 ? prev : -1) + 1);
}, interval);
}
else {
if (shouldDelete) {
timeoutRef.current = setTimeout(() => {
setAction("deleting");
}, deleteDelay);
}
else {
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
}
}
}
if (action === "deleting") {
if (index > 0) {
timeoutRef.current = setTimeout(() => {
setIndex((prev) => (prev !== null && prev !== void 0 ? prev : 1) - 1);
}, interval);
}
else {
if (loop) {
setIndex(null);
}
else {
onComplete === null || onComplete === void 0 ? void 0 : onComplete();
}
}
}
return () => {
if (timeoutRef.current)
clearTimeout(timeoutRef.current);
};
}, [
index,
action,
children,
speed,
pause,
startDelay,
deleteDelay,
shouldDelete,
loop,
onTyping,
onComplete,
]);
useEffect(() => {
if (index !== null &&
action === "typing" &&
index < children.length &&
onTyping) {
onTyping(children[index], index);
}
}, [index, action, children, onTyping]);
return (_jsxs("span", { ref: ref, className: className, ...rest, children: [children.slice(0, index !== null && index !== void 0 ? index : 0), !((action === "typing" && index === children.length) ||
(action === "deleting" && index === 0)) && (_jsx("span", { style: { display: "inline-block", ...cursorStyle }, children: cursor }))] }));
});
TypeWriterText.displayName = "TypeWriterText";