LOADING

- 系统启动中

仿Nikki联动尼尔的启动动画

尝试用 CSS 和 JS 实现一些动画。--暂未完成。

前言

  • NIKKI 联动尼尔的启动动画看着好棒,想搬来当自己的启动动画,但是很明显没有现成的,只能自己做(耗时 4 小时)。

  • 没有合适的字体,所以采用的是网站字体。

  • 动画效果采用的是 CSS 动画和 js 动画混合,效果还行,暂时还没做转场(10 个特定线生长,菱形变化)。

  • 更多动画,没有动手能力的可以去UIverse找。

  • 参考链接:【NIKKE 胜利女神】OuteR:Automata 尼尔联动 剧情纯享 完结 横屏 2K60 帧

补充

  • 莫名其妙手机访问的话会很慢加载资源,甚至无法加载

效果预览

效果预览

效果预览

代码

基本实现:

  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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        @font-face {
            font-family: 'MyCustomFont';
            src: url('/font/MarukoGothicCJKjp-Regular.ttf') format('truetype');
        }

        body {
            background-color: #1d1d1d;
            font-family: monospace;
            margin: 20px;
            padding: 0;
            font-family: 'MyCustomFont', sans-serif;
            display: flex;
            flex-direction: column;
            min-height: 100vh;
        }

        h1 {
            opacity: 0;
            animation: fadeIn 1.5s ease-in forwards;
            color: #b4b4b4;
            display: inline;
            white-space:pre;
        }

        .loading {
            display: block;
            margin-bottom: 20px;
        }

        .typewriter {
            position: relative;
            width: 100%;
            max-width: 600px;
        }

        .typewriter .line {
            display: block;
            overflow: hidden;
            white-space: nowrap;
            margin-left: 10px;
            margin-top: -1px;
            color: #b4b4b4;
            font-weight: bold;
            letter-spacing: 2px;
            height: 1.5em;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
            }

            to {
                opacity: 1;
            }
        }
    </style>
</head>

<body>
    <div class="loading">
        <h1>LOADING</h1>
        <h1 id="loadingText" style="font-size: 15px;"> - 系统启动中</h1>
    </div>
    <div class="typewriter">
        <span class="line" data-text="Neuro"></span>
        <span class="line" data-text="Implanted"></span>
        <span class="line" data-text="Machine for"></span>
        <span class="line" data-text="Protecting"></span>
        <span class="line" data-text="Human"></span>
        <span class="line" data-text="突触连接:OFF"></span>
        <span class="line" data-text="删除本机数据"></span>
        <span class="line" data-text="网络连接"></span>
        <span class="line" data-text="下载最新数据:完成"></span>
        <span class="line" data-text="确认数据损失率:正常范围"></span>
        <span class="line" data-text="确认神经元受损度:正常"></span>
        <span class="line" data-text="确认神经元处理速度:正常"></span>
        <span class="line" data-text="突触链接:ON"></span>
        <span class="line" data-text="确认路由信息恢复率:正常范围"></span>
        <span class="line" data-text="确认通信状态:良好"></span>
        <span class="line" data-text="NIMPH系统:全部正常"></span>
        <span class="line" data-text="数据自动保存"></span>
        <span class="line" data-text="准备完毕"></span>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', async function () {
            // 获取元素
            const lines = Array.from(document.querySelectorAll('.line'))
                .filter(line => line.dataset.text && line.dataset.text.trim() !== '');
            const loadingText = document.getElementById('loadingText');

            const lineEnd = lines[lines.length - 1];

            // 定义加载动画状态
            let dotCount = 0;
            let loadingInterval;

            // 启动加载动画
            function startLoadingAnimation() {
                loadingInterval = setInterval(() => {
                    dotCount = (dotCount + 1) % 4; // 0-3循环
                    loadingText.textContent = '  -  系统启动中' + '.'.repeat(dotCount);
                }, 500); // 500ms更新一次
            }

            // 停止加载动画
            function stopLoadingAnimation() {
                clearInterval(loadingInterval);
                loadingText.textContent = '  -  系统启动完成'; // 动画结束后显示完成状态
            }

            // 打字动画函数
            async function typeLine(line) {
                const fullText = line.dataset.text;
                line.textContent = '';
                if (!fullText) return;

                const firstChar = fullText[0] || '|';
                let currentIndex = 1;
                line.textContent = firstChar;

                return new Promise(resolve => {
                    function type() {
                        if (currentIndex < fullText.length) {
                            line.textContent = fullText.substring(0, currentIndex) + firstChar;
                            currentIndex++;
                            setTimeout(type, 20 + Math.random() * 15);
                        } else {
                            line.textContent = fullText;
                            resolve();
                        }
                    }
                    setTimeout(type, 20);
                });
            }

            // 执行流程:淡入动画 -> 加载动画 -> 打字动画
            startLoadingAnimation(); // 先启动加载动画

            // 等待淡入动画完成
            await new Promise(resolve => setTimeout(resolve, 1500));

            // 处理除最后一行外的所有行
            for (let i = 0; i < lines.length - 1; i++) {
                await typeLine(lines[i]);
                await new Promise(resolve => setTimeout(resolve, 100));
            }

            await new Promise(resolve => setTimeout(resolve, 500));
            await typeLine(lineEnd);


            // 所有打字动画完成后,停止加载动画
            // stopLoadingAnimation();
        });
    </script>
</body>

</html>

实际使用版本:

  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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
<!DOCTYPE html>
<html lang="zh-CN">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    @font-face {
      font-family: 'MyCustomFont';
      src: url('/font/MarukoGothicCJKjp-Regular.ttf') format('truetype');
    }


    .loadingBox {
        position: fixed;
        justify-content: center;
        align-items: center;
        display: none;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        background-color: #1d1d1d;
        z-index: 9999;
    }

    .Bigbox {
      background-color: #1d1d1d;
      font-family: monospace;
      margin: 20px;
      padding: 0;
      font-family: 'MyCustomFont', sans-serif;
      display: flex;
      flex-direction: column;
      min-height: 100vh;
    }

    h1 {
      opacity: 0;
      animation: fadeIn 1.5s ease-in forwards;
      color: #b4b4b4;
      display: inline;
      white-space: pre;
    }

    .loading {
      display: block;
      margin-bottom: 20px;
    }

    .typewriter {
      position: relative;
      width: 100%;
      max-width: 600px;
    }

    .typewriter .line {
      display: block;
      overflow: hidden;
      white-space: nowrap;
      margin-left: 10px;
      margin-top: -1px;
      color: #b4b4b4;
      font-weight: bold;
      letter-spacing: 2px;
      height: 1.5em;
    }

    @keyframes fadeIn {
      from {
        opacity: 0;
      }

      to {
        opacity: 1;
      }
    }
  </style>
</head>

<body>
  <div class="loadingBox">
    <div class="Bigbox">
      <div class="loading">
        <h1>LOADING</h1>
        <h1 id="loadingText" style="font-size: 15px;"> - 系统启动中</h1>
      </div>
      <div class="typewriter">
        <span class="line" data-text="Neuro"></span>
        <span class="line" data-text="Implanted"></span>
        <span class="line" data-text="Machine for"></span>
        <span class="line" data-text="Protecting"></span>
        <span class="line" data-text="Human"></span>
        <span class="line" data-text="突触连接:OFF"></span>
        <span class="line" data-text="删除本机数据"></span>
        <span class="line" data-text="网络连接"></span>
        <span class="line" data-text="下载最新数据:完成"></span>
        <span class="line" data-text="确认数据损失率:正常范围"></span>
        <span class="line" data-text="确认神经元受损度:正常"></span>
        <span class="line" data-text="确认神经元处理速度:正常"></span>
        <span class="line" data-text="突触链接:ON"></span>
        <span class="line" data-text="确认路由信息恢复率:正常范围"></span>
        <span class="line" data-text="确认通信状态:良好"></span>
        <span class="line" data-text="NIMPH系统:全部正常"></span>
        <span class="line" data-text="数据自动保存"></span>
        <span class="line" data-text="准备完毕"></span>
      </div>
    </div>
  </div>




  <script>
    document.addEventListener('DOMContentLoaded', async function () {

      // 使用类选择器获取元素
      const loading = document.querySelector('.loadingBox');

      // 获取元素
      const lines = Array.from(document.querySelectorAll('.line'))
        .filter(line => line.dataset.text && line.dataset.text.trim() !== '');
      const loadingText = document.getElementById('loadingText');//此处根据id获取元素--纯扩展
      const lineEnd = lines[lines.length - 1];

      // 定义加载动画状态
      let dotCount = 0;
      let loadingInterval;

      // 启动加载动画
      function startLoadingAnimation() {
        loadingInterval = setInterval(() => {
          dotCount = (dotCount + 1) % 4; // 0-3循环
          loadingText.textContent = '  -  系统启动中' + '.'.repeat(dotCount);
        }, 500); // 500ms更新一次
      }

      // 停止加载动画
      function stopLoadingAnimation() {
        // clearInterval(loadingInterval);
        // loadingText.textContent = '  -  系统启动完成'; // 动画结束后显示完成状态


        // 创建并执行动画
        const fadeOutAnimation = loading.animate([
          { opacity: 1 },
          { opacity: 0 }
        ], {
          duration: 800,
          fill: 'forwards'
        });

        // 动画结束后隐藏元素
        fadeOutAnimation.finished.then(() => {
          loading.style.display = "none";
          sessionStorage.setItem('visited', 'true');//设置sessionStorage,标识为已被访问
        });
      }

      // 打字动画函数
      async function typeLine(line) {
        const fullText = line.dataset.text;
        line.textContent = '';
        if (!fullText) return;

        const firstChar = fullText[0] || '|';
        let currentIndex = 1;
        line.textContent = firstChar;

        return new Promise(resolve => {
          function type() {
            if (currentIndex < fullText.length) {
              line.textContent = fullText.substring(0, currentIndex) + firstChar;
              currentIndex++;
              setTimeout(type, 20 + Math.random() * 15);
            } else {
              line.textContent = fullText;
              resolve();
            }
          }
          setTimeout(type, 20);
        });
      }


      // 新增:等待所有资源加载完成(DOMContentLoaded不检测字体等资源是否加载完毕,所以需要另行检测)
      function waitForResourcesLoaded() {
        return new Promise(resolve => {
          if (document.readyState === 'complete') {
            resolve();
          } else {
            window.addEventListener('load', resolve);
          }
        });
      }

        const isFirstVisit = !sessionStorage.getItem('visited');  //定义判断是否为第一次访问

        if (isFirstVisit) {
          loading.style.display = "block";

          // 执行流程:淡入动画 -> 加载动画 -> 打字动画
          startLoadingAnimation(); // 先启动加载动画

          // 等待淡入动画完成
          await new Promise(resolve => setTimeout(resolve, 1500));

          // 处理除最后一行外的所有行
          for (let i = 0; i < lines.length - 1; i++) {
            await typeLine(lines[i]);
            await new Promise(resolve => setTimeout(resolve, 100));
          }

          // 等待所有资源加载完成
          await waitForResourcesLoaded();

          await typeLine(lineEnd);

          // 所有打字动画完成后,停止加载动画
          await new Promise(resolve => setTimeout(resolve, 800));
          stopLoadingAnimation();
        }
        else {
          console.log('已被访问过,不再执行动画');
        }

    });
  </script>
</body>

</html>
  • 以下版本为不进行资源加载判断,会出现字体很久加载出来劣化体验感受的情况(可以保证绝对能访问)
  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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<!DOCTYPE html>
<html lang="zh-CN">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
    @font-face {
    font-family: 'MyCustomFont';
    src: url('/font/MarukoGothicCJKjp-Regular.ttf') format('truetype');
    }

    .loadingBox {
        position: fixed;
        justify-content: center;
        align-items: center;
        display: none;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
        background-color: #1d1d1d;
        z-index: 9999;
    }

    .Bigbox {
    background-color: #1d1d1d;
    font-family: monospace;
    margin: 20px;
    padding: 0;
    font-family: 'MyCustomFont', sans-serif;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    }

    h1 {
    opacity: 0;
    animation: fadeIn 1.5s ease-in forwards;
    color: #b4b4b4;
    display: inline;
    white-space: pre;
    }

    .loading {
    display: block;
    margin-bottom: 20px;
    }

    .typewriter {
    position: relative;
    width: 100%;
    max-width: 600px;
    }

    .typewriter .line {
    display: block;
    overflow: hidden;
    white-space: nowrap;
    margin-left: 10px;
    margin-top: -1px;
    color: #b4b4b4;
    font-weight: bold;
    letter-spacing: 2px;
    height: 1.5em;
    }

    @keyframes fadeIn {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
    }

</style>
</head>

