实现矩阵代码雨效果时store数组存储异常的技术求助
黑客帝国代码雨字符串更新异常问题
最近在CodePen学习JavaScript、HTML和CSS,实现《黑客帝国》风格的代码雨效果,具备其他编程语言基础。目前已实现包含随机字符串的div元素按周期向下滚动,但尝试将对应字符串以数组形式存入store数组,后续用于将字符串左移一位并添加新随机字符时,发现整个store数组的元素全部被设置为最新生成的字符串数组。
原代码
JavaScript代码
var numDivs = 10; var pos = []; var delay = []; /* Check page is loaded and create each of the divs for the 'raindrops' */ for (let i = 0; i < numDivs; i++) { document.addEventListener('DOMContentLoaded', function() { var div = document.createElement('div'); div.id = 'matrix'+i; div.className = 'matrixC m'+i; document.body.appendChild(div); }, false); } /* Generate 12 character string for each of the 'raindrops' */ var characters = ["日", "ハ","ミ","ヒ","ー","ウ","シ","ナ","モ","ニ","サ","ワ","ツ","オ","リ","ア","ホ","テ","マ","ケ","メ","エ","カ","キ","ム","ユ","ラ","セ","ネ","ス","タ","ヌ","ヘ","0","1","2","3","4","5","7","8","9","Z",":","・",".",'"',"=","*","+","-","<",">","¦","|","ç","ク"]; var store = []; var state2 = "false"; function genNum() { var stringArr = []; for (let n = 0; n < numDivs; n++) { var finalString = ""; var char = 0; if (state2 == "false"){ var state = "false"; } if (state == "false"){ for (let m = 0; m < 20; m++) { let char = characters[Math.floor(Math.random() * characters.length)]; finalString += char; stringArr[m] = char; } state = "true"; } else { stringArr = store[n]; stringArr.shift(); let char = characters[Math.floor(Math.random() * characters.length)]; stringArr[19] = char; for (let i = 0; i < stringArr.length; i++) { finalString += stringArr[i]; } } store[n] = stringArr; console.log(n + ": " + finalString+", " + stringArr); console.log(store); document.getElementById("matrix"+n).innerHTML = finalString; } state2 = "true"; } setInterval(genNum, 100); /* Regenerate string from above every 0.15s */ let root = document.documentElement; for (let i = 0; i < numDivs; i++) { pos[i]=((Math.random() * 100) + "vw"); /* Set random position across page */ } for (let i = 0; i < numDivs; i++) { delay[i]=((Math.random() * 10) + "s"); /* Set random offset time for start of animation */ } let dynamicStyles = null; function addAnimation(body) { /* create animation function using parsed code */ if (!dynamicStyles) { dynamicStyles = document.createElement('style'); dynamicStyles.type = 'text/css'; document.head.appendChild(dynamicStyles); } dynamicStyles.sheet.insertRule(body, dynamicStyles.length); } for (let i = 0; i < numDivs; i++) { /* For each 'raindrop' create animation from random point across page down to off-screen */ addAnimation(` @keyframes fall`+i+` { from { transform: translate(`+pos[i]+`,-150%); } to { transform: translate(`+pos[i]+`,100vh); } }`); } /* Give each div their corresponding animation, specifying all details */ document.addEventListener("DOMContentLoaded", function () { for (let i = 0; i < numDivs; i++) { document.getElementById("matrix"+i).style.animation = "fall"+i+" 7.5s linear "+delay[i]+" infinite"; } });
CSS代码
body { background-color: black; } .matrixC { transform: translate(-200%,-200%); /* Make sure none are on screen at start*/ font-size: 10px; width: 5px; /* Keeps text vertical */ display: inline-block; text-align: center; word-wrap: break-word; /* Keeps text vertical */ background: linear-gradient(black, #d0ebc4); /* Creates fade effect from bottom to top */ -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 0 20px #70ea37; /* Creates glow */ margin: -5px; /* For some reason makes it more evenly spread */ }
问题原因
核心问题是**stringArr数组在genNum函数的循环外定义,所有store[n]都指向同一个数组引用**。第一次循环时,所有雨滴复用同一个stringArr,修改它会同步影响store里的所有元素;后续state2=true后,虽然尝试取store[n],但初始时所有store元素都是同一个数组的引用,导致修改一个就全变。
另外,state变量的作用域错误:它在state2=false时才定义,且是函数内变量,无法针对每个雨滴单独控制状态。
修复方案
修改后的JavaScript核心代码(替换原genNum函数及相关变量)
var store = []; function genNum() { for (let n = 0; n < numDivs; n++) { let finalString = ""; // 把stringArr移到循环内,每个雨滴用独立数组 let stringArr; // 检查store[n]是否存在,不存在则初始化 if (!store[n]) { stringArr = []; for (let m = 0; m < 20; m++) { let char = characters[Math.floor(Math.random() * characters.length)]; finalString += char; stringArr[m] = char; } store[n] = stringArr; } else { // 取当前雨滴自己的数组 stringArr = store[n]; stringArr.shift(); let char = characters[Math.floor(Math.random() * characters.length)]; stringArr.push(char); // 直接push更安全,避免索引越界 // 拼接字符串 finalString = stringArr.join(''); } console.log(n + ": " + finalString+", " + stringArr); console.log(store); document.getElementById("matrix"+n).innerHTML = finalString; } }
关键修改点
- 将
stringArr移到循环内部:每次循环都创建新数组,确保每个雨滴的字符串数组是独立的,不会共享引用。 - 用
store[n]是否存在替代state/state2:逻辑更简洁,直接判断是否需要初始化当前雨滴的字符串数组。 - 用
push替代直接赋值索引:避免数组长度变化时出现索引越界问题,shift()后数组长度减1,push()刚好补充到20位。 - 用
join('')拼接字符串:替代手动循环拼接,代码更简洁高效。
额外优化:修复DOMContentLoaded绑定问题
原代码中循环绑定DOMContentLoaded事件会导致绑定多个相同事件,改为只绑定一次:
/* Check page is loaded and create each of the divs for the 'raindrops' */ document.addEventListener('DOMContentLoaded', function() { for (let i = 0; i < numDivs; i++) { var div = document.createElement('div'); div.id = 'matrix'+i; div.className = 'matrixC m'+i; document.body.appendChild(div); } }, false);
这样只会绑定一次事件,在页面加载完成后批量创建所有div,避免重复绑定的问题。
内容的提问来源于stack exchange,提问作者Josh Toole
相关产品推荐
相关产品推荐

