Как сделать адаптивную карточку с эмодзи? (TS React и Chakra UI)
![](/static/storage/147899479336195847850164294558994114556.png)
Экспериментируя с градиентными границами, я наткнулся на интересную технику - карточки, которые адаптируются к содержимому внутри них. Демонстрацию этого эффекта в действии можно посмотреть здесь.
Вот что мы будем создавать сегодня:
![Эта карточка будет волшебным образом подстраивать свои цвета под переданные ей эмодзи, но аналогичный подход можно использовать для любого вида контента.](/static/storage/112529661937332751019621457068085928541.png)
Давайте пройдемся по шагам с самого начала и разложим всё по полочкам!
Инициализация проекта
Мы будем строить с нуля, ничего не упрощая. Мы будем использовать следующие технологические стеки:
- 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>
![Теперь нам есть с чем работать!](/static/storage/244886206032229512216033232107176947582.png)
Эффект
Когда начальная настройка выполнена, приступаем к самому интересному - созданию эффекта!
Фоновый эмодзи: Добавьте эмодзи в качестве фона в основной контейнер, но перед оберткой содержимого:
<Box
position="absolute"
left={0}
top={0}
width="100%"
height="100%"
justifyContent={'center'}
alignItems={'center'}
display="flex"
_before={{
content: `"${emoji}"`,
fontSize: 80,
}}
/>;
![При этом дубликат эмодзи помещается в центр карточки. Обратите внимание на то, как он накладывается на содержимое, несмотря на то, что в коде компонент фона находится перед содержимым.](/static/storage/71599153678786443665326351028033941429.png)
Поскольку фоновый элемент имеет позицию absolute
, а наше содержимое по умолчанию имеет позицию static
, контекст укладки CSS помещает фон поверх содержимого. Чтобы исправить это, установите для обертки содержимого значение position="relative"
.
![](/static/storage/234326217613609476139141901320462960841.png)
Изменения в стиле: Увеличьте размер эмодзи, установив размер шрифта до 1100, и добавьте фильтр к элементу фона эмодзи: filter={"blur(80px)"}
.
![](/static/storage/256623380595915019744082506247174867279.png)
Это интересный градиентный эффект, но мы хотим ограничить его рамками нашей карточки. Добавьте в основной контейнер параметр overflow="hidden"
.
![](/static/storage/35786429091295070524825958123353456461.png)
Теперь добавим к обертке содержимого следующее, чтобы вырезать место для содержимого в середине и получить тонкую границу с помощью этих градиентных цветов:
backgroundColor="#151515"
border="2px solid transparent"
backgroundClip="padding-box"
Интересным здесь является свойство backgroundClip
. Оно определяет, насколько далеко простирается фон внутри элемента.
padding-box
означает, что установленный нами backgroundColor
будет распространяться до внешнего края подложки, но не будет выходить за ее границы.
![](/static/storage/55902369986676086460742289549229567847.png)
Теперь эта карта должна реагировать на все передаваемые в нее emoji и использовать их для заполнения градиента фона:
![](/static/storage/146462488849254569768555449326312026551.png)
Добавление полировки
Давайте еще больше усилим этот эффект!
Градиентный фон: Заменим установленное нами свойство backgroundColor на следующее:
backgroundImage="linear-gradient(rgb(20 20 20 / 0.8), rgb(20 20 20))"
В результате сплошной фон будет заменен светлым градиентом, слегка прозрачным в верхней части, чтобы часть цвета фона могла просвечивать:
![](/static/storage/291032324888785328150698326555421332466.png)
Анимация фона: Мы можем сделать карточку более динамичной и привлекательной, анимировав фон эмодзи с помощью 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 для обработки анимации.
![](/static/storage/167755821121313717372822242645911217415.png)
Это и есть конечный результат!
Молодцы, что дошли до этого! Окончательный вариант кода можно посмотреть на CodeSandbox, здесь, а оригинальная демонстрация эксперимента показывает его в действии с различными эмодзи.
Очень интересно натыкаться на подобные техники. Мы только поверхностно ознакомились с ними, а ведь существует множество различных направлений.
Представьте себе этот эффект с различными режимами освещения или в виде кнопки с пиктограммой, которая самостоятельно изменяет свой стиль в зависимости от предоставленной пиктограммы.
Не ограничивайтесь эмодзи: можно использовать любые изображения, узоры или цветной текст. Экспериментируйте с различными рамками. Поиграйте с размытием фильтра - уберите его или используйте другой.
Сообщите нам в комментариях, что у вас получилось!