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

Это описание:

Создайте сетку 10x10. Всякий раз, когда вы нажимаете на ячейку в этой сетке, 1 добавляется ко всем ячейкам в той же строке и столбце.

Если ячейка была пустой, установите ее на 1. Каждое изменение в ячейке должно вызывать кратковременную подсветку ячейки желтым цветом. Если по крайней мере 5 последовательных чисел в ячейках образуют часть последовательности Фибоначчи, кратковременно ячейки зеленым цветом, а затем очистите эти ячейки.

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

Для этого проекта я использовал React js, Typescript и tailwind CSS.

Итак, сначала я начал создавать функцию, которая может генерировать матрицу в двух измерениях со значением по умолчанию:

const matrix = (
  rows: number,
  cols: number,
  defaultValue: number | null
): number[][] => {
  return Array.from({ length: rows }).fill(
    Array.from({ length: cols }).fill(defaultValue)
  ) as Array<Array<number>>
}

Затем я создал таблицу на главной странице:

interface AppProps {
  defaultSize?: number
}

const App = ({ defaultSize = 10 }: AppProps): JSX.Element => {
  const [mainTable, setMainTable] = useState<number[][]>(
    matrix(defaultSize, defaultSize, 0)
  )
  return(
      <table
          className="text-white border">
          <tbody>
            {mainTable.map((row, i: number) => (
              <tr key={i} className="p-3">
                {row.map((_cell, j: number) => {
                  return (
                    <td
                      className="p-10 border cursor-pointer"
                      key={i * defaultSize + j}
                      id={`cell-${i}-${j}`}
                    >
                      {mainTable[i][j]}
                    </td>
                  )
                })}
              </tr>
            ))}
          </tbody>
        </table>
  )
}
export default App

Затем я создаю функцию для увеличения значений ячеек в строке и столбце:

const incrementCells = (
  arr: number[][],
  row: number,
  col: number
): number[][] => {
  return arr.map((row2, i: number) =>
    i === row
      ? row2.map((col2) => +col2 + 1)
      : row2.map((col2, j: number) => (j === col ? +col2 + 1 : +col2))
  )
}

Затем я добавил в таблицу функцию onClick:

 <table
          className="text-white border"
          onClick={(e) => {
            const target = e.target as HTMLElement
            if (target.id.includes("cell")) {
              const [row, col] = target.id.split("-").slice(1)

              clickCell(+row, +col)
            }
          }}
>

Я мог бы использовать onClick для каждой ячейки (тег td), но это не слишком хорошо из-за производительности.

Теперь нам нужно определить функцию щелчка по ячейке:

 const clickCell = (row: number, col: number) => {
    setMainTable((prev) => colorize(incrementCells(prev, row, col), row, col))
 }

И функция colorize, я пишу стили после функций:

const changeBgColor = (row: number, col: number, color: boolean): void => {
  const cell = document.getElementById(`cell-${row}-${col}`)

  if (cell && !color) {
    const prevClasses = cell.className
    if (!prevClasses.includes("fade-colors")) {
      cell.className = prevClasses + " fade-colors-yellow"
    }
  } else if (cell && color) {
    let prevClasses = cell.className
    prevClasses = prevClasses.replace("fade-colors-yellow", "")

    if (!prevClasses.includes("fade-colors-yellow")) {
      cell.className = prevClasses + " fade-colors-green"
    }
  }
}

const colorize = (arr: number[][], row: number, col: number): number[][] => {
  changeBgColor(row, col, false)

  return arr
}

Я написал функцию для проверки паттерна Фибоначчи.

Он получает каждую строку, проверяет все ячейки и вычисляет, соответствуют ли они шаблону или нет, тогда он возвращает логическое значение, по которому мы можем понять, является ли это последовательностью Фибоначчи или нет:

const checkFib = ({
  col_start,
  col_end,
  row,
  table,
}: {
  col_start: number
  col_end: number
  row: number
  table: number[][]
}): boolean => {
  const fibSeq = table[row].slice(col_start - 4, 1 + col_start)

  if (
    col_start < 0 ||
    col_end < 0 ||
    !(fibSeq[0] && fibSeq[1] && fibSeq[2] && fibSeq[3] && fibSeq[4])
  ) {
    return false
  }

  if (fibSeq[0] === fibSeq[1] && fibSeq[0] !== 1) {
    return false
  }

  return (
    fibSeq[4] - fibSeq[3] === fibSeq[2] &&
    fibSeq[3] - fibSeq[2] === fibSeq[1] &&
    fibSeq[2] - fibSeq[1] === fibSeq[0]
  )
}

