首页打字效果是如何实现的
打开首页会看到一行文字在逐字打字, 过几秒又换一句. 这个效果不复杂, 但很多人问怎么做的.
这篇说一下实现方式.
整体原理
首页加载时, 从一个 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实现, 不占主线程
总体来说, 对性能的影响接近于零.