<body>
<div class="loadingBox">
    <div class="Bigbox">
    <div class="loading">
        <h1>LOADING</h1>
        <h1 id="loadingText" style="font-size: 15px;"> - 系统启动中</h1>
    </div>
    <div class="typewriter">
        <span class="line" data-text="Neuro"></span>
        <span class="line" data-text="Implanted"></span>
        <span class="line" data-text="Machine for"></span>
        <span class="line" data-text="Protecting"></span>
        <span class="line" data-text="Human"></span>
        <span class="line" data-text="突触连接:OFF"></span>
        <span class="line" data-text="删除本机数据"></span>
        <span class="line" data-text="网络连接"></span>
        <span class="line" data-text="下载最新数据:完成"></span>
        <span class="line" data-text="确认数据损失率:正常范围"></span>
        <span class="line" data-text="确认神经元受损度:正常"></span>
        <span class="line" data-text="确认神经元处理速度:正常"></span>
        <span class="line" data-text="突触链接:ON"></span>
        <span class="line" data-text="确认路由信息恢复率:正常范围"></span>
        <span class="line" data-text="确认通信状态:良好"></span>
        <span class="line" data-text="NIMPH系统:全部正常"></span>
        <span class="line" data-text="数据自动保存"></span>
        <span class="line" data-text="准备完毕"></span>
    </div>
    </div>
</div>

<script>
    document.addEventListener('DOMContentLoaded', async function () {

    // 使用类选择器获取元素
    const loading = document.querySelector('.loadingBox');

    // 获取元素
    const lines = Array.from(document.querySelectorAll('.line'))
        .filter(line => line.dataset.text && line.dataset.text.trim() !== '');
    const loadingText = document.getElementById('loadingText');//此处更具id获取元素--纯扩展
    const lineEnd = lines[lines.length - 1];

    // 定义加载动画状态
    let dotCount = 0;
    let loadingInterval;

    // 启动加载动画
    function startLoadingAnimation() {
        loadingInterval = setInterval(() => {
        dotCount = (dotCount + 1) % 4; // 0-3循环
        loadingText.textContent = '  -  系统启动中' + '.'.repeat(dotCount);
        }, 500); // 500ms更新一次
    }

    // 停止加载动画
    function stopLoadingAnimation() {
        // clearInterval(loadingInterval);
        // loadingText.textContent = '  -  系统启动完成'; // 动画结束后显示完成状态


        // 创建并执行动画
        const fadeOutAnimation = loading.animate([
        { opacity: 1 },
        { opacity: 0 }
        ], {
        duration: 800,
        fill: 'forwards'
        });

        // 动画结束后隐藏元素
        fadeOutAnimation.finished.then(() => {
        loading.style.display = "none";
        sessionStorage.setItem('visited', 'true');//设置sessionStorage,标识为已被访问
        });
    }

    // 打字动画函数
    async function typeLine(line) {
        const fullText = line.dataset.text;
        line.textContent = '';
        if (!fullText) return;

        const firstChar = fullText[0] || '|';
        let currentIndex = 1;
        line.textContent = firstChar;

        return new Promise(resolve => {
        function type() {
            if (currentIndex < fullText.length) {
            line.textContent = fullText.substring(0, currentIndex) + firstChar;
            currentIndex++;
            setTimeout(type, 20 + Math.random() * 15);
            } else {
            line.textContent = fullText;
            resolve();
            }
        }
        setTimeout(type, 20);
        });
    }

        const isFirstVisit = !sessionStorage.getItem('visited');  //定义判断是否为第一次访问

        if (isFirstVisit) {
        loading.style.display = "block";

        // 执行流程:淡入动画 -> 加载动画 -> 打字动画
        startLoadingAnimation(); // 先启动加载动画

        // 等待淡入动画完成
        await new Promise(resolve => setTimeout(resolve, 1500));

        // 处理除最后一行外的所有行
        for (let i = 0; i < lines.length - 1; i++) {
            await typeLine(lines[i]);
            await new Promise(resolve => setTimeout(resolve, 100));
        }

        await new Promise(resolve => setTimeout(resolve, 500));
        await typeLine(lineEnd);

        // 所有打字动画完成后,停止加载动画
        await new Promise(resolve => setTimeout(resolve, 800));
        stopLoadingAnimation();
        }
        else {
        console.log('已被访问过,不再执行动画');
        }

    });
</script>
</body>

</html>

使用方法

  • 将代码复制进 layouts/partials/head/custom.html (自行创建或复制)

  • 基本都加了备注,可以自行更改打印速率和字体大小,字体,内容等等。(看不懂的看下面)

实现方法

  • 一层容器 loadingBox 用于实现加载动画整体的淡入淡出效果。
  • 二层容器 Bigbox 用于提供边距和字体等样式。
  • 三层容器 loading 和 typewriter 分别承载各自内容。

如图所示

  • loadingBox 的样式设置,设置背景色,宽高,z-index(深度,实现覆盖而非叠加)等。——此处由于边距需要所以同样设置了背景色
  • Bigbox 的样式设置,设置背景色,宽高,字体,字体大小,边距 等。
  • typewriter 的样式设置,设置宽高,字体,字体大小,边距 等。

如图所示

如图所示

基本思路:在 DOMContentLoaded 事件中,判断是否为第一次访问,如果是,则将 loadingBox 显示出来,并执行下列动画,否则不执行动画并打印日志,利用简单的 css 控制大标题的透明度变化实现淡入,利用 js 拆分字体,通过拼接的方式以数组存储,利用定时器打印出来。

  • 定义 fadeIn 动画,淡入效果。—-实现 loading 内容的淡入效果。

如图所示

  • 定义 loadingInterval,用于实现加载动画。—–实现 loading 内容(后面三个点)的加载动画。

如图所示

  • 定义 stopLoadingAnimation, startLoadingAnimation,用于启动停止加载动画。

如图所示

  • 定义 typeLine,用于实现打字动画。

如图所示

  • 使用 isFirstVisit,通过 sessionStorage,用于判断是否为第一次访问。

如图所示

  • 定义 fadeOutAnimation,用于实现淡出动画(包含在 stopLoadingAnimation 中)。

如图所示

衍生物

  • 快速版
  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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <style>
        @font-face {
            font-family: 'MyCustomFont';
            src: url('/font/MarukoGothicCJKjp-Regular.ttf') format('truetype');
        }

        body {
            background-color: #1d1d1d;
            font-family: monospace;
            margin: 20px;
            padding: 0;
            font-family: 'MyCustomFont', sans-serif;
            display: flex;
            flex-direction: column;
            min-height: 100vh;
        }

        h1 {
            opacity: 0;
            animation: fadeIn 1.5s ease-in forwards;
            color: #b4b4b4;
            margin-bottom: 30px;
        }

        .typewriter {
            position: relative;
            width: 100%;
            max-width: 600px;
        }

        .typewriter .line {
            display: block;
            overflow: hidden;
            white-space: nowrap;
            margin-left: 10px;
            margin-top: -1px;
            color: #b4b4b4;
            font-weight: bold;
            letter-spacing: 2px;
            height: 1.5em;
        }

        /* 为每一行设置不同的动画延迟 */
        .typewriter .line:nth-child(1) {
            animation-delay: 0.5s;
        }

        .typewriter .line:nth-child(2) {
            animation-delay: 1.2s;
        }

        .typewriter .line:nth-child(3) {
            animation-delay: 1.9s;
        }

        .typewriter .line:nth-child(4) {
            animation-delay: 2.6s;
        }

        .typewriter .line:nth-child(5) {
            animation-delay: 3.1s;
        }

        .typewriter .line:nth-child(6) {
            animation-delay: 3.8s;
        }

        .typewriter .line:nth-child(7) {
            animation-delay: 4.5s;
        }

        .typewriter .line:nth-child(8) {
            animation-delay: 5.2s;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
            }

            to {
                opacity: 1;
            }
        }

        /* 状态指示器 */
        .status {
            display: inline-block;
            width: 10px;
            height: 10px;
            border-radius: 50%;
            margin-left: 5px;
            background: #ff5555;
            vertical-align: middle;
        }

        .status.active {
            background: #55ff55;
            box-shadow: 0 0 8px rgba(85, 255, 85, 0.7);
        }

        /* 终端效果增强 */
        .terminal-effect {
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            background: repeating-linear-gradient(0deg,
                    rgba(0, 0, 0, 0.05),
                    rgba(0, 0, 0, 0.05) 1px,
                    transparent 1px,
                    transparent 2px);
            pointer-events: none;
            opacity: 0.2;
        }

        .system-complete {
            color: #55ff55;
            text-align: center;
            margin-top: 20px;
            opacity: 0;
            animation: fadeIn 1s 6s forwards;
        }
    </style>
