让代码像是打字一样显示在页面上

之前有一个介绍自研产品的任务,落地页需要用代码的形式展示一下安装方法,核心的几个概念,需求就是这样,让代码一行一行展示出来,字符一个个蹦出来,就好像大模型用代码作答一样。 之前没有考虑好,写的很匆促,今天在官网上又看到自己写的那个玩意,想想是不是可以写的再稍微好点。😆

我要做的是展示下面的代码(PS我网上随便找的一点Python代码,可行性与否不用在意,演示数据而已)

python 复制代码
class SimpleBook:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    
    def __str__(self):
        return f"'{self.title}' by {self.author}"

class MiniLibrary:
    def __init__(self):
        self.collection = []
    
    def add_book(self, book):
        self.collection.append(book)
        print(f"Added: {book}")

# 创建书籍和小型图书馆实例
book1 = SimpleBook("奇幻旅程", "王小小")
my_library = MiniLibrary()

# 添加书籍到图书馆
my_library.add_book(book1)

# 打印特定书籍信息
print(my_library.collection[0])

之前的想法是,每一行都是一个span标签包裹,并且为变量和函数还要给不同的行类样式,有点呆🤣。现在我们直接使用highlight.js,使用起来那是相当方便,因为我是React,我就下载react-highlight,它已经把highlight.js作为依赖了。npm i react-highlight -S安装好之后,浅浅试一下,当然我们还可以选择自己的代码风格,我这里使用monokai;

tsx 复制代码
import Highlight from 'react-highlight';
import 'highlight.js/styles/monokai.css'; // 代码风格
import './index.scss'; // 滚动条,文字大小,居中显示等

const pyCode = `
class SimpleBook:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    
    def __str__(self):
        return f"'{self.title}' by {self.author}"

class MiniLibrary:
    def __init__(self):
        self.collection = []
    
    def add_book(self, book):
        self.collection.append(book)
        print(f"Added: {book}")

# 创建书籍和小型图书馆实例
book1 = SimpleBook("奇幻旅程", "王小小")
my_library = MiniLibrary()

# 添加书籍到图书馆
my_library.add_book(book1)

# 打印特定书籍信息
print(my_library.collection[0])
`;

export default function CodeTyping() {

  return (
    <div className="code-stage">
      <Highlight className='python-code' language="python">
        {pyCode}
      </Highlight>
    </div>
  );
}

做好了展示,接下来做一下打字效果,我的想法是pyCode作为源数据,再用一个变量typedCode作为响应式数据,定时器定时更新下typedCode。

tsx 复制代码
export default function CodeTyping() {
  const [typedCode, setTypedCode] = useState('');
  const indexRef = useRef(0); // 使用 useRef 来保存当前索引

  useEffect(() => {
    const intervalId = setInterval(() => {
      if (indexRef.current < pyCode.length) {
        setTypedCode((prevTypedCode) => prevTypedCode + pyCode[indexRef.current]);
        indexRef.current++;
      } else {
        clearInterval(intervalId);
      }
    }, 50); // 调整间隔时间以改变打字速度

    return () => clearInterval(intervalId);
  }, []);

  return (
    <div className="code-stage">
      {/* 使用 Highlight 组件包裹你的代码,并设置语言属性 */}
      <Highlight className='python-code' language="python">
        {typedCode}
      </Highlight>
    </div>
  );
}

效果是有了的,但是仔细一看不对劲,代码风格没有了,我检查了一下import 'highlight.js/styles/monokai.css';这个文件是存在的,那么是别的问题,检查元素才发现,标签类名变化了,由原来的hljs-classhljs-functionhljs-title这样的类名变成了hljs-stringhljs-attibute这样的类名了, 那只能是Highlight组件接收我切割下来的不全的代码,没有做到预先的效果。搜了一圈,有两种解决方案:

  • 在打字过程中,使用 <pre> 标签显示普通文本;打字完成后,使用 Highlight 组件渲染高亮代码。它存在一个问题,那就是<pre>标签展示不太美观,会和后面的样子有不小出入,调整比较麻烦;
  • 第二种方法,直接令Highlight组件重新渲染,使用 key={typedCode.length} 强制 Highlight 组件在每次 typedCode 更新时重新渲染,这样可以确保 Highlight 组件每次都重新解析代码,从而实现动态高亮。

我这里使用第二种方法,下面是完整代码:

tsx 复制代码
export default function CodeTyping() {
  const [typedCode, setTypedCode] = useState('');
  const indexRef = useRef(0);

  useEffect(() => {
    const intervalId = setInterval(() => {
      if (indexRef.current < pyCode.length) {
        setTypedCode((prevTypedCode) => prevTypedCode + pyCode[indexRef.current]);
        indexRef.current++;
      } else {
        clearInterval(intervalId);
      }
    }, 50);

    return () => clearInterval(intervalId);
  }, []);

  return (
    <div className="code-stage">
      {/* 使用 key 强制重新渲染 Highlight 组件 */}
      <Highlight key={typedCode.length} className='python-code' language="python">
        {typedCode}
      </Highlight>
    </div>
  );
}

这里是稍微牺牲了一点性能的,不过我没有想出更好的办法,各位有更好的实现,欢迎前来指导😁

相关推荐
束尘17 分钟前
react实现虚拟列表
前端·javascript·react.js
ZoeLandia18 分钟前
vue3源码分析 -- reactive
前端·vue·源码
编码七号22 分钟前
【react】工程项目中的通过自定义Hook进行路由设计以及路由鉴权
前端·javascript·react.js
前端小鸡23 分钟前
react复习笔记一
前端·笔记·react.js
Moment25 分钟前
重新前端工程化:想了解前端工程化,PNPM 你可不能落下 😎😎😎
前端·javascript·node.js
陈大爷(有低保)27 分钟前
若依前端框架增删改查
前端·vue.js·elementui
Gazer_S30 分钟前
【Axios 开发中的代理配置陷阱与解决方案】
前端·javascript·vue.js
好_快33 分钟前
Lodash源码阅读-arraySome
前端·javascript·源码阅读
好_快33 分钟前
Lodash源码阅读-arrayMap
前端·javascript·源码阅读
*TQK*33 分钟前
HTML课后实践
前端·学习·html