如何让命令行式打字动画中指定字母(XYZ)显示为霓虹粉?
解决打字动画中高亮指定字符导致的循环异常问题
问题根源在于:直接用HTML标签修改文本后,原代码通过substring截取HTML字符串的方式会把标签的字符(比如<span class="neon-pink">)算入长度,导致实际显示的字符数和计数不匹配,进而触发错误的循环终止条件,让动画出现异常。
解决方案步骤
- 预处理文本,拆分字符单元:把需要高亮的字符单独包裹成HTML标签,并将整个文本拆分为独立的“显示单元”数组,每个单元要么是普通字符,要么是带高亮标签的字符。
- 修改打字逻辑,按单元拼接:不再通过截取HTML字符串的方式生成显示内容,而是逐个拼接预处理好的单元,确保计数和实际显示的字符完全一致。
修改后的完整代码
HTML
<p id="line1" class="hidden">ABCXYZDEF</p> <p id="cursor-line" class="visible">><span class="typed-cursor">_</span></p>
CSS(保持原有样式不变)
body { font-family: monospace; background-color: black; color: white; text-align: center; margin-top: 21%; } .neon-pink { color: #ff00ff; /* Neon Pink color code */ } .hidden { display: none; visibility: hidden; } /* ----- blinking cursor animation ----- */ .typed-cursor { opacity: 1; -webkit-animation: blink 0.95s infinite; -moz-animation: blink 0.95s infinite; -ms-animation: blink 0.95s infinite; -o-animation: blink 0.95s infinite; animation: blink 0.95s infinite; } @keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } } @-webkit-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } } @-moz-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } } @-ms-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } } @-o-keyframes blink { 0% { opacity: 1; } 50% { opacity: 0; } 100% { opacity: 1; } }
JavaScript(核心修改部分)
// set typing speed and wait times var timeInit = 400; // initial wait before typing first line var timeGap = 1000; // wait time between each line var timeChar = 110; // time until next letter var cursorChar = '_'; var originId = ['line1']; var originTextUnits = new Array(); // 预处理文本,拆分字符单元,高亮XYZ字符 function processText(rawText) { return rawText.split('').map(char => { if ('XYZ'.includes(char)) { return `<span class="neon-pink">${char}</span>`; } return char; }); } for (var i = 0; i < originId.length; i++) { var rawText = document.getElementById(originId[i]).textContent; originTextUnits.push(processText(rawText)); } var cursorLine = document.getElementById('cursor-line'); var currentTimeout; var showCursor; var typeWriter = function(index) { var loc = document.getElementById(originId[index]); var textUnits = originTextUnits[index]; var unitCount = 0; // 逐个拼接字符单元实现打字动画 var typeUnit = function() { currentTimeout = setTimeout(function() { loc.className = 'visible'; unitCount += 1; // 拼接已处理的单元生成显示内容 var showText = textUnits.slice(0, unitCount).join(''); // 所有单元处理完成时终止循环 if (unitCount === textUnits.length) { loc.innerHTML = '>' + showText + '<span class="typed-cursor">' + cursorChar + '</span>'; } else { loc.innerHTML = '>' + showText + '<span class="typed-cursor">' + cursorChar + '</span>'; typeUnit(); } }, timeChar); }; typeUnit(); }; // calculated time delays var delayTime = [timeInit]; var cumulativeDelayTime = [timeInit]; for (var i = 0; i < originId.length; i++) { // 用单元数组长度计算时长,确保和实际显示字符数匹配 var elapsedTimeLine = originTextUnits[i].length * timeChar + timeGap + timeChar * 2; delayTime.push(elapsedTimeLine); var sum = 0; for (var j = 0; j < delayTime.length; j++) { sum += delayTime[j]; } cumulativeDelayTime.push(sum); } // calls setTimeout for each line var typeLineTimeout = new Array(); for (var i = 0; i < originId.length; i++) { typeLineTimeout[i] = setTimeout( (function(index) { return function() { cursorLine.className = 'hidden'; typeWriter(index); }; })(i), cumulativeDelayTime[i] ); } // stops all timeouts var skip = function() { clearTimeout(currentTimeout); clearTimeout(showCursor); for (var i = 0; i < typeLineTimeout.length; i++) { clearTimeout(typeLineTimeout[i]); } }; // 修复rewriteText函数,正确渲染高亮后的完整文本 var rewriteText = function(index) { var loc = document.getElementById(originId[index]); loc.innerHTML = '> ' + originTextUnits[index].join(''); loc.className = 'visible'; }; // trigger skip and rewrite on pressing enter or spacebar window.onkeydown = function(key) { if (key.which === 13 || key.which === 32) { skip(); originId.forEach(rewriteText); document.getElementById('cursor-line').className = 'visible'; } };
关键修改说明
- 新增
processText函数,将原始文本拆分为字符单元,对XYZ单独包裹高亮标签,确保每个可见字符对应一个独立单元。 - 用
originTextUnits存储预处理后的单元数组,替代原有的originText,避免HTML标签干扰长度计算。 - 打字逻辑改为遍历单元数组逐个拼接,彻底解决标签字符导致的计数错误。
- 时间计算部分改用单元数组的长度,确保动画时长和实际字符数匹配。
- 修复
rewriteText函数,正确渲染包含高亮标签的完整文本。
内容的提问来源于stack exchange,提问作者themilkyone
相关产品推荐
相关产品推荐

