Skip to content

Instantly share code, notes, and snippets.

@SuperOleg39
Created October 28, 2022 18:23
Show Gist options
  • Select an option

  • Save SuperOleg39/ab10e77cd76e1a37dede980558da05c4 to your computer and use it in GitHub Desktop.

Select an option

Save SuperOleg39/ab10e77cd76e1a37dede980558da05c4 to your computer and use it in GitHub Desktop.

Компонент image

Дата: 22.07.22 Тикет: ...

Спецификация

Референс - https://nextjs.org/docs/api-reference/next/image

Поведение компонента

  • Компонент рендерит тег img в span элементе, сохраняются пропорции по рецепту из https://css-tricks.com/aspect-ratio-boxes/#aa-the-core-concept-padding-in-percentages-is-based-on-width
  • Ссылки на изображение оборачиваются в imgproxy
  • Для статически импортируемого изображения автоматически вычисляются width и height
  • По умолчанию и для layout="fixed", генерируется src-set с обычным и x2 изображением
  • Для layout="responsive" и layout="fill" генерируется src-set на основе дефолтных deviceSizes Дефолтное значение свойства deviceSizes - [720, 828, 1366, 1920, 3840], основанное на популярных на tinkoff.ru разрешениях экрана
  • При передаче свойства deviceSizes, например [400, 800], генерируется src-set с размерами 400w, 800w. Ширина ограничена вариантами из списка ...
  • По умолчанию тег img имеет атрибут loading="lazy", убираем при наличии пропса priority="hight"
  • По умолчанию качество сжатия в imgproxy - 70, при наличии пропса quality="hight" меняем на 95
  • Для start и start-prod сборки и тестов не используется imgproxy

Про дефолтные deviceSizes, с учетом аналитики пользователей tinkoff.ru:

720 - покрывает самое популярное разрешение, 360px, x2 для retina дисплеев 828 - покрывает четыре следующих популярных разрешений, 414px самое большое из них, x2 для retina дисплеев 1366 - относительно популярное десктоп разрешение, покрывает менее популярное 1280 разрешение, оставляем как промежуточный вариант 1920 - самое популярное десктоп разрешение, покрывает менее популярные 1536 и 1440 разрешения 3840 - самое популярное десктоп разрешение, x2 для retina дисплеев

Свойства компонента

  • src - ссылка на изображение форматов .png, .jpeg или .webp. Может быть получена статическим импортом изображения, тогда не требуется width и height или aspectRatio.

  • width? - строка с шириной изображения (обязательно если не статический импорт?)

  • height? - строка с высотой изображения (обязательно если не статический импорт?)

  • aspectRatio? - строка, css свойство aspect-ratio для img тега

  • alt? - строка с описанием изображения

  • ref? - ссылка на тег img изображения

  • layout? - enum, абстракция над поведением изображения, как у Next.js

  • sizes? - html атрибут sizes, подсказывает браузеру, на каких размерах экрана какое пространство займет изоборажение. Для responsive и fill по дефолту 100vw, для прочих отсутствует.

  • deviceSizes? - для responsive макета, то что пойдет в srcSet?

  • quality? - low или high, качество сжатия, в imgproxy пресеты передается compressed и compressed95 соответственно

  • enlarge? - boolean, если true, запрещаем делать ширину изображения больше оригинали, в imgproxy пресеты передается enlarge_off

  • priority? - enum, ставим loading="lazy" для низкого приоритета. Потенциальный preload для высокого.

  • decoding? - enum, стандартный атрибут decoding для img тега

  • style? - строка, стандартный атрибут style для HTML тега

  • objectFit? - строка, css свойство object-fit для img тега

  • objectPosition? - строка, css свойство object-position для img тега

  • loader? - функция что бы переопределить логику получения урла изображения

  • placeholder? - @todo: абстракция для плейсхолдера с помощью background-image и base64 изображения ???

  • event handlers...

