DevGang
Авторизоваться

Как сделать адаптивную карточку с эмодзи? (TS React и Chakra UI)

Экспериментируя с градиентными границами, я наткнулся на интересную технику - карточки, которые адаптируются к содержимому внутри них. Демонстрацию этого эффекта в действии можно посмотреть здесь.

Вот что мы будем создавать сегодня:

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

Давайте пройдемся по шагам с самого начала и разложим всё по полочкам!

Инициализация проекта

Мы будем строить с нуля, ничего не упрощая. Мы будем использовать следующие технологические стеки:

  • Vite с React и TypeScript: Для быстрой настройки среды нашего проекта.
  • Chakra UI: Это не обязательно для эффекта, но реквизит стиля Chakra облегчает процесс стилизации.
  • Framer Motion: Это зависимость от Chakra UI, которая пригодится, если мы решим анимировать карту.

Настройте проект с помощью Vite: Начните с выполнения следующих действий:

npm create vite@latest

При появлении запроса выберите React with TypeScript.

Установите Chakra UI и Framer Motion:

npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion

Создайте тему Chakra: Сохраните в файле theme.ts следующее:

import { ThemeConfig, extendTheme } from '@chakra-ui/react';

const config: ThemeConfig = {
  initialColorMode: 'dark',
  useSystemColorMode: false,
};

const theme = extendTheme({
  config,
  styles: {
    global: () => ({
      body: {
        bg: '#121212',
      },
    }),
  },
});

export default theme;

При этом цветовая схема Chakra устанавливается в темный режим и применяется темно-серый фон.

Интегрируйте провайдер Chakra: Измените файл src/main.tsx, чтобы включить в него провайдер Chakra:

import { ChakraProvider } from '@chakra-ui/react';
import theme from './theme';

...

<React.StrictMode>
    <ChakraProvider theme={theme}>
        <App />
    </ChakraProvider>
</React.StrictMode>

Корректировки стиля: Чтобы страница занимала весь экран, установите значение min-height: 100vh; для элемента #root в файле App.css.

Создание базовой карточки

Для начала создадим базовый компонент карточки.

Создайте компонент: Создайте компонент src/components/EmojiCard.tsx:

import { Box, BoxProps } from '@chakra-ui/react';

interface Props {
  emoji: string;
}

export default function EmojiCard({ emoji, children }: Props) {
  return (
    /* main container */
    <Box position="relative" maxW={700} borderRadius={8} bg="#181818">
      /* content wrapper */
      <Box
        px={8}
        py={4}
        gap={{ base: 0, sm: 8 }}
        display="flex"
        alignItems="center"
        flexDirection={{ base: 'column', sm: 'row' }}
        borderRadius={6}
      >
        /* emoji container */
        <Box
          display="flex"
          alignItems="center"
          justifyContent="center"
          fontSize="80px"
          width="200px"
        >
          {emoji}
        </Box>
        /* text content container */
        <Box height="100%" textAlign={{ base: 'center', sm: 'left' }}>
          {children}
        </Box>
      </Box>
    </Box>
  );
}

Box - это компонент Chakra UI, который позволяет нам передавать реквизиты стиля напрямую.

В этой базовой конфигурации у нас есть окружающий основной контейнер с ограниченной шириной и фоном. Внутри также есть обертка содержимого, в которой есть место для наших эмодзи и текстового содержимого.

Этот код служит базовой структурой для нашей карточки.

Рендеринг компонента: Обновите App.tsx для рендеринга нашего нового компонента:

import EmojiCard from "./components/EmojiCard";

...

<EmojiCard emoji="🍬">
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Commodi explicabo doloremque, accusantium repellat dolorem natus soluta quos!
</EmojiCard>
Теперь нам есть с чем работать!
Теперь нам есть с чем работать!

Эффект

Когда начальная настройка выполнена, приступаем к самому интересному - созданию эффекта!

Фоновый эмодзи: Добавьте эмодзи в качестве фона в основной контейнер, но перед оберткой содержимого:

<Box
  position="absolute"
  left={0}
  top={0}
  width="100%"
  height="100%"
  justifyContent={'center'}
  alignItems={'center'}
  display="flex"
  _before={{
    content: `"${emoji}"`,
    fontSize: 80,
  }}
/>;
При этом дубликат эмодзи помещается в центр карточки. Обратите внимание на то, как он накладывается на содержимое, несмотря на то, что в коде компонент фона находится перед содержимым.
При этом дубликат эмодзи помещается в центр карточки. Обратите внимание на то, как он накладывается на содержимое, несмотря на то, что в коде компонент фона находится перед содержимым.

Поскольку фоновый элемент имеет позицию absolute, а наше содержимое по умолчанию имеет позицию static, контекст укладки CSS помещает фон поверх содержимого. Чтобы исправить это, установите для обертки содержимого значение position="relative".

Изменения в стиле: Увеличьте размер эмодзи, установив размер шрифта до 1100, и добавьте фильтр к элементу фона эмодзи: filter={"blur(80px)"}.

Это интересный градиентный эффект, но мы хотим ограничить его рамками нашей карточки. Добавьте в основной контейнер параметр overflow="hidden".

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

backgroundColor="#151515"
border="2px solid transparent"
backgroundClip="padding-box"

Интересным здесь является свойство backgroundClip. Оно определяет, насколько далеко простирается фон внутри элемента.

padding-box означает, что установленный нами backgroundColor будет распространяться до внешнего края подложки, но не будет выходить за ее границы.

Теперь эта карта должна реагировать на все передаваемые в нее emoji и использовать их для заполнения градиента фона:

Добавление полировки

Давайте еще больше усилим этот эффект!

Градиентный фон: Заменим установленное нами свойство backgroundColor на следующее:

backgroundImage="linear-gradient(rgb(20 20 20 / 0.8), rgb(20 20 20))"

В результате сплошной фон будет заменен светлым градиентом, слегка прозрачным в верхней части, чтобы часть цвета фона могла просвечивать:

Анимация фона: Мы можем сделать карточку более динамичной и привлекательной, анимировав фон эмодзи с помощью Framer Motion:

import { motion, useTime, useTransform } from 'framer-motion';

const AnimatedBox = motion(Box);

...

// inside our component
const time = useTime();
const rotate = useTransform(
  time,
  [0, 16000], // every 16 seconds...
  [0, 360], // ...rotate 360deg
  { clamp: false }, // repeat the animation
);

...

// convert our emoji background to a framer motion component
<AnimatedBox
  // pass in our special framer motion value
  style={{ rotate }}
    ...

Мы преобразуем компонент Box для фона эмодзи в AnimatedBox с помощью утилиты motion от Framer Motion. Это позволяет компоненту принимать специальные значения, которые Framer Motion анимирует для нас вне стандартного цикла рендеринга React.

Мы используем хуки useTime и useTransform из Framer Motion, чтобы рассчитать, насколько сильно должен вращаться эмодзи. Затем мы передаем значение rotate в качестве свойства стиля нашему компоненту Framer Motion для обработки анимации.

Это и есть конечный результат!

Молодцы, что дошли до этого! Окончательный вариант кода можно посмотреть на CodeSandbox, здесь, а оригинальная демонстрация эксперимента показывает его в действии с различными эмодзи.

Очень интересно натыкаться на подобные техники. Мы только поверхностно ознакомились с ними, а ведь существует множество различных направлений.

Представьте себе этот эффект с различными режимами освещения или в виде кнопки с пиктограммой, которая самостоятельно изменяет свой стиль в зависимости от предоставленной пиктограммы.

Не ограничивайтесь эмодзи: можно использовать любые изображения, узоры или цветной текст. Экспериментируйте с различными рамками. Поиграйте с размытием фильтра - уберите его или используйте другой.

Сообщите нам в комментариях, что у вас получилось!

Источник:

#JavaScript #CSS #React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу