0

This is the effect I am trying to achieve: http://codepen.io/anon/pen/ENzQem

I am trying to generate an effect where the letters of a string get revealed gradually and randomly. See the codepen link above for a demonstration of this.

However, I'm finding it difficult to show each character naturally if I just simply randomly generate numbers for each delay separately.

If I simply do a Math.random() to generate a number independently for each character, sometimes adjacent letters will have similar delay numbers, and as such the effect will look chunky, with two letters side-by-side appearing at the same rate.

This is the naive solution with separate random number generators:

  renderSpans(text) {
    const textArray = text.split('');
    return textArray.map((letter, index) => {
      const transitionTime = 2000;
      const delay = parseInt(Math.random() * transitionTime, 10);
      const styles = {
        opacity: this.props.show ? '1' : '0',
        transition: `opacity ${transitionTime}ms`,
        transitionDelay: `${delay}ms`,
      };
      return <span style={styles}>{letter}</span>;
    });
  }

I need an algorithm to generate an array of numbers that I can use as the delay for each of the characters, regardless of the length of the input string.

My first thought is to use a sinusoidal wave of some sort, with a bunch of randomness put in, but I'm not sure about the specifics on this. I am sure there's a much more well-accepted way to generate natural-looking noise in mathematics.

Can someone point me to some well-known algorithms for my use case? Maybe something to do with Perlin noise or the like?

Thanks in advance.

adrianmcli
  • 1,956
  • 3
  • 21
  • 49
  • What is issue with current approach? – guest271314 Dec 25 '16 at 03:40
  • @guest271314 I mentioned it in the description above. Independently generated random numbers often will create an effect where several letters close together will have very similar delay numbers, causing a large "chunk" of the string to appear at the same rate (rather than a more distributed effect). – adrianmcli Dec 25 '16 at 03:42
  • 1
    Probably your "sometimes" is the key phrase here - sometimes, they will, sometimes they won't. It's in the nature of randomness. Perhaps you can build an array of probabilities, initially with equal values but decreasing the ones left and right of "picked" ones. (Then again, it would *still* be possible they appear in order. But that chance will be less.) – Jongware Dec 25 '16 at 03:43
  • 1
    See [Random number, which is not equal to the previous number](http://stackoverflow.com/questions/40056297/random-number-which-is-not-equal-to-the-previous-number/). The approach at http://stackoverflow.com/a/40063045/ returns an array of random numbers. – guest271314 Dec 25 '16 at 03:44
  • See also [Random integer in a certain range excluding one number](http://stackoverflow.com/questions/34182699/random-integer-in-a-certain-range-excluding-one-number) – guest271314 Dec 25 '16 at 03:55

1 Answers1

1

It turns out I was overthinking the problem.

I ended up creating the following function:

const getRandoms = (length, threshold) => {
  const tooClose = (a, b) => Math.abs(a - b) < threshold;

  const result = [];
  let random;

  for (let i = 0; i < length; i += 1) {
    random = Math.random();
    if (i !== 0) {
      const prev = result[i - 1];
      while (tooClose(random, prev)) {
        random = Math.random();
      }
    }
    result.push(random);
  }
  return result;
};

I originally wrote a version of this function that uses reduce rather than a for-loop, but ultimately decided that a for-loop would be clearer and simpler.

How it works

This function procedurally builds up an array of random numbers, given the required length and a "closeness" threshold. Each of the resulting random numbers will have a difference greater than the threshold when compared with the previous number in the array.

  1. For the first item in the array, we simply generate a random number and push it in.

  2. For each of the subsequent items in the array, we:

    • Generate a new random number,
    • Compare this number to the previous number in the array,
    • If they are too close (i.e. difference < a threshold value), then generate a new random number and compare again,
    • If they are not too close, then push it into the results array and continue.

I have found that a threshold of 0.2 seem to work pretty well for my use case.

adrianmcli
  • 1,956
  • 3
  • 21
  • 49