Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add onCharTyped and onCharDeleted callback #3

Merged
merged 1 commit into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ The following props allows you to configure the properties for the type animatio
| `direction` | no | String | front | Specifies the direction in which to perform the typing/deleting animation. It accepts two possible values: 'front' and 'back'. |
| `initialDelay` | no | Number | 0 | The delay before the animation begins (in milliseconds). |
| `loop` | no | Boolean | false | Whether to loop the typing animation indefinitely. |
| `onCharTyped` | no | Function | | Callback function triggered when a character is typed. |
| `onCharDeleted` | no | Function | | Callback function triggered when a character is deleted. |
| `preRenderText` | no | String | None | Specifies the initial text to display. |
| `repeat` | no | Number | 1 | The number of times to repeat the sequence. |
| `sequence` | yes | Array | | An array of objects defining the text to be typed and animation options. |
| `splitter` | no | Function | | A function to split text into individual characters or chunks for typing. |
| `style` | no | TextStyle | | Additional styles for the typewriter animation container. |
| `typeSpeed` | no | Number | 100 | The speed at which characters are typed. |
| `deletionSpeed` | no | Number | 100 | The speed at which characters are deleted. |

#### `sequence` Array

Expand All @@ -99,9 +103,9 @@ The `sequence` prop is an array of objects, where each object defines a part of

- `deleteCount`: The number of characters to delete before typing (backspacing).

- `deletionSpeed`: The speed at which characters are deleted (backspace speed, in milliseconds). The default is 100, but you can specify a custom value for individual sequences.
- `deletionSpeed`: The speed at which characters are deleted from this sequence (backspace speed, in milliseconds). The default is 100, but you can specify a custom value for individual sequences.

- `typeSpeed`: The speed at which characters are typed (typing speed, in milliseconds). The default is 100, but you can specify a custom value for individual sequences.
- `typeSpeed`: The speed at which characters are typed in this sequence(typing speed, in milliseconds). The default is 100, but you can specify a custom value for individual sequences.

#### `direction` String

Expand Down Expand Up @@ -135,6 +139,20 @@ The `cursorStyle` prop is used to apply additional styles to the cursor element.

The `cursor` prop determines whether the cursor is displayed during the animation. By default, it is set to `true`, but you can set it to `false` if you don't want to display the cursor.

#### `onCharTyped` Callback

This callback function is triggered each time a character is typed. It receives an object as an argument containing two properties:

- character: The character that was just typed.
- currentText: The current state of the text after the character has been typed.

#### `onCharDeleted` Callback

This callback function is triggered each time a character is deleted. It receives an object as an argument containing two properties:

- character: The character that was just deleted.
- currentText: The current state of the text after the character has been deleted.

**Note:** When using the `sequence` prop, you can define complex typing animations with different text, delays, and actions. Each object in the `sequence` array represents a step in the animation.

Example usage of `TypeAnimationProps`:
Expand Down
33 changes: 33 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,42 @@ export default function App() {
},
{ text: 'One' },
]}
onCharDeleted={({ character, currentText }) => {
console.log('char deleted', { character, currentText });
}}
onCharTyped={({ character, currentText }) => {
console.log('char typed', { character, currentText });
}}
loop
style={styles.text}
/>
<TypeAnimation
sequence={[
{ text: 'One' },
{
text: 'One Two',
},
{
action: () => {
console.log('Finished first two sequences');
},
},
{ text: 'One Two Three' },
{
text: 'One Two',
},
{ text: 'One' },
]}
loop
direction="back"
style={styles.text}
onCharDeleted={({ character, currentText }) => {
console.log('char deleted', { character, currentText });
}}
onCharTyped={({ character, currentText }) => {
console.log('char typed', { character, currentText });
}}
/>
</View>
);
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-type-animation",
"version": "0.3.0",
"version": "0.3.1",
"description": "React Native Type Animation is a component library that allows you to create engaging typewriter-style text animations in your React Native applications.",
"main": "lib/commonjs/index",
"module": "lib/module/index",
Expand Down
111 changes: 94 additions & 17 deletions src/TypeAnimation.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import React, {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import type { TextStyle } from 'react-native';
import { Animated, Text } from 'react-native';
import {
Expand Down Expand Up @@ -45,7 +51,7 @@ const Cursor: React.FC<CursorProps> = ({
Animated.delay(blinkSpeed),
])
).start();
}, []);
}, [blinkSpeed, opacity]);

return (
<Animated.Text style={{ opacity, ...style, ...cursorStyle }}>
Expand Down Expand Up @@ -80,12 +86,12 @@ type TypeAnimationProps = {
*/
deleteCount?: number;
/**
* The speed at which characters are deleted (backspace speed, in milliseconds). Default: 100
* The speed at which characters are deleted from this sequence (backspace speed, in milliseconds). Default: 100
*/
deletionSpeed?: number;

/**
* The speed at which characters are typed (typing speed, in milliseconds). Default: 100
* The speed at which characters are typed in this sequence (typing speed, in milliseconds). Default: 100
*/
typeSpeed?: number;
} & (
Expand Down Expand Up @@ -147,6 +153,23 @@ type TypeAnimationProps = {
* The delay before the animation begins (in milliseconds). Default: 0
*/
initialDelay?: number;
/**
* Callback function triggered when a character is typed.
*/
onCharTyped?: (data: { character: string; currentText: string }) => void;
/**
* Callback function triggered when a character is deleted.
*/
onCharDeleted?: (data: { character: string; currentText: string }) => void;
/**
* The speed at which characters are deleted (backspace speed, in milliseconds). Default: 100
*/
deletionSpeed?: number;

/**
* The speed at which characters are typed (typing speed, in milliseconds). Default: 100
*/
typeSpeed?: number;
};

/**
Expand All @@ -165,6 +188,10 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
direction = 'front',
preRenderText = '',
initialDelay = 0,
onCharTyped,
onCharDeleted,
typeSpeed = 100,
deletionSpeed = 100,
}) => {
const [text, setText] = useState<string>(preRenderText);
let currentText = preRenderText;
Expand All @@ -174,7 +201,7 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
* @param textToType - The text to type.
* @param speed - The typing speed.
*/
const typeLetters = (textToType: string, speed = 100) => {
const typeLetters = (textToType: string, speed = typeSpeed) => {
return new Promise<void>((resolve) => {
let i = 0;
const textArray = splitter(textToType);
Expand All @@ -184,11 +211,35 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
resolve();
} else {
if (direction === 'front') {
setText((currText) => `${currText}${textArray[i]}`);
setText((currText) => {
const character = textArray[i];
const word = `${currText}${character}`;
if (character) {
const data = {
character,
currentText: word,
};
if (onCharTyped) {
onCharTyped(data);
}
}
return word;
});
} else {
setText(
(currText) => `${textArray[textArray.length - i - 1]}${currText}`
);
setText((currText) => {
const character = textArray[textArray.length - i - 1];
const word = `${character}${currText}`;
if (character) {
const data = {
character,
currentText: word,
};
if (onCharTyped) {
onCharTyped(data);
}
}
return word;
});
}
}
i++;
Expand All @@ -201,7 +252,7 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
* @param count - The number of letters to delete.
* @param speed - The deletion speed.
*/
const deleteLetters = (count: number, speed = 100) => {
const deleteLetters = (count: number, speed = deletionSpeed) => {
return new Promise<void>((resolve) => {
let i = 0;
const interval = setInterval(() => {
Expand All @@ -210,11 +261,35 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
resolve();
} else {
if (direction === 'front') {
setText(
(currtext) => `${currtext.substring(0, currtext.length - 1)}`
);
setText((currtext) => {
const word = `${currtext.substring(0, currtext.length - 1)}`;
const character = currtext[currtext.length - 1];
if (character) {
const data = {
character,
currentText: word,
};
if (onCharDeleted) {
onCharDeleted(data);
}
}
return word;
});
} else {
setText((currtext) => `${currtext.substring(1, currtext.length)}`);
setText((currtext) => {
const word = `${currtext.substring(1, currtext.length)}`;
const character = currtext[0];
if (character) {
const data = {
character,
currentText: word,
};
if (onCharDeleted) {
onCharDeleted(data);
}
}
return word;
});
}
}
i++;
Expand Down Expand Up @@ -267,7 +342,7 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
}
};

const firstFunction = () => {
const firstFunction = useCallback(() => {
if (loop) {
const run = async () => {
await runSequence();
Expand All @@ -277,7 +352,9 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
} else {
repeatFunctionNTimes(runSequence, repeat);
}
};
/* eslint-disable react-hooks/exhaustive-deps */
}, []);

useEffect(() => {
if (initialDelay) {
setTimeout(() => {
Expand All @@ -286,7 +363,7 @@ const TypeAnimation: React.FC<TypeAnimationProps> = ({
} else {
firstFunction();
}
}, []);
}, [initialDelay, firstFunction]);

const cursorComponent = useMemo(() => {
return cursor ? (
Expand Down
Loading