Как разбить гибкую строку на новую строку для динамических данных

Я работаю над приложением для видеовызовов, используя реакцию. Компонент LocalTile содержит видео локального пользователя, а RemoteTiles возвращает видео всех удаленных пользователей. Мой желаемый макет похож на 3 пользователей:

===video1=video2===
=======video3======

для 4 пользователей:

===video1=video2===
===video3=video4===

для 5 пользователей:

===video1=video2=video3===
=======video4=video5======



<div id='gallery-parent' style = {{ display: 'flex', flex: '1 1', flexDirection: 'column', gap: '1rem', position: 'relative' }}>
       <div style = {{ alignItems: 'stretch',  display: 'flex', flex: '1 1', gap: '1rem',justifyContent:'center'}}>
         <LocalTile />
         <RemoteTiles />
       </div>                    
</div>

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

Можете ли вы предоставить минимально воспроизводимый пример с базовой структурой?

Paulie_D 25.07.2024 19:47

Существует ли максимальное количество детей? Flexbox не может динамически управлять вашими макетами. Ограничено ли количество строк. Почему в макете для 5 пользователей нет 3 строк, 2+2+1, как в макете для 3 пользователей?

Paulie_D 25.07.2024 19:56
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
60
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Попробуйте установить min-width: in (vw or %) unit + flex-wrap: wrap;

  • минимальная ширина: в единицах vw относительно ширины экрана, в % от родительской ширины

  • flex-wrap: обертка; чтобы позволить оставшемуся элю перейти на следующую строку

Так:

{
  min-width: 18%; 
  flex-wrap: wrap;

  aspect-ratio: 1; // bind the height to the width 
}

Я тоже попробовал гибкую пленку. Третье видео перенесено на следующую строку, но проблема в высоте. Видео, возвращаемые из компонентов, имеют ширину 100%, тогда с помощью гибкой обертки он отображает 3-е видео во 2-м ряду и показывает половину видео из-за скрытого переполнения.

hamza_dilber 25.07.2024 19:42

{ flex: '1 1' } — проблема с шириной третьего элемента, поскольку ничто не ограничивает ширину дочернего элемента, попробуйте: '{max-width: 50%; соотношение сторон: 1;}'

Mister HS 25.07.2024 20:38

Я предполагаю максимум 2 строки и максимум 5 детей.

Это возможно, если использовать комбинацию nth-child и nth-last-child для выбора элементов в зависимости от количества дочерних элементов.

Для этого необходимо установить определенную ширину в зависимости от количества элементов, но поскольку CSS не может фактически подсчитывать элементы, это невозможно масштабировать.

.container {
  display: flex;
  flex-wrap: wrap;
  margin-bottom: .5em;
  border:2px solid blue;
  padding: .25em;
}

.item {
  display: grid;
  place-items:center;
  font-size: 1.25em;
  height: 40px;
  background: lightblue;
  outline:1px solid black;
}

.item {
  flex:1;
}



/* 1 & 2 of 3 */
.item:first-child:nth-last-child(3),
.item:nth-child(2):nth-last-child(2)
{
  flex: 0 0 50%;
}

/* 1 - 4 of 4 */
.item:nth-child(1):nth-last-child(4),
.item:nth-child(2):nth-last-child(3),
.item:nth-child(3):nth-last-child(2),
.item:nth-child(4):nth-last-child(1)
{
  flex: 0 0 50%;
}
/* 1-3 of 5 */
.item:nth-child(1):nth-last-child(5),
.item:nth-child(2):nth-last-child(4),
.item:nth-child(3):nth-last-child(3)
{
  flex: 0 0 33.333%;
}
/* 4 & 5 of 5 */
.item:nth-child(4):nth-last-child(2),
.item:nth-child(5):nth-last-child(1)
{
  flex: 0 0 50%;
}
<div class = "container">
  <div class = "item">1</div>
  <div class = "item">2</div>
  <div class = "item">3</div>
</div>
<div class = "container">
  <div class = "item">1</div>
  <div class = "item">2</div>
  <div class = "item">3</div>
  <div class = "item">4</div>

</div>

<div class = "container">
  <div class = "item">1</div>
  <div class = "item">2</div>
  <div class = "item">3</div>
  <div class = "item">4</div>
  <div class = "item">5</div>
</div>

