JSFiddle - React, Tailwind, and code Playground
by Qwerty_Wasd
HTML
<div class="workSpace">
<div class="box" id="boxOne">1</div>
<div class="box" id="boxTwo">2</div>
<div class="buttons">
<div class="stopButton" id="stopOne">"Остановить 1" НЕ работает</div>
<div class="stopButton" id="stopTwo">"Остановить 2" НЕ работает</div>
<div class="stopButton" id="stopAll">"Остановить все" работает</div>
</div>
</div>
CSS
html, body{
margin: 0px 0px 0px 0px;
width: 100%;
height: 100%;
overflow: hidden;
}
.workSpace{
width: 100%;
height: 100%;
background: #eee5ff;
font-family: comic sans ms;
overflow: hidden;
}
.buttons{
position: relative;
top: 450px;
}
.stopButton{
margin-left: 10px;
padding: 10px 10px 10px 10px;
cursor: pointer;
letter-spacing: 1px;
border: 2px solid #eee5ff;
border-radius: 7px;
color: #fff;
display: inline-block;
text-align: center;
}
.stopButton:hover{
/* background: #eee5ff; */
border: 2px solid #000000;
}
.box{
text-align: center;
color: #fff;
}
#boxOne{
width: 50px;
height: 50px;
font-size: 33px;
overflow: hidden;
position: absolute;
background: #795548;
left: 180px;
top: 20px;
}
#boxTwo{
width: 200px;
height: 100px;
font-size: 67px;
overflow: hidden;
position: absolute;
background: #009688;
left: 180px;
top: 90px;
}
#boxThree{
width: 130px;
height: 61px;
font-size: 25px;
overflow: hidden;
position: absolute;
background: #dad2e9;
color: #eee5ff;
text-align: center;
line-height: 25px;
left: 180px;
top: 210px;
}
#stopOne{
width: 140px;
background: #795548;
}
#stopTwo{
width: 140px;
background: #009688;
}
#stopAll{
width: 150px;
background: linear-gradient(to top left, #009688, #795548);
}
#stopOne:active{
background: #00af00;
}
#stopTwo:active{
background: #00af00;
}
#stopAll:active{
background: #00af00;
}
JavaScript
// В скором времени var станет deprecated. ES2015 подарил нам let и const
let box = document.querySelectorAll('.box'),
workSpace = document.querySelectorAll('.workSpace')[0],
workArea = {}, coord = {}, interval = {};
//Когда не нужно возвращать массив, старайтесь не использовать без конечной ссылки map\filter\reduce и т.д. Неименованный список будет не доступ сборщику мусора. И у коллекций тоже есть метод forEach
box.forEach((e,i)=>{
workArea[i] = {
x : workSpace.offsetWidth - e.offsetWidth,
y : workSpace.offsetHeight - e.offsetHeight
};
coord[i] = {
x: 0,
y: 0
};
moveRight(e,i);
});
function moveRight(e,i){
interval[i] = requestAnimationFrame( ()=>moveRight(e,i) );
if(coord[i].x <= workArea[i].x){
coord[i].x += 5;
e.style.left = coord[i].x + 'px';
}
if(coord[i].x > workArea[i].x){
cancelAnimationFrame(interval[i]);
}
}
// остановить по-отдельности
//Если делегирование все-таки не используете, назначайте обработчик в мапе, а не в цикле
// И старайтесь там где возможно использовать живые коллекции,
//во-превых они быстрее отрабатывают;
//во-вторых если у вас смениться дерево элемента, такая коллекция подхватит изменения.
Array.from(document.getElementsByClassName('stopButton'),(e,i)=>{
e.addEventListener('click', ()=> {
cancelAnimationFrame(interval[i]);
})});
// остановить все
//Ну и напоследок, .onclick это свойство, в которое запишется всего одно событие
//или перезапишет, если уже было. Только addEventListener пополняет очередь.
//Лучше привыкать писать методом.
// И в обработчик лучше пихать именованную функцию или функциональное выражение.
// По этой ссылке можно будет удалить обработчик при необходимости.
// Например помочь сборщику перед выходом со страницы.
//Но для этого приложения это не критично так что....
document.querySelector('#stopAll').addEventListener('click', ()=> {
for(i=0; i<Object.keys(interval).length; i++){
cancelAnimationFrame(interval[i]);
}
});