Как динамически создавать пути анимации через JavaScript? - PullRequest
4 голосов
/ 01 августа 2020

Я работаю над анимированным фоном.

Я стремлюсь к тому, чтобы «пузыри» перемещались по случайной траектории со случайной скоростью. Достигнув края экрана, он телепортируется обратно на противоположную сторону, чтобы продолжить путь.

Я бы сделал анимацию через CSS, но я хочу, чтобы путь, по которому следует каждый «пузырь», был случайным.

Примерно так для каждого элемента.

Each element follows its own randomly generated path and repeats it indefinitely.

How would I achieve this effect through JavaScript?

This is the project in it's current state.

Codepen : https://codepen.io/JosephChunta/pen/WNrKxeY

Текущее JavaScript

const orderedNumber = document.querySelectorAll('.backgroundBubble');
var colors = [
  '#ff5b00', '#b8d500', '#795ced', 
  '#ab004a', '#21aec0', '#fe9300' 
];
for (li of orderedNumber) {
  var random_color = colors[Math.floor(Math.random() * colors.length)];
  li.style['background'] = random_color;
  var random_dimensions = Math.floor(Math.random() * 20 + 5);
  li.style['width'] = random_dimensions + "px";
  li.style['height'] = random_dimensions + "px";
  var random_left = Math.floor(Math.random() * 99 + 1);
  li.style['left'] = random_left + "%";
  var random_top = Math.floor(Math.random() * 99 + 1);
  li.style['top'] = random_top + "%";
}

«Пузырь» HTML

<div class="context">
  <h1>Hello</h1>
</div>

<div class="area">
  <ul class="circles">
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
  </ul>
</div>

Ответы [ 2 ]

2 голосов
/ 01 августа 2020

Вот решение, которое может сработать для вас:

  • Каждый элемент (круг) перемещается по случайному пути, используя свойство transform: translate( x, y ) CSS

  • Каждый элемент получает случайное смещение x, y, на которое он движется по немного другой траектории

  • Когда каждый элемент достигает границ области просмотра (window.innerWidth = far справа, window.innerHeight = bottom, 0 = top и left) он меняет направление, инвертируя начальные значения смещения offsetX и offsetY

  • Анимация в настоящее время реализуется с использованием setInterval, но лучшим кандидатом может быть requestAnimationFrame.

(Взгляните на комментарии, которые я разместил вместе с недавно вставленным кодом.)

Codepen

const orderedNumber = document.querySelectorAll('.backgroundBubble');
var colors = [
  '#ff5b00', '#b8d500', '#795ced', 
  '#ab004a', '#21aec0', '#fe9300' 
];
const liData = [];

orderedNumber.forEach((li,index)=>{
  
  var random_color = colors[Math.floor(Math.random() * colors.length)];
  li.style['background'] = random_color;
  var random_dimensions = Math.floor(Math.random() * 20 + 5);
  li.style['width'] = random_dimensions + "px";
  li.style['height'] = random_dimensions + "px";
  var random_left = Math.floor(Math.random() * 99 + 1);
  li.style['left'] = random_left + "%";
  var random_top = Math.floor(Math.random() * 99 + 1);
  li.style['top'] = random_top + "%";
  // ADDING MOVEMENT VALUES: In here each element
  // gets some additional data that will be used to define its movement.
  liData[index] = {
    hasFlipped: false, // Has the element hit a viewport boundary? If yes, this property gets flipped: false -> true -> false
    targetX: 0, // Current x position. Starts at 0.
    targetY: 0, // Current y position. Starts at 0.
    offsetX: Math.floor( Math.random() * 10 ), // Random x offset by which the element will move across the x axis
    offsetY: Math.floor( Math.random() * 10 ) // Random y offset by which the element will move across the y axis
  }

});

setInterval(function(){
  orderedNumber.forEach((li,index)=>{
    // Next line will give us the top, left, right and bottom position of the element:
    const { top, left, right, bottom } = li.getBoundingClientRect();
    // Get the offsetX and offsetY so that we can move the element
    let { offsetX, offsetY } = liData[index];
    if ( liData[index].hasFlipped ){
      // Did the element just hit the top or left viewport boundaries?
      if ( top <= 0 || left <= 0 ){
        // ...if so, flip its movement direction
        liData[index].hasFlipped = false;
      }
      liData[index].targetX -= offsetX;
      liData[index].targetY -= offsetY;

    } else {
      // Did the element just hit the bottom, right viewport boundaries?
      // ...if so, flip its movement direction
      if ( bottom >= window.innerHeight || right >= window.innerWidth ){
        liData[index].hasFlipped = true;
      }
      liData[index].targetX += offsetX;
      liData[index].targetY += offsetY;
    }
      li.style.transform = `translate( ${liData[index].targetX}px,  ${liData[index].targetY}px )`;

  });

}, 50 )
@import url('https://fonts.googleapis.com/css?family=Exo:400,700');

*{
    margin: 0px;
    padding: 0px;
}

.context {
    width: 100%;
    position: absolute;
    top:50vh;
}

.context h1{
    text-align: center;
    color: #fff;
    font-size: 50px;
    font-family: 'Exo', sans-serif;
}


.area{
    background: #2a2e31;  
    background: -webkit-linear-gradient(to left, #8f94fb, #4e54c8); // #026e9f #03a9f4
    width: 100%;
    height:100vh;
}

.circles{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.circles li{
    position: absolute;
    display: block;
    list-style: none;
    bottom: -150px;
    border-radius: 50%;
}
<div class="context">
  <h1>Hello</h1>
</div>

<div class="area">
  <ul class="circles">
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
  </ul>
</div>

Как можно изменить код движения, чтобы вместо изменения направления движения он отскакивал от стены?

В этом случае мы можем проверить, мяч попадает в одну из границ, и если он попадает в одну из левых / правых сторон, мы переворачиваем знак X (положение -> отрицательное -> положительное -> ...) в противном случае, если он попадает в одну из верхних / нижних сторон сторон мы переворачиваем знак Y.

Вот эскиз, чтобы прояснить это:

image

const orderedNumber = document.querySelectorAll('.backgroundBubble');
var colors = [
  '#ff5b00', '#b8d500', '#795ced', 
  '#ab004a', '#21aec0', '#fe9300' 
];
const liData = [];

orderedNumber.forEach((li,index)=>{
  
  var random_color = colors[Math.floor(Math.random() * colors.length)];
  li.style['background'] = random_color;
  var random_dimensions = Math.floor(Math.random() * 20 + 5);
  li.style['width'] = random_dimensions + "px";
  li.style['height'] = random_dimensions + "px";
  var random_left = Math.floor(Math.random() * 99 + 1);
  li.style['left'] = random_left + "%";
  var random_top = Math.floor(Math.random() * 99 + 1);
  li.style['top'] = random_top + "%";
  // ADDING MOVEMENT VALUES:
  liData[index] = {
    targetX: 0,
    targetY: 0,
    offsetX: Math.floor( Math.random() * 10 ),
    offsetY: Math.floor( Math.random() * 10 ),
    directionX: 1, // Define each ball's direction using a multiplier. Positive means left-to-right. Negative means right-to-left.
    directionY: 1  // Same here, but for top-to-bottom and vice versa
  }

});

setInterval(function(){
  orderedNumber.forEach((li,index)=>{
    
    const { top, left, right, bottom } = li.getBoundingClientRect();
    let { offsetX, offsetY } = liData[index];
   
    // If we've just hit the top or bottom boundaries, we'll flip the Y direction:
    if ( top <= 0 || bottom >= window.innerHeight ){ 
      liData[index].directionY = -liData[index].directionY; 
    } 
    // If we've just hit the left or right boundaries, we'll flip the X direction:
    if ( left <= 0 || right >= window.innerWidth){ 
      liData[index].directionX = -liData[index].directionX;
    } 

    // The multiplier, either 1 or -1, defines the balls direction on each axis:
    liData[index].targetX += offsetX * liData[index].directionX; 
    liData[index].targetY += offsetY * liData[index].directionY; 

    li.style.transform = `translate( ${liData[index].targetX}px,  ${liData[index].targetY}px )`;

  });

}, 50 )
@import url('https://fonts.googleapis.com/css?family=Exo:400,700');

*{
    margin: 0px;
    padding: 0px;
}

.context {
    width: 100%;
    position: absolute;
    top:50vh;
}

.context h1{
    text-align: center;
    color: #fff;
    font-size: 50px;
    font-family: 'Exo', sans-serif;
}


.area{
    background: #2a2e31;  
    background: -webkit-linear-gradient(to left, #8f94fb, #4e54c8); // #026e9f #03a9f4
    width: 100%;
    height:100vh;
}

.circles{
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.circles li{
    position: absolute;
    display: block;
    list-style: none;
    bottom: -150px;
    border-radius: 50%;
}
<div class="context">
  <h1>Hello</h1>
</div>

<div class="area">
  <ul class="circles">
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
    <li class="backgroundBubble"></li>
  </ul>
</div>

Codepen

Следующие шаги:

  • Вы можете использовать requestAnimationFrame вместо setInterval() для повышения производительности.

  • Поэкспериментируйте с различными случайными схемами для значений смещения x и y, чтобы ввести больше траекторий и элементов, движущихся с разными скоростями

0 голосов
/ 01 августа 2020

Это может сработать, но вместо телепортации на другой конец экрана он отскакивает назад, создавая более интересную анимацию.

 const orderedNumber = document.querySelectorAll('.backgroundBubble');
var colors = [
  '#ff5b00', '#b8d500', '#795ced', 
  '#ab004a', '#21aec0', '#fe9300' 
];
const liData = [];

orderedNumber.forEach((li,index)=>{
  
  var random_color = colors[Math.floor(Math.random() * colors.length)];
  li.style['background'] = random_color;
  var random_dimensions = Math.floor(Math.random() * 20 + 5);
  li.style['width'] = random_dimensions + "px";
  li.style['height'] = random_dimensions + "px";
  var random_left = Math.floor(Math.random() * 99 + 1);
  li.style['left'] = random_left + "%";
  var random_top = Math.floor(Math.random() * 99 + 1);
  li.style['top'] = random_top + "%";
  // ADDING MOVEMENT VALUES:
  liData[index] = {
    hasFlipped: false,
    targetX: 0,
    targetY: 0,
    offsetX: Math.floor( Math.random() * 10 ),
    offsetY: Math.floor( Math.random() * 10 )
  }

});

setInterval(function(){
  orderedNumber.forEach((li,index)=>{
    const { top, left, right, bottom } = li.getBoundingClientRect();
    let { offsetX, offsetY } = liData[index];
    if ( liData[index].hasFlipped ){
      if ( top <= 0 || left <= 0 ){
        liData[index].hasFlipped = false;
      }
      liData[index].targetX -= offsetX;
      liData[index].targetY -= offsetY;

    } else {
      if ( bottom >= window.innerHeight || right >= window.innerWidth ){
        liData[index].hasFlipped = true;
      }
      liData[index].targetX += offsetX;
      liData[index].targetY += offsetY;
    }
      li.style.transform = `translate( ${liData[index].targetX}px,  ${liData[index].targetY}px )`;

  });

}, 50 )

просто замените это на свой javascript, и он будет работать

...