Примечание. Если требуется больше строк, лучше использовать такие макеты, как

2+1 для 3 пользователей

2+2 для 4 пользователей

2+2+1 для 5 пользователей и т.д.

Это делает CSS намного проще и масштабируемее.

Ответ принят как подходящий

Я не уверен на 100%, как бы вы реализовали это в своей более крупной кодовой базе, но я бы сделал это именно так в ванильном JS. После того, как список вызывающих абонентов будет отображен, вы можете взять тот, который находится чуть дальше половины, и вставить перед ним несжимающийся элемент полной ширины, что приведет к выполнению переноса.

Этот небольшой пример утилиты допускает в поле ввода n количество пользователей и разделяет их, как и ожидалось.

function updateHandler(){
  if (input.value === '') return;
  //else
  const n = parseInt(input.value),
    half = Math.ceil(n/2)
  output.innerHTML = Array.apply(null, Array(n)).map(() => `<div class = "caller"></div>`).join('')
  
  const halfwayElem = output.querySelectorAll('.caller')[half],
    splitter = document.createElement('hr')
    
  splitter.className = 'callers-divider'
  
  output.insertBefore(splitter, halfwayElem)
}

updateHandler()
input.oninput = updateHandler
#output {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
 }
 .caller {
  flex: 1;
  border: 1px solid black;
  margin: 8px;
  aspect-ratio: 4/3;
 }
 .callers-divider {
  display: block;
  width: 100%;
  flex-shrink: 0;
}
<label>Number of users connected: <input id = "input" value = "5" /></label>
<div id = "output"></div>

В качестве альтернативы, если вы не хотите, чтобы вторая строка занимала всю ширину и просто центрируйте два вызывающих объекта, это можно сделать с помощью CSS вместо манипуляций с DOM. При этом используется побитовый оператор n ^ 1, чтобы определить, является ли n нечетным, и если да, то увеличить его до следующего четного числа.

function updateHandler(){
  if (input.value === '') return;
  //else
  let n = parseInt(input.value)
    
  output.innerHTML = Array.apply(null, Array(n)).map(() => `<div class = "caller"></div>`).join('')
  
  const shiftedN = n ^ 1
  if (n !== shiftedN){
    ++n
  }
  
  const callerWidth = (200/n) + '%'
  output.style.setProperty('--caller-width', callerWidth)
}

updateHandler()
input.oninput = updateHandler
#output {
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
 }
 .caller {
  border: 1px solid black;
  aspect-ratio: 4/3;
  box-sizing: border-box;
  width: var(--caller-width);
 }
<label>Number of users connected: <input id = "input" value = "5" /></label>
<div id = "output"></div>

Из этого ответа я получил логику для своего сценария и работал на себя. Спасибо.

hamza_dilber 27.07.2024 23:24

Хотя это не очень хорошо, но вы можете сделать это с помощью CSS Grid. Например, что-то вроде этого:

body {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

.videos {
  display: inline-grid;
  gap: 4px;
  counter-reset: counter;
  &:has(.video:nth-last-child(3)) {
    grid-template-columns: 1fr 1fr;
    .video:last-child {
      grid-column: span 2;
      margin: 0 auto;
    }
  }
  &:has(.video:nth-last-child(4)) {
    grid-template-columns: 1fr 1fr;
    .video:last-child {
      grid-column: unset;
    }
  }
  &:has(.video:nth-last-child(5)) {
    grid-template-columns: repeat(6, 1fr);
    .video {
      grid-column: span 2;
      &:last-child {
        grid-column: span 3;
        margin: 0 auto 0 0;
      }
      &:nth-last-child(2) {
        grid-column: span 3;
        margin: 0 0 0 auto;
      }
    }
  }
}

.video {
  background: orange;
  &:before {
    content: 'video' counter(counter);
    counter-increment: counter;
  }
}
<div class = "videos">
  <div class = "video"></div>
  <div class = "video"></div>
  <div class = "video"></div>
</div>
<div class = "videos">
  <div class = "video"></div>
  <div class = "video"></div>
  <div class = "video"></div>
  <div class = "video"></div>
</div>
<div class = "videos">
  <div class = "video"></div>
  <div class = "video"></div>
  <div class = "video"></div>
  <div class = "video"></div>
  <div class = "video"></div>
</div>

Другие вопросы по теме