Repository navigation
Expand file tree
/
Copy pathscript.js
More file actions
117 lines (106 loc) · 3.75 KB
/
Copy pathscript.js
File metadata and controls
117 lines (106 loc) · 3.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* ==========================================================================
famiji — GitHub Pages 主页脚本
========================================================================== */
(function () {
"use strict";
/* ---------- 0. 主题切换(日间/夜间) ---------- */
const root = document.documentElement;
let theme = "light";
try {
theme = localStorage.getItem("theme") || "light";
} catch (e) { /* 隐私模式等场景下忽略 */ }
root.setAttribute("data-theme", theme);
const toggle = document.getElementById("theme-toggle");
if (toggle) {
toggle.addEventListener("click", function () {
theme = theme === "dark" ? "light" : "dark";
root.setAttribute("data-theme", theme);
try {
localStorage.setItem("theme", theme);
} catch (e) { /* ignore */ }
});
}
/* ---------- 1. 滚动淡入 ---------- */
const revealEls = document.querySelectorAll(".reveal");
if ("IntersectionObserver" in window) {
const io = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
io.unobserve(entry.target);
}
});
},
{ threshold: 0.12 }
);
revealEls.forEach((el) => io.observe(el));
} else {
revealEls.forEach((el) => el.classList.add("visible"));
}
/* ---------- 2. 终端打字动画 ---------- */
const body = document.getElementById("terminal-body");
if (body) {
const lines = [
{ type: "cmd", text: "whoami" },
{ type: "out", text: "famiji" },
{ type: "cmd", text: "date" },
{ type: "out", text: new Date().toString() },
];
// 清空占位光标行
body.innerHTML = "";
let lineIdx = 0;
let charIdx = 0;
let currentLine = null;
function typeChar() {
// 每个命令逐字打出;普通输出直接整行出现
if (currentLine.type === "cmd") {
if (charIdx === 0) {
const div = document.createElement("div");
div.className = "t-line";
div.innerHTML =
'<span class="t-prompt">$ </span><span class="t-cmd"></span>';
currentLine.el = div.querySelector(".t-cmd");
body.appendChild(div);
}
currentLine.el.textContent = currentLine.text.slice(0, ++charIdx);
if (charIdx >= currentLine.text.length) return finishLine();
return setTimeout(typeChar, 34);
}
// 非命令:整行渲染
const div = document.createElement("div");
div.className = "t-line";
div.innerHTML =
currentLine.type === "ok"
? '<span class="t-ok"></span>'
: '<span class="t-out"></span>';
div.querySelector("span").textContent = currentLine.text;
body.appendChild(div);
return finishLine();
}
function finishLine() {
lineIdx++;
if (lineIdx >= lines.length) {
// 全部输出完:加一个常驻闪烁光标
const cur = document.createElement("div");
cur.className = "t-cursor-line";
cur.innerHTML = '<span class="t-prompt">$ </span><span class="cursor-blink">▊</span>';
body.appendChild(cur);
body.scrollIntoView({ behavior: "smooth", block: "nearest" });
return;
}
currentLine = lines[lineIdx];
charIdx = 0;
setTimeout(typeChar, currentLine.type === "cmd" ? 320 : 180);
}
// 等页面淡入区域就绪后再启动,略微延迟制造仪式感
const t = setTimeout(() => {
currentLine = lines[0];
typeChar();
clearTimeout(t);
}, 900);
}
/* ---------- 3. 页脚年份 ---------- */
const yearEl = document.getElementById("year");
if (yearEl) yearEl.textContent = String(new Date().getFullYear());
})();