Компоненты/Дизеринг

Дизеринг

27 сентября 2026

Упорядоченный дизеринг Байера для градиента или фотографии

Установка

npx astra-rn add dither
pnpm dlx astra-rn add dither
yarn dlx astra-rn add dither
bunx --bun astra-rn add dither

Файлы

index.tsx
НазадКиматикаДалееУгольки

Картинка переводится в ограниченную палитру через пороговую матрицу Байера 2×2, 4×4 или 8×8. Без image дизерится встроенная анимированная сцена со сферой; с image — ваша фотография, крупными пикселями.

Шейдер считается целиком на GPU через Skia, время идёт на UI-потоке Reanimated — React не перерисовывается на каждом кадре. Анимация замирает, когда включено «Уменьшение движения», приложение свёрнуто, передан paused или (в вебе) поверхность ушла за пределы экрана. Работает на iOS, Android и в вебе.

Вручную

Поставьте зависимости:

npm install @shopify/react-native-skia react-native-reanimated
pnpm add @shopify/react-native-skia react-native-reanimated
yarn add @shopify/react-native-skia react-native-reanimated
bun add @shopify/react-native-skia react-native-reanimated

Скопируйте эти файлы в src/shared/components/organisms/dither/:

Проверьте алиас путей. Импорты в коде выше записаны так, как их пишет astra-rn add: @/shared/components, @/shared/utils и @/shared/hooks. Нужен алиас "@/*": ["./src/*"] в tsconfig.json — или поправьте импорты под свои пути.

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

import React from "react";
import { StyleSheet, View } from "react-native";
import { Dither } from "@/shared/components/organisms/dither";
import { Showcase, useCustomize } from "~/showcase";
import { PHOTO_SOURCES } from "~/assets/photos";

const CUSTOMIZE = [
  { key: "darkColor", label: "Тёмный", type: "color", default: "#0a0a0a" },
  { key: "lightColor", label: "Светлый", type: "color", default: "#f4f1ea" },
  { key: "pixelSize", label: "Пиксель", type: "slider", min: 1, max: 10, step: 1, default: 3, unit: "px" },
  { key: "levels", label: "Уровни", type: "slider", min: 2, max: 6, step: 1, default: 2 },
  { key: "matrix", label: "Матрица", type: "select", options: [{value: "2", label: "2×2"},{value: "4", label: "4×4"},{value: "8", label: "8×8"}], default: "8" },
  { key: "contrast", label: "Контраст", type: "slider", min: 0.5, max: 2.5, step: 0.05, default: 1.2 },
  { key: "colorMode", label: "Цвет", type: "select", options: [{value: "palette", label: "Палитра"},{value: "source", label: "Исходный"}], default: "palette" },
  { key: "usePhoto", label: "Фото вместо градиента", type: "toggle", default: false },
  { key: "speed", label: "Скорость", type: "slider", min: 0, max: 3, step: 0.05, default: 1 },
] as const;

const PHOTO = PHOTO_SOURCES["photo-stack-1"];

export default function DitherScreen() {
  const custom = useCustomize(CUSTOMIZE);

  return (
    <Showcase>
      <View style={styles.container}>
        <Dither
          fill
          darkColor={custom.darkColor}
          lightColor={custom.lightColor}
          pixelSize={custom.pixelSize}
          levels={custom.levels}
          contrast={custom.contrast}
          speed={custom.speed}
          matrix={Number(custom.matrix) as 2 | 4 | 8}
          colorMode={custom.colorMode === "source" ? "source" : "palette"}
          image={custom.usePhoto ? PHOTO : undefined}
        />
      </View>
    </Showcase>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#0a0a0a",
  },
});

По умолчанию компонент занимает ширину родителя и высоту height. Чтобы растянуть его фоном на весь экран, передайте fill.

Пропсы

Пропс
Описание
image?

Тип DataSourceParam

darkColor?

Тип string

lightColor?

Тип string

pixelSize?

Тип number

levels?

Тип number

matrix?

Тип DitherMatrix

contrast?

Тип number

colorMode?

Тип DitherColorMode

speed?

Тип number

paused?

Тип boolean

fill?

Тип boolean

width?

Тип number

height?

Тип number

borderRadius?

Тип number

children?

Тип ReactNode

style?

Тип StyleProp<ViewStyle>