markdown版打字機(jī)組件實(shí)現(xiàn)

前言

隨著2024年國內(nèi)業(yè)務(wù)需求的持續(xù)發(fā)展,前端領(lǐng)域打字機(jī)效果組件已廣泛應(yīng)用于多種網(wǎng)站和應(yīng)用程序中,有效增強(qiáng)了網(wǎng)站的動(dòng)態(tài)性與交互性。本文旨在深入探討如何從基礎(chǔ)開始,逐步開發(fā)實(shí)現(xiàn)簡易版的一個(gè)打字機(jī)效果的流式組件,包括示例代碼及其詳細(xì)解釋。

效果

打字機(jī)效果

一、初始準(zhǔn)備

開始開發(fā)前,我們首先需要準(zhǔn)備一些基礎(chǔ)內(nèi)容。本次示例中,我們將以一個(gè)Markdown文本數(shù)據(jù)作為打字機(jī)效果的內(nèi)容來源。這不僅能夠模擬實(shí)際開發(fā)中的場景,也便于我們展示如何處理和展示復(fù)雜文本。

示例Markdown文本

我們的Markdown文本包含了標(biāo)題、文本格式(如粗體、斜體)、圖片和表格等元素,這些都是Markdown常用的標(biāo)記元素。通過對這些不同類型的內(nèi)容進(jìn)行逐字展示,我們可以演示打字機(jī)效果處理復(fù)雜文本的能力。

export const mockMarkdownStr = `
# Markdown 流式文章示例
Markdown是一種輕量級標(biāo)記語言,它允許人們使用易讀易寫的純文本格式編寫文檔。Markdown編寫的文檔后綴為 \`.md\`。在這篇文章中,我將向您展示如何使用Markdown創(chuàng)建一篇包含標(biāo)題、文本、圖片和表格的文章。

## 標(biāo)題
在Markdown中,標(biāo)題是通過在文字前面加上 \`#\` 來表示的。\`#\` 的數(shù)量代表標(biāo)題的級別。例如,一個(gè) \`#\` 代表一級標(biāo)題,兩個(gè) \`##\` 代表二級標(biāo)題,以此類推。

## 文本
Markdown支持普通的文本格式,如粗體、斜體、刪除線和下劃線。您可以使用以下方式創(chuàng)建這些文本格式:
- **粗體**:使用兩個(gè) \`\*\` 或 \`_\` 包圍文本,例如 \`\*\*粗體\*\*\` 或 \`__粗體__\`
- *斜體*:使用一個(gè) \`\*\` 或 \`_\` 包圍文本,例如 \`\*斜體\*\` 或 \`_斜體_\`
- ~~刪除線~~:使用兩個(gè) \`~\` 包圍文本,例如 \`~~刪除線~~\`
- <u>下劃線</u>:使用HTML標(biāo)簽 \`<u>\` 和 \`</u>\` 包圍文本,例如 \`<u>下劃線</u>\`

## 圖片
在Markdown中,您可以使用以下語法插入圖片:
\`\`\`
![圖片描述](圖片地址 "可選的標(biāo)題")
\`\`\`
例如:
\`\`\`
![這是一張小米SU7圖片](https://s1.xiaomiev.com/activity-outer-assets/0328/images/su7/su7_1.jpg)
\`\`\`

## 表格
Markdown支持簡單的表格創(chuàng)建。您可以使用以下語法創(chuàng)建表格:
\`\`\`
| 標(biāo)題1 | 標(biāo)題2 | 標(biāo)題3 |
|-------|-------|-------|
| 單元格1 | 單元格2 | 單元格3 |
| 單元格4 | 單元格5 | 單元格6 |
\`\`\`
例如:
\`\`\`
| 姓名 | 年齡 | 性別 |
|------|------|------|
| 張三 | 25   | 男   |
| 李四 | 22   | 女   |
\`\`\`

## 結(jié)束語
以上是Markdown的基本用法,通過這些簡單的標(biāo)記,您可以創(chuàng)建一篇包含標(biāo)題、文本、圖片和表格的文章。Markdown的語法簡單易懂,適合快速排版和分享文檔。希望這篇文章對您有所幫助!
`;

二、代碼實(shí)現(xiàn)

實(shí)現(xiàn)打字機(jī)效果涉及三個(gè)部分:

  • 核心邏輯TypeWriterCore
  • Hook封裝useTypeWriter
  • React組件實(shí)現(xiàn)TypeWriter Components
    三個(gè)部分業(yè)務(wù)側(cè)可按需取用,以下部分將詳細(xì)介紹每一部分的實(shí)現(xiàn)邏輯及關(guān)鍵代碼。

