尝试调整组件的大小 onUpdate

Trying to resize a component onUpdate

我编写了一个组件,如果 div 中有 space 足够的宽度可用,它会将数组中的字符串呈现为 div 中的跨度。

调用 renderDinos() 方法 onMount() 按预期工作。从那里我想更新组件以在 div 的宽度发生变化时动态地重新呈现 div 中的字符串(从数组中添加或减去字符串)。

为了实现这一点,如果组件的宽度发生变化,我将在 afterUpdate() 中再次调用 renderDinos() 。当onMount()运行时scrollWidth是恐龙的宽度[i]所以scrollWidthSum计算准确。

当 afterUpdate() 调用相同的方法时,scrollWidth 的初始值为 0,而不是恐龙 [i] 的宽度。这会打乱 scrollWidthSum 的计算。我还想从 if 块中的 currentWidth 中减去 scrollWidth,以便更好地对齐子项,但是,再次;当我从 onUpdate 调用方法时,该值最初为 0。

有办法解决吗? div 调整大小时,是否有更简单的方法重新呈现其内容?

<script>
  import { onMount, tick, afterUpdate } from 'svelte'

  let dinosaurs = [
    'dinosaur1',
    'dinosaur2',
    'dinosaur3',
    'dinosaur4',
    'dinosaur5',
  ]

  let currentWidth = 0,
    previousWidth = 0,
    scrollWidth,
    scrollWidthSum

  $: dinosToShow = []

  onMount(async () => {
    previousWidth = currentWidth
    renderDinos()
  })

  afterUpdate(() => {
    if (previousWidth != currentWidth) {
      renderDinos()
      previousWidth = currentWidth
    }
  })

  async function renderDinos() {
    dinosToShow = []
    scrollWidthSum = 0
    scrollWidth = 0

    for (let i = 0; i < dinosaurs.length; i++) {
      dinosToShow = [...dinosToShow, `${dinosaurs[i]},\xa0`]
      await tick()

      console.log(scrollWidth)
      scrollWidthSum += scrollWidth

      if (scrollWidthSum > currentWidth - 60) {
        dinosToShow.pop()
        await tick()
        break
      }
    }
  }
</script>

<style>
  .display {
    overflow: hidden;
  }
  .dinosaur {
    display: inline-block;
  }
</style>

<div class="display" bind:clientWidth={currentWidth}>
  {#each dinosToShow as r}
    <span bind:clientWidth={scrollWidth} class="dinosaur">{r}</span>
  {/each}
</div>

您将每个跨度的 clientWidth 绑定到同一个变量,随着每个循环的进行而更改它,这被认为是一种不好的做法。

我认为做这样的事情会更好(添加评论以获得一些解释):

<script>
  let dinosaurs = [
    'dinosaur1',
    'dinosaur2',
    'dinosaur3',
    'dinosaur4',
    'dinosaur5',
  ]
    
  let currentWidth = 0;
  // have the array updated when currentWidth updates
  $: dinosToShow = render(currentWidth);
    
  function render(currentWidth) {
    // Early return
    if (currentWidth == 0) return [];
        
    let i = 0;
    let sumWidth = 0;

    // While instead of for, removes the need for breaks        
    while(i < dinosaurs.length && sumWidth < currentWidth) {
      // Create a dummy span and add it somewhere far off screen
      let span = document.createElement('span')
      span.classList.add('dinosaur')
      span.textContent = `${dinosaurs[i]},\xa0`
      span.style = "visibility: hidden; position: fixed; top: -1000;";
      document.body.appendChild(span);
      // Sum the width of the dummy elements
      sumWidth += span.getBoundingClientRect().width
      // Remove the dummy again
      document.body.removeChild(span)
    
      // if the sum with the dummy is less than the current width do i + 1
      // this will effectively include this dino in the list
      if (sumWidth < currentWidth) {
        i++
      }
    }

   // return the subarray that fits.
   // note that if sumWidth > currentWidth this will NOT include the dino that was too big  
   return dinosaurs.slice(0, i)
  }
</script>

<div class="display" bind:clientWidth={currentWidth}>
  {#each dinosToShow as r}
    <!-- No binding of stuff here -->
    <span class="dinosaur">{r}</span>
  {/each}
</div>

<style>
  .display {
      overflow: hidden;
  }
  .dinosaur {
    display: inline-block;
  }
</style>