</head>

<body>
    <h1>LOADING - 系统启动中...</h1>
    <div class="typewriter">
        <span class="line" data-text="Neuro"></span>
        <span class="line" data-text="Implanted"></span>
        <span class="line" data-text="Machine for"></span>
        <span class="line" data-text="Protecting"></span>
        <span class="line" data-text="Human"></span>
        <span class="line" data-text="突触连接:OFF"></span>
        <span class="line" data-text="删除本机数据"></span>
        <span class="line" data-text="网络连接"></span>

        <span class="line" data-text="下载最新数据:完成"></span>
        <span class="line" data-text="确认数据损失率:正常范围"></span>
        <span class="line" data-text="确认神经元受损度:正常"></span>
        <span class="line" data-text="确认神经元处理速度:正常"></span>
        <span class="line" data-text="突触链接:ON"></span>
        <span class="line" data-text="确认路由信息恢复率:正常范围"></span>
        <span class="line" data-text="确认通信状态:良好"></span>
        <span class="line" data-text="NIMPH系统:全部正常"></span>
        <span class="line" data-text="数据自动保存"></span>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            // 获取所有行元素
            const lines = document.querySelectorAll('.line');
            const statusIndicator = document.querySelector('.status');
            const delays = [0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.1];

            lines.forEach((line, index) => {
                // 获取文本内容
                const fullText = line.dataset.text;
                // 清空内容,准备开始打字
                line.textContent = '';

                // 设置延迟
                setTimeout(() => {
                    // 获取首字母
                    const firstChar = fullText[0];
                    // 初始显示首字母(作为光标)
                    line.textContent = firstChar;

                    let currentIndex = 1;

                    // 打字函数
                    function type() {
                        if (currentIndex < fullText.length) {
                            // 显示当前文本 + 首字母(作为光标)
                            line.textContent = fullText.substring(0, currentIndex) + firstChar;
                            currentIndex++;

                            // 随机速度,模拟真实打字
                            const speed = 30 + Math.random() * 10;
                            setTimeout(type, speed);
                        } else {
                            // 打字完成,显示完整文本
                            line.textContent = fullText;
                        }
                    }

                    // 开始打字
                    setTimeout(type, 200);
                }, delays[index] * 1000);
            });
        });
    </script>
</body>

</html>
  • 宽度实现方法(单纯 css 实现)
  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
118
119
120
121
122
123
124
125
126
127
128
129
130
<!DOCTYPE html>
<html>

<head>
    <title>Test CSS</title>
    <style>

@font-face {

font-family: 'MyCustomFont';


src:
     url('/font/MarukoGothicCJKjp-Regular.ttf') format('truetype');
}


        body {
            background-color: #1d1d1d;
            font-family: monospace;
            margin: 20px;
            padding: 0;

            font-family: 'MyCustomFont', sans-serif;
        }

        h1 {
            opacity: 0;
            animation: fadeIn 1.5s ease-in forwards;
            color:#b4b4b4;
        }

        .typewriter {
            position: relative;
        }

        .typewriter .line {
            display: block;
            overflow: hidden;
            white-space: nowrap;
            margin-left: 10px;
            margin-top:-1px;
            color:#b4b4b4;
            font-weight: bold;
            letter-spacing: 2px;
            /* border-right: #b4b4b4 15em solid; */

            animation:
                typing 2s steps(60, end) forwards,
                blink-caret 0.45s step-end infinite;
        }

        /* 为每一行设置不同的动画延迟 */
        .typewriter .line:nth-child(1) {
            animation-delay: 0.5s;
            width: 0;
        }

        .typewriter .line:nth-child(2) {
            animation-delay: 1.2s;
            width: 0;
        }

        .typewriter .line:nth-child(3) {
            animation-delay: 1.9s;
            width: 0;
        }

        .typewriter .line:nth-child(4) {
            animation-delay: 2.6s;
            width: 0;
        }

        .typewriter .line:nth-child(5) {
            animation-delay: 3.1s;
            width: 0;
        }

        .typewriter .line:nth-child(6) {
            animation-delay: 3.8s;
            width: 0;
        }

        .typewriter .line:nth-child(7) {
            animation-delay: 4.5s;
            width: 0;
        }

        .typewriter .line:nth-child(8) {
            animation-delay: 5.2s;
            width: 0;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
            }

            to {
                opacity: 1;
            }
        }

        @keyframes typing {
            from {
                width: 0
            }

            to {
                width: 100%
            }
        }
    </style>
