2
\$\begingroup\$

One of the dream idea to build typewriting in ReactJS. The code seems to be working, still there are something looks not that great in the code 😔 Looking for better solution (best practice) in the function like animationManager()

Demo: https://codesandbox.io/s/qk4591q1kw

index.js

import React from "react";
import ReactDOM from "react-dom";
import Typewriter from "./Typewriter";

import "./styles.css";

const words = [
  "Twinkle twinkle little star",
  "How I wonder what you are.",
  "Up above the world so high",
  "Like a diamond in the sky"
];

function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <Typewriter data={words} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Typewriter.js

import React, { Component } from "react";
import PropTypes from "prop-types";
import styled, { keyframes } from "styled-components";
let lastTime = performance.now();

const rotate = keyframes`
  0% {
    border-right: 2px solid tomato;
  }
  50% {
    border-right: 2px solid transparent;
  }
  100% {
    border-right: 2px solid tomato;
  }
`;
const Rotate = styled.span`
  color: tomato;
  animation: ${rotate} 0.7s linear infinite;
`;

class Typewriter extends Component {
  state = {
    output: ""
  };
  index = 0;
  rafRef = null;
  timeoutRef = null;

  static defaultProps = {
    typingSpeed: 50,
    // it needs to implement in delete function
    deletingSpeed: 32,
    pauseBeforeRestarting: 200,
    pauseBeforeDeleting: 1500,
    data: [],
    style: {},
    className: null
  };

  componentDidMount() {
    this.animationManager();
  }

  componentWillUnmount() {
    // cleanup
    if (this.timeoutRef) {
      clearTimeout(this.timeoutRef);
    }
    if (this.rafRef) {
      cancelAnimationFrame(this.rafRef);
    }
  }

  animationManager = () => {
    this.rafRef = requestAnimationFrame(time => {
      const typingData = this.props.data;
      this.typeEffect(time, typingData[this.index], () => {
        this.timeoutRef = setTimeout(() => {
          this.rafRef = requestAnimationFrame(time => {
            this.deleteEffect(time, () => {
              this.timeoutRef = setTimeout(() => {
                this.index =
                  this.index === typingData.length - 1 ? 0 : this.index + 1;
                this.animationManager();
              }, this.props.pauseBeforeRestarting);
            });
          });
        }, this.props.pauseBeforeDeleting);
      });
    });
  };

  typeEffect = (time, text, callback) => {
    if (time - lastTime < this.props.typingSpeed) {
      this.rafRef = requestAnimationFrame(time => {
        this.typeEffect(time, text, callback);
      });
      return;
    }
    lastTime = time;
    this.setState({
      output: text.substr(0, this.state.output.length + 1)
    });
    if (this.state.output.length < text.length) {
      this.rafRef = requestAnimationFrame(time => {
        this.typeEffect(time, text, callback);
      });
    } else {
      return callback();
    }
  };

  deleteEffect = (time, callback) => {
    if (time - lastTime < this.props.typingSpeed) {
      this.rafRef = requestAnimationFrame(time => {
        this.deleteEffect(time, callback);
      });
      return;
    }
    lastTime = time;
    this.setState({
      output: this.state.output.substr(0, this.state.output.length - 1)
    });
    if (this.state.output.length !== 0) {
      this.rafRef = requestAnimationFrame(time => {
        this.deleteEffect(time, callback);
      });
    } else {
      return callback();
    }
  };

  render() {
    return (
      <Rotate className={this.props.className} style={this.props.style}>
        {this.state.output}
      </Rotate>
    );
  }
}

Typewriter.propTypes = {
  typingSpeed: PropTypes.numnber,
  deletingSpeed: PropTypes.numnber,
  pauseBeforeRestarting: PropTypes.numnber,
  pauseBeforeDeleting: PropTypes.numnber,
  data: PropTypes.array,
  style: PropTypes.object,
  className: PropTypes.string
};

export default Typewriter;
\$\endgroup\$
4
  • \$\begingroup\$ Is the user supposed to be able to type, or does it just simulate text being typed? \$\endgroup\$ Commented Jan 18, 2019 at 17:13
  • \$\begingroup\$ @SᴀᴍOnᴇᴌᴀ It is just typing animation based on strings in the array \$\endgroup\$ Commented Jan 20, 2019 at 20:48
  • \$\begingroup\$ I think you can do it with only one setTimeout - this will add one letter to the string and then check if there are letters left. If so, call the same setTimeout again. I think you have too many intervals and requestanimationframes going on. \$\endgroup\$ Commented Apr 9, 2019 at 8:02
  • \$\begingroup\$ @Kokodoko Could you please help for that ? \$\endgroup\$ Commented Apr 9, 2019 at 13:31

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.