无法在排序算法可视化程序中实现洗牌条功能 – setTimeout 更新未渲染

huangapple go评论61阅读模式
英文:

Cannot implement shuffle bars feature in sorting algorithm visualizer - setTimeout updates are not rendering

问题

我正在制作一个排序算法可视化项目,正在实现一个洗牌按钮的过程中。可视化涉及不同高度的条形图被移动。我想让洗牌按钮每次修改条形数组一步,以高速展示每次交换。我尝试了许多不同的调整(其中一些确实以奇怪的方式移动了条形图),但似乎无法使所需的功能正常工作。以下是一些相关的代码:

// Swap不会修改原始数组,但实现细节不会真正影响结果。
// 基本上它会交换条形数组中的两个元素,然后返回更新后的数组。

const swapBars = (bars, bar1, bar2) => {
  if (!bars || !bar1 || !bar2) {
    return;
  }
  const _bars = bars;
  let _bar1 = bar1;
  let _bar2 = bar2;

  const tempLeft = _bar1.left;
  _bar1.left = _bar2.left;
  _bar2.left = tempLeft;

  const temp = _bar1;
  _bar1 = _bar2;
  _bar2 = temp;

  return _bars;
};

// 初始化条形图基本上是同步洗牌。它获取创建的数组并将其洗牌
// 因为数组应该在洗牌状态下开始。这正常工作。

const initBars = (bars) => {
  let currentIndex = bars.length - 1;
  while (currentIndex > 0) {
    // randomIndex始终与currentIndex不同,因此每个条都会洗牌
    const randomIndex = Math.floor(Math.random() * currentIndex);
    swapBars(bars, bars[currentIndex], bars[randomIndex]);
    currentIndex--;
  }
  setBarsToRender(bars);
};

// createBarArray用于根据滑块传递的数量实际填充空数组中的条形图。
// 这也正常工作。

const createBarArray = (quantity) => {
  let bars = [];
  const width = calcWidthPercentage(quantity);
  for (let i = 0; i < quantity; i++) {
    const height = calcHeightPercentage(quantity, i + 1);
    const left = calcLeftPosPercentage(quantity, i + 1);
    bars.push({ correctPos: i, height: height, width: width, left: left });
  }
  return initBars(bars);
};

// shuffleBars似乎是有问题的。我尝试了很多不同的方法,这只是最新的快照。
// 当单击洗牌按钮时,使用`shuffleBars(barsToRender)`进行调用,其中barsToRender是正在渲染的状态值。

const shuffleBars = (bars) => {
  let currentIndex = bars.length - 1;
  while (currentIndex > 0) {
    const randomIndex = Math.floor(Math.random() * currentIndex);
    setTimeout(() => {
      setBarsToRender((prev) => {
        return swapBars(prev, prev[currentIndex], prev[randomIndex]);
      });
    }, 50 * (bars.length - currentIndex));
    currentIndex--;
  }
};

如果我像将swapBars调用移到setBarsToRender之外,然后执行setBarsToRender[...bars],我可以看到一些条形图移动,但不是预期的行为(最小的条形图是唯一一个不断交换的)。我不确定我是否误解了setTimeout内部的状态更新工作方式,或者是否还有其他问题,因此我非常感谢您的帮助。

英文:

I'm making a sorting algorithm visualizer project and I'm in the process of implementing a shuffle button. The visualization involves bars of different heights getting moved around. I would like the shuffle button to modify the bar array one step at a time, showcasing each swap at a high speed. I've tried tweaking many different things (some of which do move bars around in a strange manner), but I can't seem to get the desired functionality to work. Here's some of the relevant code:

// Swap does not modify the original array, but the implementation details don&#39;t really affect the result. 
// It basically swaps two elements in the bar array, and then returns the updated array.
const swapBars = (bars, bar1, bar2) =&gt; {
if (!bars || !bar1 || !bar2) {
return;
}
const _bars = bars;
let _bar1 = bar1;
let _bar2 = bar2;
const tempLeft = _bar1.left;
_bar1.left = _bar2.left;
_bar2.left = tempLeft;
const temp = _bar1;
_bar1 = _bar2;
_bar2 = temp;
return _bars;
};
// Init bars is basically synchronous shuffle. It takes the array that is created and shuffles it
// because the array should begin in a shuffled state. This is working properly.
const initBars = (bars) =&gt; {
let currentIndex = bars.length - 1;
while (currentIndex &gt; 0) {
// randomIndex will always be different from currentIndex, so each bar will always shuffle
const randomIndex = Math.floor(Math.random() * currentIndex);
swapBars(bars, bars[currentIndex], bars[randomIndex]);
currentIndex--;
}
setBarsToRender(bars);
};
// createBarArray is what is used to actually populate an empty array with bars depending on a number passed
// through by a slider. This is also working properly.
const createBarArray = (quantity) =&gt; {
let bars = [];
const width = calcWidthPercentage(quantity);
for (let i = 0; i &lt; quantity; i++) {
const height = calcHeightPercentage(quantity, i + 1);
const left = calcLeftPosPercentage(quantity, i + 1);
bars.push({ correctPos: i, height: height, width: width, left: left });
}
return initBars(bars);
};
// shuffleBars seems to be broken. I&#39;ve tried many different things, and this is just the latest snapshot of it.
// It is being called when the shuffle button is being clicked using `shuffleBars(barsToRender)` where barsToRender is the stateful value that is being rendered.
const shuffleBars = (bars) =&gt; {
let currentIndex = bars.length - 1;
while (currentIndex &gt; 0) {
const randomIndex = Math.floor(Math.random() * currentIndex);
setTimeout(() =&gt; {
setBarsToRender((prev) =&gt; {
return swapBars(prev, prev[currentIndex], prev[randomIndex]);
});
}, 50 * (bars.length - currentIndex));
currentIndex--;
}
};

If I do something like moving the swapBars call inside setBarsToRender outside of it and then
do setBarsToRender[...bars], I can see some of the bars moving, but not with the intended behavior (the smallest bar is the only one that keeps swapping). I'm not sure if I'm misunderstanding how state updates work inside setTimeout, or if it's something else, so I'd greatly appreciate some help.

答案1

得分: 1

我去掉了setTimeout,并使用了过渡延迟来创建交错效果。

以下是工作演示:

const swapBars = (bars, bar1, bar2) => {
  if (!bars || !bar1 || !bar2) {
    return;
  }
  const _bars = bars;
  let _bar1 = bar1;
  let _bar2 = bar2;

  const tempLeft = _bar1.left;
  _bar1.left = _bar2.left;
  _bar2.left = tempLeft;

  const temp = _bar1;
  _bar1 = _bar2;
  _bar2 = temp;

  return _bars;
};

const initBars = (bars) => {
  let currentIndex = bars.length - 1;
  while (currentIndex > 0) {
    const randomIndex = Math.floor(Math.random() * currentIndex);
    swapBars(bars, bars[currentIndex], bars[randomIndex]);
    currentIndex--;
  }
  return bars;
};

const createBarArray = (quantity) => {
  let bars = [];
  const width = 100 / quantity;
  for (let i = 0; i < quantity; i++) {
    const height = width * (i + 1);
    const left = width * i;
    bars.push({ correctPos: i, height: height, width: width, left: left });
  }
  return initBars(bars);
};

