Skip to content

Commit 3f9d91c

Browse files
committed
implement chance param
1 parent 88e7c6a commit 3f9d91c

File tree

3 files changed

+47
-50
lines changed

3 files changed

+47
-50
lines changed

README.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,22 @@ export const App = () => {
4747

4848
## Props
4949

50-
| Property | type | default | range | description |
51-
| ---------------- | --------------- | -------- | ----- | ------------------------------------------------------------------------------------------ |
52-
| playOnMount | boolean | true | | Skip the animation on the first text input |
53-
| text | string | - | | Text value to scramble to |
54-
| speed | number | 1 | 0-1 | Animation framerate. 1 will redraw 60 times a second. 0 will pause the animation |
55-
| tick | number | 1 | 1-∞ | Frames per tick, combined with `speed`, you can fully control the pace rate |
56-
| step | number | 1 | 1-∞ | Moves the animation `step` characters forward, on every tick |
57-
| scramble | number | 1 | 0-∞ | How many times to randomize each character. A value of 0 will emulate a typewriter effect. |
58-
| seed | number | 1 | 0-∞ | Adds random characters ahead of the animation sequence |
59-
| range | number[] | [65,125] | | Unicode characters range to select random characters from |
60-
| overdrive | boolean, number | true | | Defaults to underscore (95) unicode character, or provide a custom unicode value |
61-
| overflow | boolean | true | | Set to false to always restart the animation from an empty string |
62-
| onAnimationStart | function | - | | callback invoked when the animation starts playing |
63-
| onAnimationFrame | function | - | | callback invoked on every rerender |
64-
| onAnimationEnd | function | - | | callback invoked on when the animation ends |
50+
| Property | type | default | range | description |
51+
| ---------------- | --------------- | -------- | ----- | ------------------------------------------------------------------------------------------------ |
52+
| playOnMount | boolean | true | | Skip the animation on the first text input |
53+
| text | string | - | | Text value to scramble to |
54+
| speed | number | 1 | 0-1 | Animation framerate. 1 will redraw 60 times a second. 0 will pause the animation |
55+
| tick | number | 1 | 1-∞ | Frames per tick, combined with `speed`, you can fully control the pace rate |
56+
| step | number | 1 | 1-∞ | Moves the animation `step` characters forward, on every tick |
57+
| scramble | number | 1 | 0-∞ | How many times to randomize each character. A value of 0 will emulate a typewriter effect. |
58+
| seed | number | 1 | 0-∞ | Adds random characters ahead of the animation sequence |
59+
| chance | number | 1 | 0-1 | Chance of scrambling a character, range from 0 to 1, 0 being no chance, and 1 being 100% chance. |
60+
| range | number[] | [65,125] | | Unicode characters range to select random characters from |
61+
| overdrive | boolean, number | true | | Defaults to underscore (95) unicode character, or provide a custom unicode value |
62+
| overflow | boolean | true | | Set to false to always restart the animation from an empty string |
63+
| onAnimationStart | function | - | | callback invoked when the animation starts playing |
64+
| onAnimationFrame | function | - | | callback invoked on every rerender |
65+
| onAnimationEnd | function | - | | callback invoked on when the animation ends |
6566

6667
## Return Values
6768

playground/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const App = () => {
2222
step: { value: 1, min: 1, max: 42, step: 1 },
2323
scramble: { value: 4, min: 0, max: 42, step: 1 },
2424
seed: { value: 0, min: 0, max: 10, step: 1 },
25+
chance: { value: 0.85, min: 0, max: 1, step: 0.01 },
2526
overflow: false,
2627
});
2728

