UNPKG

fanyucomponents

Version:

一款以 純邏輯為核心、無樣式綁定 的 React 元件套件

85 lines (84 loc) 3.13 kB
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";