react-typist
react-typist copied to clipboard
Add option to restart the animation
Is it possible to put the typist on a loop? (ie restart after done)
+1
Maybe a bit ugly, but this will do the trick:
class Foo extends Component {
state = {
typing: true,
}
done = () => {
this.setState({ typing: false }, () => {
this.setState({ typing: true })
});
}
render() {
{this.state.typing
? <Typist onTypingDone={this.done}>blablabla</Typist>
: ''
}
}
}
That works like a charm, thanks!
I changed it to this Component for reusability :)
class RestartingTypist extends Component {
state = {typing: true}
done = () => {
this.setState({ typing: false }, () => {
setTimeout(() => this.setState({ typing: true }), this.props.timeout || 1200);
});
}
render() {
const {children, timeout, ...props} = this.props;
return this.state.typing ?
<Typist {...props} onTypingDone={this.done}>{children}</Typist>
: <span>{children}</span>;
}
}
The difference between : ''
and : <span>{children}</span>
is that with the first, the text disappears between Typists while the second will keep it displayed. Which one you want probably depends on your taste/requirements :)
hi all! Glad to see it this was solved by composing Typist. I'll look into adding an option to do this automatically. Thanks!
The only small warning with this approach, is if we have:
const cursor = {
hideWhenDone: true,
blink: false,
hideWhenDoneDelay: 800,
};
Then we'll get the warning..
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the Cursor component.
And I think that is because there is a setTimeout being called in the Cursor component here.
So when the onTypingDone function is called, which removes the Typist component, the Cursor component is no longer mounted to update.
The solution would be to add another setTimeout
in the onTypingDone
function for the same duration of hideWhenDoneDelay
?
@magician11 I've encountered a similar problem. It was because setState
was called after the component was unmounted. To handle this, I used clearTimeout
function.
Something like:
class RestartingTypist extends Component {
state = { typing: true }
done = () => {
this.setState({ typing: false }, () => {
this.timeoutes.push(
setTimeout(() => this.setState({ typing: true }), this.props.timeout || 1200);
)
});
}
componentWillMount() {
this.timeouts = [];
}
componentWillUnmount() {
this.timeouts.forEach(window.clearTimeout);
}
render() {
const {children, timeout, ...props} = this.props;
return this.state.typing ?
<Typist {...props} onTypingDone={this.done}>{children}</Typist>
: <span>{children}</span>;
}
}
we might add an option for restart
soon!
Seems like there is still interest in adding a restart
feature. Anyone currently working on a PR @jstejada ?
@jstejada any news on a restart feature? I'd be happy to make a PR if no one is working on it.
Hi everyone, sorry once again for dragging my feet on this, I've been a bit busy with other stuff. I cleaned up some of Typist's internals a little bit with the recent new features, but haven't had time to look into the restart
option. It should be fairly simple to implement!
@IAmNatch (or anyone else) feel free to submit a PR, that would be great! Let me know if you have any questions :)
@jstejada @IAmNatch After thinking on this a little, I'm not sure that the restart feature in its own right has much of a use case. Perhaps the feature that we want to build here is something that accepts an array of strings and loops over them typing each one out and erasing it. Something like what's being done here. This is pretty similar to the way I've used Typist in my own projects as well. Thoughts?
Also, have started digging into this a bit. Will work on a PR if no one is working on one already.
How is going this feature?
So it looks like someone has already implemented this feature as a separate package. https://www.npmjs.com/package/react-typist-cycle
Not sure whether this is worth doing if we're just going to be duplicating work. Interested in hearing the package maintainer's thoughts on adding this feature though. @jstejada
@jstejada As you consider restart
option - please consider usecases for looping through phrases. Essentially, the library should allow for re-rendering the typist effect if the children change.
hmm yeah that's a good point @jmknoll, it seems that restart could work in different ways:
- Type the words, then immediately start type them again (without a backspace animation)
- Type the words, then backspace animation, then type them again
I feel like the example in https://www.thinkful.com/, i.e. with a backspace animation and displaying a different word every time, could (and should?) be implemented without a restart
option, and can just be implemented with <Typist.Backspace />, e.g. something like:
const words = ['one', 'two', 'three'];
return (
<Typist>
Word number: {words.map(word => <>{word}<Typist.Backspace count={word.length} /></>)}
</Typist>
)
or something like that (didn't actually test that code)
It seems like the simplest possible option would be something like loopIndefinitely
(possibly a better name than restart`, which just unmounts Typist and remounts it with the same text and the end of the animation.
You're right though, I'm not sure if that's worth implementing though, given that you could implement it quite easily or use something like react-typist-cycle.
If there's enough interest, maybe we can implement that option. Curious to hear what others think!
It works by mounting the component again, changing the state of the component.
I'd like the final code looks like
<Typist loop={true} lastSentenceEffect={fadeOut} >
<span> First Sentence </span>
<Typist.Backspace count={8} delay={200} />
<span> Phrase </span>
</Typist>
Any updates on this part?
@yluijten 1+
Also react-typist-cycle hasn't been updated in a year ---> https://github.com/rorz/react-typist-cycle
Can we get an update if this is going to be implemented or if someone is working on a PR? or if you guys just want future peeps to use react-typist-cycle
cheers!
I have no current plans to implement this, but happy to take a PR!
I had a need for this today (I wanted to infinitely loop through an array of phrases with Typist) and have come up with something though not sure how it could be implemented as a feature of the library.
The general idea follows your example @jstejada I had to add a couple of pieces to get it working and I was also concerned about memory management but think I have that covered now.
Here's my code and I'll go over some of the relevant bits at the end -
const commandments = [
'Never let no one know how much dough you hold',
'Never let em know your next move',
'Never trust no-bo-dy',
...
];
class Tablet extends React.Component {
constructor(props) {
super(props);
this.state = {
currentCommandmentIndex: 1,
commandmentContent: this.contentElement(0),
};
}
contentElement = index => {
return (
<Typist key={index} onTypingDone={this.manageContent}>
{commandments[index]}
<Typist.Backspace count={commandments[index].length} />
</Typist>
);
};
generateContent = () => {
let index = this.state.currentCommandmentIndex;
if (index > commandments.length - 1) {
this.setState({ currentCommandmentIndex: 0 });
return true;
}
this.setState({
currentCommandmentIndex: index + 1,
commandmentContent: this.contentElement(index),
});
};
manageContent = () => {
this.generateContent() ? this.generateContent() : null;
};
render() {
return (
<div className="commandments">
{this.state.roleContent}
</div>
.....
So this uses component state to store the current content to render. The content uses the key
property on the Typist
component to trigger the rerender on setState
. The Typist
component uses the onTypingDone
callback to recursively call the next render of the Typist
component by setting state with new content to render.
I added the manageContent
method to kill the recursion once the array of phrases had been rendered once. I'm not sure if this is nessecary and the decision to call it after a full loop through the phrase list was arbitrary. I had been running it without this and the memory for the page was slowly creeping up in dev tools but with this it is remaining at a steadyish 1~2MB higher mem usage than the same page without Typist.
Any feedback would be great and if you think this can be implemented as a feature I'd be happy to contribute.
You can try:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Typist from 'react-typist';
import uuid from 'uuid'
import './styles.scss'
export default class Typing extends Component {
static propTypes = {
words: PropTypes.array.isRequired
}
static defaultProps = {
words: [
'affordable',
'accessible',
'rewarding'
]
}
render() {
const { words } = this.props
const n = words.map((word, i) => {
return ([
<span key={uuid()}>{word}</span>,
<Typist.Backspace key={uuid()} count={word.length} delay={600} />
])
})
return [
<Typist key={uuid()} onTypingDone={() => this.forceUpdate()}>
{n}
</Typist>
]
}
}
This is ugly but works:
const words = ['Reach', 'Engage', 'Impact'];
for (let i = 3; i < 18; i++) {
words[i] = words[i - 3];
}
function Component() {
return (
<Typist>
{words.map((word, i) => (
<span key={word}>
{word}
<Typist.Backspace
count={word.length}
delay={(i + 1) * 1000}
/>
</span>
))}
</Typist>
)
}
Of course it is not unlimited but limited by the iteration count in the loop.
Updating key
in onTypingDone
rerenders the whole component after typing is complete, restarting the animation:
<Typist
key={typistIndex}
onTypingDone={() => this.setState(state => ({ typistIndex: state.typistIndex + 1 }))}
>
{['foo', 'bar', 'baz'].map(word => ([
<span>{word}</span>,
<Typist.Backspace count={word.length} delay={1000} />,
]))}
</Typist>
Using React Hooks:
type RestartingTypistProps = {
children: Node,
};
const RestartingTypist = (props: RestartingTypistProps) => {
const [done, setDone] = useState(false);
const { children } = props;
useEffect(() => {
if (done) {
setTimeout(() => setDone(false), 250);
}
});
if (done) {
return <p> </p>;
}
return (
<Typist onTypingDone={() => setDone(true)}>
{children}
</Typist>
);
};
Usage example:
<RestartingTypist>
occassion
<Typist.Backspace count={9} delay={500} />
</RestartingTypist>
Updating
key
inonTypingDone
rerenders the whole component after typing is complete, restarting the animation:<Typist key={typistIndex} onTypingDone={() => this.setState(state => ({ typistIndex: state.typistIndex + 1 }))} > {['foo', 'bar', 'baz'].map(word => ([ <span>{word}</span>, <Typist.Backspace count={word.length} delay={1000} />, ]))} </Typist>
@Cinamonas I'm trying something like this but the animation does not repeat. I'm unsure why
import React, {Component} from 'react';
import Typist from 'react-typist';
export default class TypeAnimation extends Component {
typistIndex;
constructor(props){
super(props);
this.state = {
typistIndex: 0,
}
}
render() {
return (
<Typist
key={this.typistIndex}
onTypingDone={() => this.setState(state => ({ typistIndex: state.typistIndex + 1 }))}
>
{['bank statements.', 'investment reports.'].map(word => ([
<span>{word}</span>,
<Typist.Backspace count={word.length} delay={1000} />,
]))}
</Typist>
);
}
}
I just created this using react hooks in a stateless function Hope it helps
https://codesandbox.io/s/happy-zhukovsky-rycur
I just created this using react hooks in a stateless function Hope it helps
https://codesandbox.io/s/happy-zhukovsky-rycur
instead of use count outside de element, you can put it on a key prop:
<Typist key={countTyping} cursor={{show:false}} avgTypingDelay={50} onTypingDone={() => setCountTyping(0)} >
A more elegant approuch
updated sandbox code https://codesandbox.io/s/react-text-typing-effect-in-infinite-loop-hooks-5yip6
I got a bettern version
import React, { useState } from "react";
import Typist from 'react-typist';
export default function InfTypist({words}) {
const [index, setIndex] = useState(0);
let word = words[index%words.length];
let Infi = ()=>{ return ( <Typist onTypingDone={()=>{setIndex((i)=> i+1)}}>
{word}
<Typist.Backspace count={word.length} delay={word.length*100} />
</Typist>)}
return (
<Infi/>
)
}
https://codesandbox.io/s/react-text-typing-effect-in-infinite-loop-hooks-forked-14bff?file=/src/index.js
No update on this ticket? It has been opened in 2016... Loop typing should be a stapple feature... Any update?