1. TypeWriterCore.ts - 打字機(jī)核心邏輯

TypeWriterCore.ts文件中定義了TypeWriterCore類,這個(gè)類封裝了打字機(jī)效果的核心邏輯。通過構(gòu)造函數(shù),我們可以傳入不同的配置選項(xiàng),如打字速度、暫停時(shí)間等,以適應(yīng)不同的使用場景。

interface TypeWriterCoreOptions {
    onConsume: (str: string) => void; // 定義一個(gè)回調(diào)函數(shù),用于消費(fèi)(處理)字符
    maxStepSeconds?: number; // 可選屬性,定義最大步進(jìn)間隔(毫秒)
}

export default class TypeWriterCore {
    onConsume: (str: string) => void; // 消費(fèi)(處理)字符的回調(diào)函數(shù)
    queueList: string[] = []; // 存儲(chǔ)待消費(fèi)字符的隊(duì)列
    maxStepSeconds: number = 50; // 默認(rèn)最大步進(jìn)間隔為50毫秒
    maxQueueNum: number = 2000; // 隊(duì)列中最大字符數(shù)
    timer: number | undefined; // 用于控制下一次消費(fèi)的定時(shí)器

    constructor({onConsume, maxStepSeconds}: TypeWriterCoreOptions) {
        this.onConsume = onConsume; // 初始化消費(fèi)字符的回調(diào)

        if (maxStepSeconds !== undefined) {
            this.maxStepSeconds = maxStepSeconds; // 如果提供了最大步進(jìn)間隔,則使用提供的值
        }
    }

    // 動(dòng)態(tài)計(jì)算消費(fèi)字符的速度
    dynamicSpeed() {
        const speedQueueNum = this.maxQueueNum / this.queueList.length; // 根據(jù)隊(duì)列長度動(dòng)態(tài)調(diào)整速度
        const resNum = +(
            speedQueueNum > this.maxStepSeconds
                ? this.maxStepSeconds : speedQueueNum
        ).toFixed(0); // 確保結(jié)果為整數(shù)

        return resNum;
    }

    // 將字符串添加到隊(duì)列中
    onAddQueueList(str: string) {
        this.queueList = [...this.queueList, ...str.split('')]; // 分解字符串為字符數(shù)組并追加到隊(duì)列
    }

    // 添加字符串到隊(duì)列的公共方法
    add(str: string) {
        if (!str) return; // 如果字符串為空,則不執(zhí)行任何操作
        this.onAddQueueList(str); // 調(diào)用內(nèi)部方法添加字符串到隊(duì)列
    }

    // 從隊(duì)列中消費(fèi)一個(gè)字符
    consume() {
        if (this.queueList.length > 0) {
            const str = this.queueList.shift(); // 從隊(duì)列頭部移除一個(gè)字符
            str && this.onConsume(str); // 如果字符存在,則調(diào)用消費(fèi)函數(shù)處理該字符
        }
    }

    // 定時(shí)消費(fèi)隊(duì)列中的字符
    next() {
        this.timer = setTimeout(() => {
            if (this.queueList.length > 0) {
                this.consume(); // 消費(fèi)一個(gè)字符
                this.next(); // 遞歸調(diào)用,繼續(xù)消費(fèi)下一個(gè)字符
            }
        }, this.dynamicSpeed()); // 根據(jù)動(dòng)態(tài)速度設(shè)置定時(shí)器
    }

    // 開始消費(fèi)隊(duì)列中的字符
    start() {
        this.next(); // 調(diào)用next方法開始消費(fèi)字符
    }

    // 渲染完成后的清理工作
    onRendered() {
        clearTimeout(this.timer); // 清除定時(shí)器,防止繼續(xù)消費(fèi)字符
    }

    // 清空隊(duì)列并停止當(dāng)前的消費(fèi)過程
    onClearQueueList() {
        this.queueList = []; // 清空字符隊(duì)列
        clearTimeout(this.timer); // 清除定時(shí)器
    }
}

2. useTypeWriter.ts - Hook封裝

通過HookuseTypeWriter封裝TypeWriterCore類,提供簡潔的接口,使得在React或Vue組件中易于實(shí)現(xiàn)打字機(jī)效果。

代碼示例如下:

import {useEffect, useState, useMemo} from 'react';
import TypeWriterCore from './TypeWriterCore';

interface UseWriterOptions {
    maxStepSeconds?: number; // 將 maxStepSeconds 定義為可選的
}