Макеты изображения

intrinsic (дефолт)

  • Изображение растягивается до своей максимальной ширины и высоты
  • При этом, изображение не превышает размер контейнера
  • srcset содержит размеры на 1x и 2x

fixed

  • Изображение имеет фиксированную ширину и высоту
  • srcset содержит размеры на 1x и 2x

responsive

  • Изображение растягивается до ширины контейнера
  • srcset содержит размеры из deviceSizes

fill

  • Изображение растягивается до ширины и высоты контейнера
  • Дефолтный object-fit - cover
  • srcset содержит размеры из deviceSizes

Use сases

  • Хотим просто сжать изображение на лету, и отдать его в современном формате по возможности

    Использование:

    import { Image } from '@tramvai/image';
    import testImg from './images/test.jpg';
    
    const Component = () => {
      return <Image src={testImg} />;
    };

    Результат:

    <img
      src="...retina_x2/url"
      srcset="
        .../url 1x,
        ...retina_x2/url 2x
      "
      style="display: block; max-width: 100%; height: auto"
    />
  • Хотим дополнительно указать, что на экранах шире 400px изображение занимает только половину экрана

    Использование:

    import { Image } from '@tramvai/image';
    import testImg from './images/test.jpg';
    
    const Component = () => {
      return <Image
        src={testImg}
        sizes="
          (max-width: 400px) 100vw,
          50vw
        "
      />;
    };

    Результат:

    <img
      src="...retina_x2/url"
      srcset="
        .../url 1x,
        ...retina_x2/url 2x
      "
      sizes="
        (max-width: 400px) 100vw
        50vw
      "
      style="display: block; max-width: 100%; height: auto"
    />
  • Хотим сделать адаптивное изображение, растягивающееся по всей ширине контейнера

    Использование:

    import { Image } from '@tramvai/image';
    import testImg from './images/test.jpg';
    
    const Component = () => {
      return <Image
        src={testImg}
        layout="responsive"
      />;
    };

    Результат:

    <img
      src="...1920_x2/url"
      srcset="
        ...360_x2/url 720w,
        ...414_x2/url 828w,
        ...1366/url 1366w,
        ...1920/url 1920w,
        ...1920_x2/url 3840w
      "
      sizes="100vw"
      style="display: block; width: 100%; height: auto"
    />
  • Хотим сделать изображение, заполняющее контейнер по ширине и высоте

    Использование:

    import { Image } from '@tramvai/image';
    import testImg from './images/test.jpg';
    
    const Component = () => {
      return <Image
        src={testImg}
        layout="fill"
      />;
    };

    Результат:

    <img
      src="...1920_x2/url"
      srcset="
        ...360_x2/url 720w,
        ...414_x2/url 828w,
        ...1366/url 1366w,
        ...1920/url 1920w,
        ...1920_x2/url 3840w
      "
      sizes="100vw"
      style="display: block; width: 100%; height: 100%; object-fit: cover"
    />
  • @todo: Хотим добавить плейсхолдер

Глобальная конфигурация

  • IMGPROXY_URL - путь до imgproxy
  • Требуется di провайдер
  • Требуется store провайдер?
  • Требуется media стор?

Side-effects

  • preconnect ссылка к IMGPROXY_URL

  • preload ссылка на изображение с высоким приоритетом?

  • "прогрев" ссылок на изображение в imgproxy?

  • Ширина и высота

    При статическом импорте изображения нам нужно знать ширину и высоту изображения. Также, неплохо бы сразу генерировать заблюренный плейсхолдер в base64. Самый простой способ сделать это - webpack loader типа next-image-loader. Но, нужна обратная совместимость, т.к. импорт вместо строки начнет возвращать объект. Два варианта решения:

    • Применять этот loader для конфигуриуемой папки, например src/assets/images
    • Применять этот loader для файлов с суффиксом типа .module, например image.module.png
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment