前言
22 年暑假时,我把博客首页的打字机特效换成了类似于现在这种,但是当时也是随便做出来玩玩的,不仅功能单一,结构臃肿,用了 530 行 js 也只能达到让主页有两句循环的轮播,实在太不优雅。 今年十月初我决定重制这个功能将其从 530 行压缩到 370 行,并在原有的基础上加入了跳过空格、自定义速度、快捷引用目标等功能,并将其封装为函数,现在想要使用只需一句:
virgule(target, context, speed);
太优雅了。 项目现已开源。@Github:RavelloH/virgule.js
效果
升级前后对比
升级前
升级后
即时体验
使用
直接引入 JavaScript 脚本
<script src="https://ravelloh.github.io/virgule.js/virgule.js"></script>
手动复制源代码
// Author:RavelloH
// LICENCE: MIT
// Repo src: github.com/RavelloH/virgule.js
randArrMin = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9"];
randArr = ["あ","ぃ","い","ぅ","う","ぇ","え","ぉ","お","か","が","き","ぎ","く","ぐ","け","げ","こ","ご","さ","ざ","し","じ","す","ず","せ","ぜ","そ","ぞ","た","だ","ち","ぢ","っ","つ","づ","て","で","と","ど","な","に","ぬ","ね","の","は","ば","ぱ","ひ","び","ぴ","ふ","ぶ","ぷ","へ","べ","ぺ","ほ","ぼ","ぽ","ま","み","む","め","も","ゃ","や","ゅ","ゆ","ょ","よ","ら","り","る","れ","ろ","ゎ","わ","ゐ","ゑ","を","ん","ゔ","ゕ","ゖ","ァ","ア","ィ","イ","ゥ","ウ","ェ","エ","ォ","オ","カ","ガ","キ","ギ","ク","グ","ケ","ゲ","コ","ゴ","サ","ザ","シ","ジ","ス","ズ","セ","ゼ","ソ","ゾ","タ","ダ","チ","ヂ","ッ","ツ","ヅ","テ","デ","ト","ド","ナ","ニ","ヌ","ネ","ノ","ハ","バ","パ","ヒ","ビ","ピ","フ","ブ","プ","ヘ","ベ","ペ","ホ","ボ","ポ","マ","ミ","ム","メ","モ","ャ","ヤ","ュ","ユ","ョ","ヨ","ラ","リ","ル","レ","ロ","ヮ","ワ","ヰ","ヱ","ヲ","ン","ヴ","ヵ","ヶ","ヷ","ヸ","ヹ","ヺ","ー","ヾ","ㄅ","ㄆ","ㄇ","ㄈ","ㄉ","ㄊ","ㄋ","ㄌ","ㄍ","ㄎ","ㄏ","ㄐ","ㄑ","ㄒ","ㄓ","ㄔ","ㄕ","ㄖ","ㄗ","ㄘ","ㄙ","ㄝ","ㄞ","ㄟ","ㄠ","ㄡ","ㄢ","ㄣ","ㄤ","ㄥ","ㄦ","ㄧ","ㄨ","ㄩ","〇","口","甲","乙","丙","丁","戊","己","庚","辛","壬","癸",];
function virgule(target, context, speed = 100) {
//context重组
contextArr = [];
for (var i = 0; i < context.length; i++) {
contextArr.push(context[i])
}
// 添加/
target.innerHTML = "";
numVirgule = 0
var virgulegenerate = setInterval(
function() {
// 字符划分
if (escape(contextArr[numVirgule]).indexOf("%u") < 0) {
if (contextArr[numVirgule] == ' ') {
target.innerHTML += ' '
} else {
target.innerHTML += '/'
}
} else {
target.innerHTML += '//'
}
numVirgule += 1
if (numVirgule > context.length) {
clearInterval(virgulegenerate);
target.innerHTML = target.innerHTML.slice(0, target.innerHTML.length-1)
setTimeout(function() {
textIn()}, 100)
}
},
1000/speed)
// 文字进入
numIn = 0;
numCharacter = 0;
function textIn() {
originText = target.innerHTML;
var virgulereplace = setInterval(
function() {
numIn += 1
if (numIn >= contextArr.length) {
clearInterval(virgulereplace)
textwrite()
}
cacheText = ''
numCharacter = 0
for (i = 0; i < numIn; i++) {
if (escape(contextArr[i]).indexOf("%u") < 0) {
if (contextArr[i] == ' ') {
cacheText += ' '
numCharacter += 1
} else {
//单字符
var rand = Math.floor(Math.random() * randArrMin.length);
cacheText += randArrMin[rand]
numCharacter += 1
}
} else {
// 双字符
var rand = Math.floor(Math.random() * randArr.length);
cacheText += randArr[rand]
numCharacter += 2
}
}
target.innerHTML = cacheText + originText.slice(numCharacter, originText.length)
},
2000/speed)
// 原始文字写入
numWrite = 0
function textwrite() {
originText = target.innerHTML;
var virgulewrite = setInterval(
function() {
numWrite += 1
if (numWrite >= contextArr.length) {
clearInterval(virgulewrite)
}
cacheText = ''
numCharacter = 0
for (i = 0; i < numIn; i++) {
if (escape(contextArr[i]).indexOf("%u") < 0) {
if (contextArr[i] == ' ') {
cacheText += ' '
numCharacter += 1
} else {
//单字符
var rand = Math.floor(Math.random() * randArrMin.length);
cacheText += randArrMin[rand]
numCharacter += 1
}
} else {
// 双字符
var rand = Math.floor(Math.random() * randArr.length);
cacheText += randArr[rand]
numCharacter += 2
}
}
target.innerHTML = context.slice(0, numWrite) + cacheText.slice(numWrite, cacheText.length) + originText.slice(numCharacter, originText.length)
},
2000/speed)
}
}
}
用以上任意一种方法,获取到 JS 即可。接下来就是如何使用,也十分方便:
virgule(target,context,speed) //target context必填,speed可选填,默认100
//example:
virgule(document.getElementById('jumping'), 'Place the text you want as the result here',100)
//对文档中一个id为jumping的元素使用virgule效果,目标文字是"Place the text you want as the result here",速度为100
需要注意的是,此项目需搭配等宽字体使用,如自带的 Courier New、Terminal 等,或者自己引入其他等宽字体。 这里推荐 Microsoft Yahei Mono 和 SF Mono SC。
实现方法
注:下方行数表示以上 Js 代码所处行数。4-5:定义了两个列表randArrMin
以及randArr
,前者用于一个英文字符宽的字符的替换,后者用于一个中文字符宽的替换。7:定义了 virgule 的主函数,默认参数中target``context
必填,speed
选填,默认 100。8-12:将 context 中的内容转换到 contextArr 中储存。14-15:重置目标16-36:创建延迟循环 virgulegenerate,间隔 1000/speed 毫秒。
- 18-28:判断中英文,中文插入两个/,英文插入一个/,空格插入空格。
- 29-34:判断何时结束斜杠的插入动作,延迟 100ms 唤起 textIn()
39-40:重置变量41-109:textIn()主函数,用于将生成的斜杠替换为 context 文字。
- 43:创建定时器 virgulereplace,间隔 2000/speed 毫秒
- 46-49:终止器,用于在此环节结束后呼出下一个操作函数 textWrite()
- 52-69:二次递归循环,用于按上一级循环进行量将对应数量斜杠替换为随机文字,同样分双字符、单字符、空格三种情况。
- 70:对应写入,切分随机字符组与斜杠字符组,保证总长度不变。
16-108:textIn()函数下的二级函数 textWrite(),用于在将 target 中所有字符替换为随机字符后,继续将随机字符替换为 context()
- 79-83:在所有过程结束后终止定时器
- 86-103:同 52-69,将 contextArr 中内容逐个添加进去。
- 104:重组字符串,酱三个列表中的内容整合写入 target 中。
关键分析
其实整个过程中最复杂的是三个插入过程的顺序。这简单来说,分为以下三个阶段: 1.用斜杠覆盖文本。 2.逐渐将斜杠替换为随机文字。这一过程中,每将一个斜杠组替换一个新字符,就会重新将它前面的随机字符再随机化。 3.用 target 中的文字替换随机文字。这一过程大体与 2 相反,没将一个随机字符替换为目标字符,就会刷新其后的随机字符。
以上三个过程顺次进行,每个过程完成后唤起下一个过程。如果还不理解,可以在#效果章节的在线体验中,选择较低的速度运行,以理解这三个过程。
后言
上述就是 virgule.js 现有的功能介绍和使用方法,顺带着也写了大体的实现方法。 本来打算把 virgule 效果应用给整个博客的,但碍于实在找不到什么地方再去添加,先鸽了。