.information
  span It takes
  span.step 0
  span steps.

.wrapper
  // generate boxes

.operator
  button.sort(onclick="bubbleSort()") SORT
  button.reset(onclick="reset()") RESET
View Compiled
body
  margin: 0
  padding: 0
  display: flex
  justify-content: center
  align-items: center
  flex-direction: column
  height: 100vh
  font-family: 'Barlow', sans-serif
  background-color: #333

.information
  color: #fff
  font-size: 1.5rem
  margin-bottom: 1.25rem

  > *
    margin: 0 0.125rem

.wrapper
  display: flex
  position: relative
  width: 800px
  height: 80px

.box
  display: flex
  justify-content: center
  align-items: center
  position: absolute
  width: 80px
  height: 80px
  border: 4px solid #000
  background-color: #fff
  font-size: 3rem
  font-weight: 500
  left: calc(var(--i) * 80px)
  transition: all 1s
  overflow: hidden

  &:before
    content: ''
    width: 100%
    height: 100%
    position: absolute
    top: -5%
    left: -90%
    background-color: rgba(silver, 0.3)
    transform: skew(10deg)

.operator
  margin: 2rem

  button
    padding: 0.25rem
    width: 100px
    font-size: 1.5rem
    background-color: #fff
    cursor: pointer
    border: 3px solid #000
    font-family: 'Barlow', sans-serif
    font-weight: 500
    position: relative
    overflow: hidden

    &:before
      content: ''
      width: 100%
      height: 100%
      position: absolute
      top: 0
      left: 0
      transition: 0.4s
      background-color: rgba(gold, 0.5)
      transform: scale(0)

    &:hover
      box-shadow: 0 0 10px 0 rgba(gold, 0.4)

      &:before
        transform: scale(1)
        background-color: rgba(gold, 0.06)
View Compiled
const example = [4, 7, 0, 5, 3, 0, 9, 2, 8, 6];

let arr = [];
let sortStep = 0;
let swapStep = 0;

// 建立 box
function createEl() {
  let wrapper = document.querySelector(".wrapper");
  let index = 0;

  arr = JSON.parse(JSON.stringify(example));
  for (let a of arr) {
    let box = document.createElement("div");
    box.classList.add("box", `box_${a}`);
    box.innerText = a;
    // 運用 setProperty 塞入 css variable
    box.style.setProperty("--i", index);
    wrapper.appendChild(box);
    index++;
  }
}

// 氣泡排序
function bubbleSort() {
  sortStep = 0;
  for (let i = 0; i < arr.length - 2; i++) {
    for (let j = arr.length - 1; j >= i + 1; j--) {
      if (arr[j] < arr[j - 1]) {
        let moveForward = arr[j];
        let moveBackward = arr[j - 1];
        arr[j] = arr[j - 1];
        arr[j - 1] = moveForward;
        // 交換位置
        setTimeout(
          () => swapPosition(moveForward, moveBackward),
          (sortStep + 1) * 1200
        );
        sortStep++;
      }
    }
  }
}

// 抓出兩個要交換的 box 的 transform 位置
// forward 往前 -80px, backward 往後 +80px
function swapPosition(forward, backward) {
  const seperateNum = (str) => Number(str.match(/-*\d+/));

  let forwardEl = document.querySelector(`.box_${forward}`);
  let backwardEl = document.querySelector(`.box_${backward}`);
  forwardEl.style.transform = `translateX(${
    seperateNum(forwardEl.style.transform) - 80
  }px)`;
  backwardEl.style.transform = `translateX(${
    seperateNum(backwardEl.style.transform) + 80
  }px)`;
  swapStep++;
  rewriteStep();
}

// 清空所有 box 的 transform, 重置數列
function reset() {
  // 阻止排序中重置
  if (sortStep !== swapStep) return;

  let allBoxes = Array.from(document.querySelectorAll(".box"));
  allBoxes.forEach((a) => (a.style.transform = ""));
  arr = JSON.parse(JSON.stringify(example));
  swapStep = 0;
  rewriteStep();
}

function rewriteStep() {
  let stepEl = document.querySelector(".step");
  stepEl.innerText = swapStep;
}

createEl();

// WIP: 重複數字的問題,應該要判斷正確的 index 再 swap
// WIP: 也許可以用 animation delay 取代 setTimeout 動畫
// WIP: function 共用優化

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.