function Bars({ quantity = 10 }) {
  const [barsToRender, setBarsToRender] = React.useState([]);

  React.useEffect(() => {
    const bars = createBarArray(quantity);

    setBarsToRender(bars);
  }, [quantity]);

  const shuffleBars = () => {
    const bars = [...barsToRender];
    setBarsToRender(initBars(bars));
  };

  return (
    <div>
      <ul
        style={{
          height: "50vh",
          display: "flex",
          position: "relative"
        }}
      >
        {barsToRender.map((bar, i) => (
          <Bar key={bar.correctPos} bar={bar} index={i} />
        )}
      </ul>
      <button onClick={shuffleBars}>Shuffle</button>
    </div>
  );
}

function Bar({ bar, index: i }) {
  return (
    <li
      style={{
        background: "blue",
        height: `${bar.height}%`,
        width: `${bar.width}%`,
        left: `${bar.left}%`,
        position: "absolute",
        bottom: 0,
        transitionProperty: "left",
        transitionTimingFunction: "ease-in-out",
        transitionDuration: ".25s",
        transitionDelay: `${i*50}ms`
      }}
    >
      <p>{bar.correctPos}</p>
    </li>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<Bars />
p {
  font-family: sans-serif;
  font-weight: 700;
  height: 1.5rem;
  width: 1.5rem;
  display: grid;
  place-content: center;
  background: white;
  border-radius: 50%;
  border: 1px solid;
}

ul {
  padding: 0;
  list-style-type: none;
}

li {
  display: grid;
  align-content: end;
  justify-items: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
<div id="root"></div>
英文:

I removed the setTimeout and used a transition delay to create the staggered effect.

Working demo below:

<!-- begin snippet: js hide: false console: true babel: true -->

<!-- language: lang-js -->

const swapBars = (bars, bar1, bar2) =&gt; {
if (!bars || !bar1 || !bar2) {
return;
}
const _bars = bars;
let _bar1 = bar1;
let _bar2 = bar2;
const tempLeft = _bar1.left;
_bar1.left = _bar2.left;
_bar2.left = tempLeft;
const temp = _bar1;
_bar1 = _bar2;
_bar2 = temp;
return _bars;
};
const initBars = (bars) =&gt; {
let currentIndex = bars.length - 1;
while (currentIndex &gt; 0) {
const randomIndex = Math.floor(Math.random() * currentIndex);
swapBars(bars, bars[currentIndex], bars[randomIndex]);
currentIndex--;
}
return bars;
};
const createBarArray = (quantity) =&gt; {
let bars = [];
const width = 100 / quantity;
for (let i = 0; i &lt; quantity; i++) {
const height = width * (i + 1);
const left = width * i;
bars.push({ correctPos: i, height: height, width: width, left: left });
}
return initBars(bars);
};
function Bars({ quantity = 10 }) {
const [barsToRender, setBarsToRender] = React.useState([]);
React.useEffect(() =&gt; {
const bars = createBarArray(quantity);
setBarsToRender(bars);
}, [quantity]);
const shuffleBars = () =&gt; {
const bars = [...barsToRender];
setBarsToRender(initBars(bars));
};
return (
&lt;div&gt;
&lt;ul
style={{
height: &quot;50vh&quot;,
display: &quot;flex&quot;,
position: &quot;relative&quot;
}}
&gt;
{barsToRender.map((bar, i) =&gt; (
&lt;Bar key={bar.correctPos} bar={bar} index={i} /&gt;
))}
&lt;/ul&gt;
&lt;button onClick={shuffleBars}&gt;Shuffle&lt;/button&gt;
&lt;/div&gt;
);
}
function Bar({ bar, index: i }) {
return (
&lt;li
style={{
background: &quot;blue&quot;,
height: `${bar.height}%`,
width: `${bar.width}%`,
left: `${bar.left}%`,
position: &quot;absolute&quot;,
bottom: 0,
transitionProperty: &quot;left&quot;,
transitionTimingFunction: &quot;ease-in-out&quot;,
transitionDuration: &quot;.25s&quot;,
transitionDelay: `${i*50}ms`
}}
&gt;
&lt;p&gt;{bar.correctPos}&lt;/p&gt;
&lt;/li&gt;
);
}
ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(&lt;Bars /&gt;)

<!-- language: lang-css -->

p {
font-family: sans-serif;
font-weight: 700;
height: 1.5rem;
width: 1.5rem;
display: grid;
place-content: center;
background: white;
border-radius: 50%;
border: 1px solid;
}
ul {
padding: 0;
list-style-type: none;
}
li {
display: grid;
align-content: end;
justify-items: center;
}

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;
&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年2月18日 20:33:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/75493360.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定