首页打字效果是如何实现的

打开首页会看到一行文字在逐字打字, 过几秒又换一句. 这个效果不复杂, 但很多人问怎么做的.

这篇说一下实现方式.

整体原理

首页加载时, 从一个 JSON 文件里随机选一句台词, 用 Typed.js 这个库模拟打字效果.

首页加载
  ↓
main.js 检测到当前是首页
  ↓
根据语言 (中文/英文) 确定取哪个字段
  ↓
从 /data/quotes.json 随机取一条
  ↓
传给 Typed.js, 开始打字动画

数据来源: quotes.json

台词存在 static/data/quotes.json 里.

[
  {
    "en": "Hope is a good thing, maybe the best of things, and no good thing ever dies.",
    "zh": "希望是美好的,也许是世间最美好的事物,而美好的事物永不消逝。",
    "author": "The Shawshank Redemption"
  },
  {
    "en": "Get busy living or get busy dying.",
    "zh": "忙着活,或忙着死。",
    "author": "The Shawshank Redemption"
  }
]

每条数据包含三个字段:

  • en: 英文台词
  • zh: 中文翻译
  • author: 出处 (电影, 书籍等)

目前收集了七百多条台词, 来自各种电影. 每次刷新首页随机展示一条, 不容易腻.

文件放在 static/ 下, 构建时直接复制到 public/data/quotes.json, 前端通过 AJAX 加载.

打字库: Typed.js

用的是 Typed.js, 一个专门做打字动画的库. 不依赖框架, jQuery 可用就行.

layouts/partials/js.html 里加载:

{{ if .IsHome }}
<script src="{{ absURL "/lib/typed.js" }}"></script>
{{ end }}

只在首页 (IsHome) 加载, 其他页面不加载. 文件大概 15KB, 不是首页就不下载.

有件事说一下: Typed.js 有两个大版本, 初始化和参数配置完全不一样. 网上搜到的示例可能是旧版的, 直接复制跑不起来. 我一开始就是对着旧版文档写了半天, 发现怎么都不对, 才发现版本没看对.

打字逻辑: main.js

核心逻辑在 static/js/main.js 里:

$(function () {
  var isHome = /^\/(en|zh)?\/?$/.test(location.pathname);
  if (!isHome) return;

  var lang = 'en';
  if (location.pathname.indexOf('/zh') === 0) {
    lang = 'zh';
  }

  $.get("/data/quotes.json", function (data) {
    if (!data || !data.length) return;
    var idx = Math.floor(Math.random() * data.length);
    var quote = data[idx];
    if (!quote) return;

    var text = (lang === 'zh' ? quote.zh : quote.en) + '\n—— ' + (quote.author || '');
    var typed = new Typed(".description .typed", {
      strings: [text],
      typeSpeed: 20,
      startDelay: 300,
      showCursor: true,
    });
  });
});

一步步拆解:

1. 判断是否为首页

var isHome = /^\/(en|zh)?\/?$/.test(location.pathname);

正则匹配 /, /en/, /zh/ 这些路径. 首页才执行打字逻辑, 文章页不执行.

2. 确定语言

var lang = 'en';
if (location.pathname.indexOf('/zh') === 0) {
  lang = 'zh';
}

根据 URL 路径判断当前语言. 中文首页 /zh/quote.zh, 英文首页 /en/quote.en.

3. 随机选一条台词

var idx = Math.floor(Math.random() * data.length);
var quote = data[idx];

从 quotes.json 里随机取一条. 纯随机, 不记录历史, 所以有可能连续两次刷新看到同一条.

4. 拼装文字

var text = (lang === 'zh' ? quote.zh : quote.en) + '\n—— ' + (quote.author || '');

文字 + 换行 + 出处. 比如:

And the rest is just noise.
—— The Social Network

5. 启动打字效果

var typed = new Typed(".description .typed", {
  strings: [text],
  typeSpeed: 20,
  startDelay: 300,
  showCursor: true,
});
  • strings: 要打出来的文字
  • typeSpeed: 打字速度, 20ms 一个字
  • startDelay: 延迟 300ms 开始, 让页面先渲染完
  • showCursor: 显示闪烁的光标

HTML 结构

首页模板里有一个容器来接收打字内容:

<div class="description">
  <p class="typed"></p>
</div>

Typed.js 会往这个元素里逐字写入文字.

为什么用 quotes.json 而不是 API

有考虑过每天从接口获取一句名言. 但静态博客的首页是纯静态文件, 不想依赖外部 API. 万一接口挂了, 首页就空一块.

所以直接把台词放在 JSON 文件里, 随构建一起部署. 没有外部依赖, 永远可用.

缺点是台词是固定的, 不能每天自动更新. 手动更新的话需要改 JSON 再部署. 但七百多条, 够刷一阵子了.

还有一个坑: quotes.json 文件位置放不对的话, 项目构建后路径会变, 前端就加载不到. 直接放在 static/data/ 下最稳, Hugo 会原样复制到 public/data/.

性能考虑

  • Typed.js 只在首页加载, 其他页面不加载
  • quotes.json 约 30KB, 只加载一次后浏览器缓存
  • 首页加载完后再请求 quotes.json, 不阻塞首屏渲染
  • 打字动画用 requestAnimationFrame 实现, 不占主线程

总体来说, 对性能的影响接近于零.

文章链接:

/zh/archive/blog-architecture-typing-effect/

# 相关文章推荐