Теперь пришло время проверить все ячейки и найти шаблон.
Сначала я определил переменную cells для сохранения данных ячейки, таких как номер строки, номер начального столбца и номер последнего столбца.

Затем я использовал метод карты для массива mainTable, чтобы проверить все ячейки и значения в таблице, и использовал функцию checkFib, чтобы проверить все ячейки и строки.

  useEffect(() => {
    const cells: {
      row: number
      col_start: number
      col_end: number
      setMainTable: React.Dispatch<React.SetStateAction<number[][]>>
    }[] = []

    const fibCells = mainTable.map((row, i) =>
      row.map((col, j) =>
        checkFib({ row: i, col_start: j, col_end: j - 4, table: mainTable })
      )
    )

    fibCells.forEach((row, i) => {
      row.forEach((col, j) => {
        if (col) {
          console.log("here : ", true)
          for (let k = 4; k >= 0; k--) {
            changeBgColor(i, j - k, true)
          }
          cells.push({
            row: i,
            col_start: j,
            col_end: j - 4,
            setMainTable,
          })
        }
      })
    })
    setTimeout(() => cells.forEach(removeCellValues), 2000)
  }, [mainTable])

Этот раздел кода перебирает двумерный массив fibCells с использованием метода forEach. Внешний цикл forEach перебирает строки массива, а внутренний цикл forEach перебирает столбцы массива.

Для каждого правдивого элемента в массиве fibCells вызывается функция changeBgColor с аргументами i, j - k и true. Это, вероятно, используется для изменения цвета фона ячеек в таблице, которые являются частью последовательности Фибоначчи.

Кроме того, в массив cells добавляется объект со свойствами row, col_start, col_end и setMainTable. Эти свойства будут использоваться позже в коде, возможно, для удаления ячеек из таблицы через определенное время (здесь 2 секунды).

И для удаления значений ячеек я написал эту функцию, она получает данные ячейки и удаляет значение ячейки и все стили этого.

const removeCellValues = ({
  col_start,
  col_end,
  row,
  setMainTable,
}: {
  col_start: number
  col_end: number
  row: number
  setMainTable: React.Dispatch<React.SetStateAction<number[][]>>
}): void => {
  for (let i = col_start; i >= col_end; i--) {
    setMainTable((prev) => {
      const newTable = [...prev]
      newTable[row][i] = 0
      return newTable
    })
  }

  const cells = document.querySelectorAll("td") as NodeListOf<HTMLElement>

  cells.forEach((cell) => {
    let prevClasses = cell.className
    prevClasses = prevClasses.replace("fade-colors-green", "")

    cell.className = prevClasses
  })
}

Пришло время стилей, я использовал эти стили:

.fade-colors-yellow {
  animation: fadeinYellow 1s linear both;
}

@keyframes fadeinYellow {
  from {
    opacity: 0;
    background: transparent;
    color: white;
  }
  to {
    opacity: 1;
    background: yellow;
    color: black;
  }
}

.fade-colors-green {
  animation: fadeinGreen 1s linear both;
}

@keyframes fadeinGreen {
  from {
    opacity: 1;
    background: yellow;
    color: black;
  }
  to {
    opacity: 1;
    background: green;
    color: white;
  }
}

table {
  border-spacing: 10px;
  border-collapse: separate;
  border-color: red;
  border-width: 12px;
}

td,
th,
tbody,
tr {
  border: 1px solid rgb(49, 124, 189);
}

td {
  width: fit-content;
}

.main {
  width: 100%;
  height: 100%;
  background: black;
  color: white;
}

Вы можете проверить готовый код в этом репозитории. Не забудьте поставить звездочку :)

Давайте подключимся

Спасибо, что прочитали мою статью. Если вам нравится мой контент, рассмотрите возможность угостить меня кофе ☕.