首页 > 解决方案 > 如何从 vanilla JS 中的元素中删除 CSS 动画

问题描述

我有一个由 nxn 个较小的方形 div 元素组成的方形网格,我想用 CSS 背景颜色动画按顺序照亮这些元素。我有一个为序列生成随机数组的函数。我遇到的麻烦是,一旦某个正方形被照亮了一次,如果它在阵列中再次出现,它就不会再次照亮。我相信这是因为一旦为元素分配了 CSS 动画,动画就无法在该元素上再次触发,我想不出办法让它工作。这是针对我正在学习的响应式 Web 应用程序课程,并且评估规定我们只能使用 vanilla JS,并且所有元素必须在 JS 中创建并附加到<body>index.html 中的空白处。

根据序列的每个闪光都是通过一个 setTimeout 函数触发的,该函数循环遍历数组中的所有元素,每次循环将其计时器增加 1 秒(动画长度也是 1 秒)。

定义容器和子 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++;
    }
}

尽管进行了许多不同的尝试和大量研究,但我无法找到一种方法来让动画在同一个元素上多次触发。

标签: javascriptcss

解决方案


经过一番研究,我想出了一个解决方法。我重写了该函数,以便 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;
}

我知道,有一些懒惰的工作,但效果很好。


推荐阅读