</head>

<body>
    <h1>LOADING -系统启动中...</h1>
    <div class="typewriter">
        <span class="line">Neuro</span>
        <span class="line">Implanted</span>
        <span class="line">Machine for</span>
        <span class="line">Protecting</span>
        <span class="line">Human</span>
        <span class="line">突触连接:OFF</span>
        <span class="line">删除本机数据</span>
        <span class="line">网络连接</span>
    </div>
</body>

</html>
  • 中间展开版
  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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<!DOCTYPE html>
<html>

<head>
    <title>Test CSS</title>
    <style>
        @font-face {
            font-family: 'MyCustomFont';
            src: url('/font/MarukoGothicCJKjp-Regular.ttf') format('truetype');
        }

        body {
            background-color: #1d1d1d;
            font-family: monospace;
            margin: 20px;
            padding: 0;
            font-family: 'MyCustomFont', sans-serif;
        }

        h1 {
            opacity: 0;
            animation: fadeIn 1.5s ease-in forwards;
            color: #b4b4b4;
        }

        .typewriter {
            position: relative;
        }

        .typewriter .line {
            display: flex;
            justify-content: center;
            margin-left: 10px;
            margin-top: -1px;
            color: #b4b4b4;
            font-weight: bold;
            letter-spacing: 2px;
            overflow: hidden;
        }

        .typewriter .char {
            opacity: 0;
            transform: scale(0);
            animation: popIn 0.3s forwards;
        }

        /* 为每一行设置不同的动画延迟 */
        .typewriter .line:nth-child(1) {
            animation-delay: 0.5s;
        }

        .typewriter .line:nth-child(2) {
            animation-delay: 1.2s;
        }

        .typewriter .line:nth-child(3) {
            animation-delay: 1.9s;
        }

        .typewriter .line:nth-child(4) {
            animation-delay: 2.6s;
        }

        .typewriter .line:nth-child(5) {
            animation-delay: 3.1s;
        }

        .typewriter .line:nth-child(6) {
            animation-delay: 3.8s;
        }

        .typewriter .line:nth-child(7) {
            animation-delay: 4.5s;
        }

        .typewriter .line:nth-child(8) {
            animation-delay: 5.2s;
        }

        @keyframes fadeIn {
            from {
                opacity: 0;
            }

            to {
                opacity: 1;
            }
        }

        @keyframes popIn {
            0% {
                opacity: 0;
                transform: scale(0);
            }

            100% {
                opacity: 1;
                transform: scale(1);
            }
        }
    </style>
</head>

<body>
    <h1>LOADING -系统启动中...</h1>
    <div class="typewriter">
        <div class="line" data-text="Neuro"></div>
        <div class="line" data-text="Implanted"></div>
        <div class="line" data-text="Machine for"></div>
        <div class="line" data-text="Protecting"></div>
        <div class="line" data-text="Human"></div>
        <div class="line" data-text="突触连接:OFF"></div>
        <div class="line" data-text="删除本机数据"></div>
        <div class="line" data-text="网络连接"></div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            const lines = document.querySelectorAll('.line');

            lines.forEach((line, lineIndex) => {
                const text = line.getAttribute('data-text');
                line.innerHTML = '';

                // 创建字符元素
                const chars = text.split('');
                chars.forEach((char, charIndex) => {
                    const charElement = document.createElement('span');
                    charElement.className = 'char';
                    charElement.textContent = char;

                    // 计算每个字符的动画延迟 - 从中间向两边
                    const middle = chars.length / 2;
                    const distanceFromMiddle = Math.abs(charIndex - middle);
                    const delay = lineIndex * 0.7 + distanceFromMiddle * 0.1;

                    charElement.style.animationDelay = `${delay}s`;
                    line.appendChild(charElement);
                });
            });
        });
    </script>
</body>

</html>
Made with ❤️ by KKPT
最后更新于 Aug 20, 2025 21:35 +0800
使用 Hugo 构建
主题 StackJimmy 设计