src/index.ts

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ export type UseScrambleProps = {
4747
* @default 1
4848
*/
4949
step?: number;
50+
51+
/**
52+
* Chance of scrambling a character, range from 0 to 1, 0 being no chance, and 1 being 100% chance
53+
*/
54+
chance?: number;
5055
/**
5156
* Randomize `seed` characters at random text positions
5257
*
@@ -108,6 +113,7 @@ export const useScramble = (props: UseScrambleProps) => {
108113
step = 1,
109114
tick = 1,
110115
scramble = 1,
116+
chance = 0.5,
111117
overflow = true,
112118
range = [65, 125],
113119
overdrive = true,
@@ -116,20 +122,6 @@ export const useScramble = (props: UseScrambleProps) => {
116122
onAnimationEnd,
117123
} = props;
118124

119-
if (speed === 0) {
120-
console.error('speed 0 will stop the animation');
121-
}
122-
123-
if (step < 1) {
124-
step = 1;
125-
console.error('step must be at least 1. ');
126-
}
127-
128-
if (tick < 1) {
129-
tick = 1;
130-
console.error('tick must be at least 1');
131-
}
132-
133125
const prefersReducedMotion = window.matchMedia(
134126
'(prefers-reduced-motion: reduce)'
135127
).matches;
@@ -177,7 +169,11 @@ export const useScramble = (props: UseScrambleProps) => {
177169
typeof controlRef.current[index] !== 'undefined'
178170
) {
179171
controlRef.current[index] =
180-
controlRef.current[index] === ' ' ? ' ' : scramble || seed;
172+
controlRef.current[index] === ' '
173+
? ' '
174+
: getRandomInt(0, 10) > (1 - chance) * 10
175+
? scramble || seed
176+
: 0;
181177
}
182178
}
183179
};
@@ -188,14 +184,24 @@ export const useScramble = (props: UseScrambleProps) => {
188184
if (scrambleIndexRef.current < text.length) {
189185
const currentIndex = scrambleIndexRef.current;
190186

187+
const shouldScramble = getRandomInt(0, 10) > (1 - chance) * 10;
188+
191189
controlRef.current[currentIndex] =
192-
text[scrambleIndexRef.current] === ' ' ? ' ' : scramble;
193-
scrambleIndexRef.current += 1;
190+
text[scrambleIndexRef.current] === ' '
191+
? ' '
192+
: shouldScramble
193+
? scramble
194+
: 0;
195+
scrambleIndexRef.current++;
194196
}
195197
}
196198
};
197199

198-
const increaseControl = () => {
200+
const resizeControl = () => {
201+
if (text.length < controlRef.current.length) {
202+
controlRef.current.pop();
203+
controlRef.current.splice(text.length, step);
204+
}
199205
for (var i = 0; i < step; i++) {
200206
if (controlRef.current.length < text.length) {
201207
controlRef.current.push(
@@ -205,13 +211,6 @@ export const useScramble = (props: UseScrambleProps) => {
205211
}
206212
};
207213

208-
const decreaseControl = () => {
209-
if (text.length < controlRef.current.length) {
210-
controlRef.current.pop();
211-
controlRef.current.splice(text.length, step);
212-
}
213-
};
214-
215214
const onOverdrive = () => {
216215
if (!overdrive) return;
217216

@@ -224,15 +223,14 @@ export const useScramble = (props: UseScrambleProps) => {
224223
: String.fromCharCode(
225224
typeof overdrive === 'boolean' ? 95 : overdrive
226225
);
227-
overdriveRef.current += 1;
226+
overdriveRef.current++;
228227
}
229228
}
230229
};
231230

232231
const onTick = () => {
233232
stepForward();
234-
increaseControl();
235-
decreaseControl();
233+
resizeControl();
236234
seedForward();
237235
};
238236

@@ -244,14 +242,11 @@ export const useScramble = (props: UseScrambleProps) => {
244242
const animate = (time: number) => {
245243
if (!speed) return;
246244

247-
const timeElapsed = time - elapsedRef.current;
248-
249245
rafRef.current = requestAnimationFrame(animate);
250246

251-
if (overdrive) {
252-
onOverdrive();
253-
}
247+
onOverdrive();
254248

249+
const timeElapsed = time - elapsedRef.current;
255250
if (timeElapsed > fpsInterval) {
256251
elapsedRef.current = time;
257252

@@ -336,7 +331,7 @@ export const useScramble = (props: UseScrambleProps) => {
336331
cancelAnimationFrame(rafRef.current);
337332
}
338333

339-
stepRef.current += 1;
334+
stepRef.current++;
340335
};
341336

342337
/**
@@ -387,7 +382,7 @@ export const useScramble = (props: UseScrambleProps) => {
387382
useEffect(() => {
388383
cancelAnimationFrame(rafRef.current);
389384

390-
if (speed > 0) rafRef.current = requestAnimationFrame(animate);
385+
rafRef.current = requestAnimationFrame(animate);
391386

392387
// cancel raf on unmount
393388
return () => {

0 commit comments

Comments
 (0)