Компоненты/Живое поле ввода

Живое поле ввода

31 января 2025

Поле ввода с плейсхолдерами, которые сменяют друг друга по очереди

Установка

npx astra-rn add animated-input-bar
pnpm dlx astra-rn add animated-input-bar
yarn dlx astra-rn add animated-input-bar
bunx --bun astra-rn add animated-input-bar

Файлы

index.tsx
НазадРастущая шапкаДалееТекст под маской

Вручную

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

npm install expo-blur react-native-reanimated
pnpm add expo-blur react-native-reanimated
yarn add expo-blur react-native-reanimated
bun add expo-blur react-native-reanimated

Скопируйте эти файлы в src/shared/components/base/animated-input-bar/:

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

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

import { View, StyleSheet, Dimensions, TextInput } from "react-native";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { StatusBar } from "expo-status-bar";
import { useFonts } from "expo-font";
import { Ionicons } from "@expo/vector-icons";
import { useMemo, useRef, useState } from "react";
import AnimatedInputBar from "@/shared/components/base/animated-input-bar";
import { Showcase, useAutoplay, useCustomize } from "~/showcase";

const PLACEHOLDERS: string[] = [
  "Share your thoughts...",
  "What's on your mind?",
  "Type something interesting...",
  "Express yourself here...",
];

const CUSTOMIZE = [
  { key: "placeholder1", label: "Подсказка 1", type: "text", default: PLACEHOLDERS[0], maxLength: 40 },
  { key: "placeholder2", label: "Подсказка 2", type: "text", default: PLACEHOLDERS[1], maxLength: 40 },
  { key: "placeholder3", label: "Подсказка 3", type: "text", default: PLACEHOLDERS[2], maxLength: 40 },
  { key: "placeholder4", label: "Подсказка 4", type: "text", default: PLACEHOLDERS[3], maxLength: 40 },
  { key: "interval", label: "Смена подсказки", type: "slider", min: 600, max: 5000, step: 100, unit: "ms", default: 1200 },
  { key: "charDelay", label: "Задержка букв", type: "slider", min: 0, max: 60, step: 1, unit: "ms", default: 12 },
  { key: "barColor", label: "Фон поля", type: "color", default: "#111111" },
  { key: "iconColor", label: "Цвет иконки", type: "color", default: "#ffffff" },
  { key: "dividerColor", label: "Разделитель", type: "color", default: "#6e6e6e" },
  { key: "radius", label: "Скругление", type: "slider", min: 0, max: 96, step: 1, unit: "px", default: 96 },
] as const;

export default function App() {
  const [fontLoaded] = useFonts({
    SfProRounded: require("@/assets/fonts/sf-pro-rounded.ttf"),
  });

  const [text, setText] = useState<string>("");
  const custom = useCustomize(CUSTOMIZE);
  const { placeholder1, placeholder2, placeholder3, placeholder4 } = custom;
  const placeholders = useMemo(
    () => [placeholder1, placeholder2, placeholder3, placeholder4],
    [placeholder1, placeholder2, placeholder3, placeholder4],
  );
  const inputRef = useRef<TextInput>(null);

  useAutoplay(async ({ wait }) => {
    if (inputRef.current?.isFocused()) inputRef.current.blur();
    setText("");
    await wait(3200);
    inputRef.current?.focus();
    await wait(1100);
    setText("H");
    await wait(160);
    setText("Hi");
    await wait(2000);
  });

  return (
    <Showcase>
      <View style={styles.container}>
        <StatusBar style="light" />
        <View
          style={[
            styles.inputContainer,
            { backgroundColor: custom.barColor, borderRadius: custom.radius },
          ]}
        >
          <View style={styles.iconSlot}>
            <Ionicons name="add" size={18} color={custom.iconColor} />
          </View>
          <View
            style={[styles.divider, { backgroundColor: custom.dividerColor }]}
          />
          <AnimatedInputBar
            ref={inputRef}
            placeholders={placeholders}
            value={text}
            characterDelayIncrement={custom.charDelay}
            animationInterval={custom.interval}
            onChangeText={setText}
            containerStyle={styles.field}
            selectionColor={"#353535"}
            placeholderStyle={{
              fontFamily: fontLoaded ? "SfProRounded" : undefined,
            }}
          />
        </View>
      </View>
    </Showcase>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#0a0a0a",
    alignItems: "center",
    justifyContent: "flex-end",
    paddingBottom: 56,
  },
  inputContainer: {
    width: Dimensions.get("window").width - 50,
    flexDirection: "row",
    alignItems: "center",
    backgroundColor: "#111",
    paddingHorizontal: 16,
    borderRadius: 96,
    height: 46,
  },
  iconSlot: {
    marginLeft: 10,
    width: 18,
    alignItems: "center",
  },
  field: {
    flex: 1,
    minWidth: 0,
  },
  divider: {
    width: 0.4,
    backgroundColor: "#6e6e6e",
    height: 18,
    marginLeft: 18,
  },
  btn: {
    flexDirection: "row",
    alignItems: "center",
    gap: 10,
    backgroundColor: "#fff",
    paddingVertical: 16,
    paddingHorizontal: 32,
    borderRadius: 16,
  },
  btnText: {
    fontSize: 17,
    fontWeight: "600",
    color: "#000",
  },
});

Пропсы

Пропс
Описание
placeholders

Тип string[]

ref?

Тип Ref<TextInput>

animationInterval?

Тип number

containerStyle?

Тип StyleProp<ViewStyle>

inputWrapperStyle?

Тип StyleProp<ViewStyle>

inputStyle?

Тип StyleProp<TextStyle>

placeholderStyle?

Тип StyleProp<TextStyle>

characterEnterDuration?

Тип number

characterExitDuration?

Тип number

characterDelayIncrement?

Тип number

blurAnimationDuration?

Тип number

blurIntensityRange?

Тип [number, number, number]

blurProgressRange?

Тип [number, number, number]

ICharacter

Пропс
Описание
char

Тип string

index

Тип number

enterDuration

Тип number

exitDuration

Тип number

delayIncrement

Тип number

style?

Тип TextStyle