RavelloH's Blog

LOADing...



Virgule.js现已发布

virgule-js-now-released

技术/设计/文档 6049

javascript


前言

22 年暑假时,我把博客首页的打字机特效换成了类似于现在这种,但是当时也是随便做出来玩玩的,不仅功能单一,结构臃肿,用了 530 行 js 也只能达到让主页有两句循环的轮播,实在太不优雅。 今年十月初我决定重制这个功能将其从 530 行压缩到 370 行,并在原有的基础上加入了跳过空格、自定义速度、快捷引用目标等功能,并将其封装为函数,现在想要使用只需一句:

virgule(target, context, speed);

太优雅了。 项目现已开源。@Github:RavelloH/virgule.js

效果

升级前后对比

升级前

old

升级后

new

即时体验

使用

直接引入 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 效果应用给整个博客的,但碍于实在找不到什么地方再去添加,先鸽了。

INFO

00:00


无正在播放的音乐
00:00/00:00

账号
User avatar
未登录未设置描述...