How can I create animated letter increments of a g

2019-09-15 05:43发布

问题:

I'm creating an "animated letter incrementer" that takes any given word and increments each letter of that word starting from A.

Example:

Word = Dog

D - Increments from A to D [A, B, C, D]
O - Increments from A to O [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O]
G - Increments from A to G [A, B, C, D, E, F, G]

The effect I'd like to achieve is similar to this jQuery animated number counter, except I'll be incrementing letters instead of counting numbers. Also, each letter of the word should increment independently, but they should all reach their destination letter at the same time.

JS:

var wordToIncrement = 'DOG';
var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '.split('');

for (var i = 0, len = wordToIncrement.length; i < len; i++) {

    var letterToIncrementTo = wordToIncrement[i];

    var arrayLength = alphabet.length;
    for (var z = 0; z < arrayLength; z++) {

        var alphabetLetter = alphabet[z];

        if (alphabetLetter == letterToIncrementTo) {

            console.log('MATCH FOUND!');
            console.log('Word Letter: ' + letterToIncrementTo);
            console.log('Alphabet Letter: ' + alphabetLetter);

            alphabetPositionValue = z;
            console.log('VALUE: ' + alphabetPositionValue);

            function incrementToLetter(letterToIncrementTo,alphabetPositionValue) {

                // This is where I'm stuck

                var div = document.getElementById('#word_block');
                div.innerHTML = div.innerHTML + 'Extra stuff';


            }

        }

    }

}

HTML:

<div id="work_block"></div>

How can I complete the code above to achieve similar functionality to the animated number counter example and increment through each letter of the word? I am looking for a javascript-based solution.

回答1:

I would build a letter object maintaining the letter and timings. This way you can provide a simple update functionality on the object and the object will itself make sure it produces the correct current letter.

For example:

function Letter(table, letter, duration) {
  this.table = table;                          // lookup-table
  this.letter = letter;                        // target letter
  this.current = 0;                            // index in table
  this.delay = duration / tbl.indexOf(letter); // ms
  this.time = Date.now();                      // current (start) time
  this.done = false;                           // status
}

Then a common prototyped update() method:

Letter.prototype.update = function() {
  if (this.done) return;                       // if done, do no more
  var time = Date.now();                       // get current time
  if (time - this.time >= this.delay) {        // exceeded delay?
    this.time = time;                          // store current time
    if (this.letter === this.table[this.current] || 
        this.current === this.table.length) {  // target reached? unknown letter
      this.done = true;                        // we're done
    }
    else {
      this.current++;                          // next in table
    }
  }
};

Then we can produce the objects from the string:

var letters = [];
word.toUpperCase().split("").forEach(function(l) {
  letters.push(new Letter(tbl, l, 2500));  // 2.5s duration
});

Then animate it:

(function loop() {
   var txt = "", isDone = true;
   letters.forEach(function(l) {
     l.update();
     if (!l.done) isDone = false;
     txt += l.table[l.current];
   });

   // output txt
   if (!isDone) requestAnimationFrame(loop);
   else { /* done */ }
})();

Demo

function Letter(table, letter, duration) {
  this.table = table;
  this.letter = letter;
  this.current = 0;
  this.delay = duration / tbl.indexOf(letter);   // ms
  this.time = Date.now();
  this.done = false;
}
Letter.prototype.update = function() {
  if (this.done) return;
  var time = Date.now();
  if (time - this.time >= this.delay) {
    this.time = time;
    if (this.letter === this.table[this.current] || 
        this.current === this.table.length) {
      this.done = true;
    }
    else {
      this.current++;
    }
  }
};

var word = "hello there";
var tbl = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var letters = [];
word.toUpperCase().split("").forEach(function(l) {
  letters.push(new Letter(tbl, l, 2500))
});

(function loop() {
  var txt = "", isDone = true;
  letters.forEach(function(l) {
    l.update();
    if (!l.done) isDone = false;
    txt += l.table[l.current];
  });

  // output txt
  d.innerHTML = txt;
  
  if (!isDone) requestAnimationFrame(loop);
  else { /* done */ }
})();
#d {font:bold 32px monospace}
<div id=d></div>

Misc

If the delay is below 16.7ms it may not increment fast enough. It can be solved by dividing current relative time on duration. Multiply this normalized value with index of the target letter in the table to get a current, just round the result to an integer value.

You can provide different tables to obtain randomness/variation.