javascript - 如何从 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++;
}
}
尽管进行了许多不同的尝试和大量研究,但我无法找到一种方法来让动画在同一个元素上多次触发。
解决方案
经过一番研究,我想出了一个解决方法。我重写了该函数,以便 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;
}
我知道,有一些懒惰的工作,但效果很好。
推荐阅读
- django - Django get_or_create 模型函数
- python - 将应用程序部署到 Heroku 时出错,H14 错误
- java - 拆分字符“|”时从文件中读取数据时出现异常
- c# - 为什么我的相机控制器将相机传送到地面?
- mysql - 如何访问 mysql 子查询中的外列?
- python - 如何检测鼠标点击和按键?
- python - 尝试将pyinstaller与skyfield一起使用,构建.exe但无法运行
- angular - 角度元素单元测试 .valid 返回 false
- java - 添加到此数组时为什么会得到“null”?
- google-apps-script - googledrive 移动文件脚本刚刚开始失败并且无法正常工作