export const useTypeWriter = (
    {text, options}:
    { text: string, options?: UseWriterOptions }
) => {
    const [typedText, setTypedText] = useState('');

    const typingCore = useMemo(
        () => new TypeWriterCore(
            {
                onConsume: (str: string) => setTypedText(prev => prev + str),
                ...options,
            }
        ),
        []
    );

    useEffect(
        () => {
            typingCore.onRendered(); // 渲染完成 => 清空定時(shí)器
            typingCore.add(text);
            typingCore.start();

            return () => typingCore.onRendered(); // 渲染完成 => 清空定時(shí)器
        },
        [text]
    );

    return [typedText];
};

3. index.tsx - 組件實(shí)現(xiàn)示例

這個(gè)文件展示了如何在React組件中使用useTypeWriterHook來實(shí)現(xiàn)打字機(jī)效果。以下是實(shí)現(xiàn)的關(guān)鍵部分:

import React from 'react';
import ReactMarkdown from 'react-markdown';
import {useTypeWriter} from './useTypeWriter'; // 替換為實(shí)際的導(dǎo)入路徑
import TypeWriterCore from './TypeWriterCore';


interface TypingWriterProps {
  text: string;
  options?: {
    maxStepSeconds?: number;
  };
}

const TypingWriter: React.FC<TypingWriterProps> = ({text, options = {}}) => {
    const [typedText] = useTypeWriter({text, options});

    return (
        <div>
            <ReactMarkdown>
                {typedText}
            </ReactMarkdown>
        </div>
    );
};


export {
  TypingWriter,
  TypeWriterCore,
  useTypeWriter,
};

通過這三個(gè)文件的詳細(xì)解析和代碼實(shí)現(xiàn),我們展示了從核心邏輯的構(gòu)建到在React組件中的應(yīng)用,如何逐步開發(fā)一個(gè)打字機(jī)效果的流式組件。

三、應(yīng)用示例

1、模擬流式文本消息推送:

以下示例展示如何模擬SSE(Server-Sent Events)文本消息推送,模擬實(shí)時(shí)數(shù)據(jù)流的場景。

export const simulateWebSocketPush = (text, onDataReceived) => {
    const words = text.split(/([\s,。;:!、])/); // 在標(biāo)點(diǎn)符號前后插入分隔符,以便保留標(biāo)點(diǎn)符號
    let currentIndex = 0;

    // 模擬推送函數(shù)
    function pushNextChunk() {
        const chunkSize = Math.floor(Math.random() * 5) + 1; // 隨機(jī)生成 1 到 5 的字?jǐn)?shù)
        const currentChunk = words.slice(currentIndex, currentIndex + chunkSize).join('');
        currentIndex += chunkSize;

        // 模擬推送,實(shí)際中需要通過 WebSocket 推送給客戶端
        // 這里將數(shù)據(jù)通過回調(diào)函數(shù)傳遞給調(diào)用方
        onDataReceived(currentChunk);

        // 繼續(xù)推送,直到所有文字都被推送完
        if (currentIndex < words.length) {
            const interval = Math.floor(Math.random() * 500) + 1000; // 隨機(jī)生成時(shí)間間隔
            setTimeout(pushNextChunk, interval);
        }
    }

    // 開始推送
    pushNextChunk();
};

2. 使用示例React

此示例展示如何在應(yīng)用中模擬文本數(shù)據(jù)傳入,實(shí)現(xiàn)打字機(jī)效果。

import {useEffect, useState} from 'react';
import {TypingWriter} from 'ui-type-writer';
import {simulateWebSocketPush} from '@/utils';
import {mockMarkdownStr} from '@/mock/index'

export default function App() {
    const [markdownContent, setMarkdownContent] = useState(' ');

    useEffect(
        () => {
        // 在組件掛載時(shí)開始模擬WebSocket推送
            simulateWebSocketPush(mockMarkdownStr, data => {
            // 這里處理每次推送的數(shù)據(jù),可以將數(shù)據(jù)存儲(chǔ)到狀態(tài)中,或者進(jìn)行其他操作
                // 在每次推送時(shí)拼接數(shù)據(jù)
                setMarkdownContent(data);
            });
            // 清理定時(shí)器或其他資源
            return () => {};
        },
        []
    );

    return (
        <TypingWriter text={markdownContent} />
    );
}

npm包發(fā)布

打字機(jī)效果組件已發(fā)布到npm,未進(jìn)行預(yù)編譯,可以在目錄下直接調(diào)試。

npm install  ui-type-writer

希望本文能為您提供有價(jià)值的參考與指導(dǎo),如有疑問或建議,敬請留言討論。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容