Покемоны в виде HTML-списка с использованием функций SASS

ℹ Это болтливый пост, описывающий мой пошаговый процесс разработки. Вы можете перейти к конечному результату здесь.

В начале этого года один из еженедельных вызовов CodePen был посвящен стилям списков. Если вы еще этого не сделали, я настоятельно рекомендую подписаться на еженедельные вызовы Codepen. Они отлично вдохновляют на небольшие упражнения по разработке фронтенда.

Вернемся к стилям списков — я сразу почувствовал вдохновение написать что-то легкомысленное и забавное. Я решил заменить традиционные числа упорядоченного списка изображениями, которые эзотерически представляют числа. В частности, я решил использовать покемонов в порядке покедекса.

Да, я фанат покемонов, но и многие разработчики в сообществе тоже. Есть много замечательных ресурсов по покемонам для побочных проектов разработчиков, таких как PokéAPI. Для этого небольшого проекта я выбрал изображения спрайтов PokéSprite в качестве пули упорядоченного списка.

Как я его создал

Шаг 1: Инкрементирование чисел в правилах CSS

Я начал абстрактно думать о том, что мне нужно сделать в CSS. Замена одного маркера номера списка изображением потребовала бы следующего CSS:

ol li::marker {
  content: url("image_url_here");
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Для того чтобы для каждого элемента списка было свое изображение, мне потребовалось отдельное правило для каждого дочернего элемента с использованием псевдокласса CSS nth-of-type.

ol li:nth-of-type(1)::marker {
  content: url("image_001");
}

ol li:nth-of-type(2)::marker {
  content: url("image_002");
}

ol li:nth-of-type(3)::marker {
  content: url("image_003");
}

/* etc etc */
Вход в полноэкранный режим Выход из полноэкранного режима

Копировать и вставлять это правило для каждого элемента списка быстро стало бы утомительно. Сейчас существует почти 1 000 покемонов. Ни в коем случае: Мне нужно было программное решение.

На данном этапе я хотел оформить это с помощью ванильного CSS, если это возможно, поэтому я начал играть с CSS-функцией counter(), чтобы программно генерировать числа. Что-то вроде этого:

counter-increment: idx;

ol li:nth-of-type(idx)::marker {
  content: url("image_{idx}"); /* This is invalid 😡 */
}
Вход в полноэкранный режим Выход из полноэкранного режима

Но после нескольких попыток и яростного гугления на тему того, как вставить значение счетчика в URL изображения, я нашел вот такой вопрос (без нужды не проголосованный) на Stack Overflow:

Могу ли я вставить счетчик css в url контента?

Ответ — нет. Облом. 😞

С другой стороны, ответ на этот вопрос Stack Overflow дал решение: SASS @for loop.

С его помощью я подготовил CSS-проводку для URL-адресов изображений покемонов:

ol li {
  @for $i from 1 through 908 {
    &:nth-of-type(#{$i})::marker {
      content: url("#{$i}.png");
    }
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Шаг 2: Подготовка файлов изображений

Теперь не хватало только самих файлов изображений покемонов. Я загрузил эту коллекцию спрайтов с сайта PokéSprites и быстро заметил, что сделал неверное предположение: файлы были названы по имени покемона, а не по его номеру dex, например, bulbasaur.png.

Но не стоит беспокоиться, PokéSprite также поставляется со структурированным файлом данных JSON, через который я мог сопоставить имя файла с номером dex. Я начал писать скрипт, который мог бы переименовывать сотни файлов за меня. Я не специалист по Node, поэтому после долгого гугления на тему Node FS я пришел к следующему:

// extractOrderedListOfPokemon.mjs
import * as input from './pokemon.json';
import * as fs from 'fs';

const dirPath = './by-nat-dex-number';
const data = input.default;
const filesToDelete = [];

const errCallback = err => {
  if (err) {
    console.error('ERROR: ' + err);
  }
}

const dir = await fs.promises.opendir(dirPath);
for await (const dirent of dir) {
  const filename = dirent.name;
  const pokemonSlug = filename.replace(".png", "");
  const dexNumber = Object.values(data).find(pokemon => pokemon.slug.eng === pokemonSlug)?.idx;

  if (dexNumber) {
    fs.rename(`${dirPath}/${filename}`, `${dirPath}/${dexNumber}.png`, errCallback);
  } else {
    filesToDelete.push(`${dirPath}/${filename}`);
  }
}

filesToDelete.forEach(file => {
  fs.unlink(file, errCallback);
});
Войти в полноэкранный режим Выход из полноэкранного режима

Я запустил это как сценарий Node в терминале. Узнав, что мне нужна опция --experimental-json-modules для работы импорта .json, я получил нужные мне файлы с именами 001.png, 002.png и т.д., вплоть до 908.png. Я загрузил их все в ведро AWS S3 для хостинга.

Шаг 3: Функция SASS zerofill

Все, что мне осталось сделать, это вставить URL-адреса изображений в код следующим образом:

ol li {
  @for $i from 1 through 908 {
    &:nth-of-type(#{$i})::marker {
      content: url("https://my-s3-bucket-domain.com/#{$i}.png");
    }
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

за исключением:

Упс, мои URL-адреса изображений заполнены нулями, или нулями в начале, чтобы каждое имя файла имело одинаковую длину символов. Мой код CSS ищет 1.png, когда ему нужен 001.png.

И снова решение нашлось на Stack Overflow, и я адаптировал свой код:

@function zerofill($i) {
  @return #{str-slice("000", 0, 3 - str-length(#{$i}))}#{$i};
}

ol li {
  @for $i from 1 through 908 {
    &:nth-of-type(#{$i})::marker {
      $i: zerofill($i);
      content: url("https://my-s3-bucket-domain.com/#{$i}.png");
    }
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Результат

И та-да, 🎉 мой упорядоченный список покемонов!

⚠ Если вы пользователь Safari или iOS, к сожалению, вы не увидите пули покемонов. На момент написания статьи Safari имеет ограниченную поддержку псевдоэлемента ::marker. Более подробную информацию вы можете найти в билете об ошибке.

Оцените статью
devanswers.ru
Добавить комментарий