我有一个n×n个较小的div元素的正方形网格,我想用CSS背景颜色动画按顺序进行照明。我有一个函数可以为序列生成随机数组。我遇到的麻烦是一旦某个正方形被照亮了一次,如果它再次出现在阵列中,它将不会再次亮起。我相信这是因为一旦为元素分配了CSS动画,该动画就无法在该元素上再次触发,而且我也无法找到使它起作用的方法。这是我正在参加的响应式Web应用程序课程,并且评估规定我们仅使用香草JS,并且所有元素都必须在JS中创建并附加到index.html中的空白<body>
上
每个序列的闪光都是通过setTimeout函数触发的,该函数循环遍历数组中的所有元素,从而使每个循环的计时器增加1s(动画长度也为1s)。
定义容器和子div:
function createGameContainer(n, width, height) {
var container = document.createElement('div');
//CSS styling
container.style.margin = '50px auto'
container.style.width = width;
container.style.height = height;
container.style.display = 'grid';
// loop generates string to create necessary number of grid columns based on the width of the grid of squares
var columns = '';
for (i = 0; i < n; i++) {
columns += ' calc(' + container.style.width + '/' + n.toString() + ')'
}
container.style.gridTemplateColumns = columns;
container.style.gridRow = 'auto auto';
// gap variable to reduce column and row gap for larger grid sizes
// if n is ever set to less than 2, gap is hardcoded to 20 to avoid taking square root of 0 or a negative value
var gap;
if (n > 1) {
gap = 20/Math.sqrt(n-1);
} else {
gap = 20;
}
container.style.gridColumnGap = gap.toString() + 'px';
container.style.gridRowGap = gap.toString() + 'px';
container.setAttribute('id', 'game-container');
document.body.appendChild(container);
}
/*
function to create individual squares to be appended to parent game container
*/
function createSquare(id) {
var square = document.createElement('div');
//CSS styling
square.style.backgroundColor = '#333';
//square.style.padding = '20px';
square.style.borderRadius = '5px';
square.style.display = 'flex';
square.style.alignItems = 'center';
//set class and square id
square.setAttribute('class', 'square');
square.setAttribute('id', id);
return square;
}
/*
function to create game container and and squares and append squares to parent container
parameter n denotes dimensions of game grid - n x n grid
*/
function createGameWindow(n, width, height) {
window.dimension = n;
createGameContainer(n, width, height);
/*
loop creates n**2 number of squares to fill game container and assigns an id to each square from 0 at the top left square to (n**2)-1 at the bottom right square
*/
for (i = 0; i < n**2; i++) {
var x = createSquare(i);
document.getElementById('game-container').appendChild(x);
}
}
CSS动画
@keyframes flash {
0% {
background: #333;
}
50% {
background: orange
}
100% {
background: #333;
}
}
.flashing {
animation: flash 1s;
}
生成数组的代码:
function generateSequence(sequenceLength) {
var sequence = [];
for (i = 0; i < sequenceLength; i++) {
var random = Math.floor(Math.random() * (dimension**2));
// the following loop ensures each element in the sequence is different than the previous element
while (sequence[i-1] == random) {
random = Math.floor(Math.random() * (dimension**2));
}
sequence[i] = random;
};
return sequence;
}
将动画应用于正方形的代码:
function flash(index, delay) {
setTimeout( function() {
flashingSquare = document.getElementById(index);
flashingSquare.style.animation = 'flashOne 1s';
flashingSquare.addEventListener('animationend', function() {
flashingSquare.style.animation = '';
}, delay);
}
我也尝试过再次删除并添加一个类以尝试重置动画:
function flash(index, delay) {
setTimeout( function() {
flashingSquare = document.getElementById(index);
flashingSquare.classList.remove('flashing');
flashingSquare.classList.add('flashing');
}, delay);
}
生成和显示序列的功能:
function displaySequence(sequenceLength) {
var sequence = generateSequence(sequenceLength);
i = 0;
while (i < sequence.length) {
index = sequence[i].toString();
flash(index, i*1000);
i++;
}
}
尽管进行了许多不同的尝试和大量研究,但我仍然找不到让动画在同一元素上多次触发的方法。
答案 0 :(得分:0)
尝试这个:
function flash(index, delay){
setTimeout( function() {
flashingSquare = document.getElementById(index);
flashingSquare.classList.add('flashing');
flashingSquare.addEventListener('animationend', function() {
flashingSquare.classList.remove('flashing');
}, delay);
});
}
不删除动画,删除课程。
删除课程直接后,动画就完成了。因此,浏览器有时间处理所有事情。并且,当您添加类直接之前所需的动画时,浏览器可以触发所有需要的步骤来实现。
您尝试删除和添加类的尝试很好,但是速度很快。我认为浏览器和DOM可优化您的步骤,而无济于事。
答案 1 :(得分:0)
经过研究,我找到了解决方法。我重写了函数,以便setTimeout嵌套在for循环中,而setTimeout嵌套在立即调用的函数表达式中(我仍然不完全了解,但是,如果可以的话)。新功能如下:
/*
function to display game sequence
length can be any integer greater than 1
speed is time between flashes in ms and can presently be set to 1000, 750, 500 and 250.
animation length for each speed is set by a corresponding speed class
in CSS main - .flashing1000 .flashing750 .flashing500 and .flashing250
*/
function displaySequence(length, speed) {
var sequence = generateSequence(length);
console.log(sequence);
for (i = 0; i < sequence.length; i++) {
console.log(sequence[i]);
// immediately invoked function expression
(function(i) {
setTimeout( function () {
var sq = document.getElementById(sequence[i]);
sq.classList.add('flashing' + speed.toString());
sq.addEventListener('animationend', function() {
sq.classList.remove('flashing' + speed.toString());
})
}, (speed * i))
})(i);
}
}
每个类的CSS:
@keyframes flash {
0% {
background: #333;
}
50% {
background: orange
}
100% {
background: #333;
}
}
.flashing1000 {
animation: flash 975ms;
}
.flashing750 {
animation: flash 725ms;
}
.flashing500 {
animation: flash 475ms;
}
.flashing250 {
animation: flash 225ms;
}
我知道有一些懒惰的解决方法,但是效果很好。