<div id='settings'>
  <button id='go'>Restart</button>
</div>
<div id="container"></div>


<script src="https://unpkg.com/konva@7.0.3/konva.min.js"></script>
body {
  margin: 14px;
  padding: 10px;
  font: 12pt Verdana, Arial, sans-serif;
}
#settings {
  position: absolute;
z-index: 100;
  padding: 2px;  
}
#container {
  position: absolute;
  padding: 2px;
}
th {
  text-align: left;
  padding-top: 5px;
  background-color: silver;
}
td {
  padding-left:15px;
}
button {
  z-index: 100;
}
console.clear;
let stage = null,
    layer = null,
    wordList = [],
    timerSpan = $('#timer'),
    frameRateSpan = $('#frameRate');

setup(); // boring stuff to set up the stage and words.

console.log('Animate ' + wordList.length + ' words' )

let easing = new Easings();
 
stage.draw();

animate();

function animate(){

  // These are the values affecting the animation
  let 
      // initial spiral outer circle radius - reduces per animation frame.
      maxRadius = 40, 
      
      // duration of animation in ms
      duration = 2000,  
      
      // angle to traverse in the duration, in radians.
      rotationDegrees = 720 * Math.PI/180; 

   layer.find('Text').opacity(0); // fade out the text
  
   let anim = new Konva.Animation(function(frame) {

     let time = frame.time;
     let t1 = performance.now()   

     // for each word...
     for (let i = 0; i < wordList.length; i++){

       let 
           // calculate the new radius
           radius = easing.EaseInCubic(frame.time, maxRadius, -maxRadius, duration), 
           // and new angle - note all the words have a different initial angle value
           angle = wordList[i].angle  + easing.EaseInCubic(frame.time, 0, rotationDegrees, duration),

           // and the final position we are destined for
           pos = wordList[i].finalPosition,

           // simple trig to calculate the x,y position of the word at this frame
           x = pos.x + (Math.sin(angle) * radius),
           y = pos.y - (Math.cos(angle) * radius);         

       // Apply the position to the word
       wordList[i].shape.position({x: x, y: y});
       
       // fade the text into view as the animation progresses
       wordList[i].shape.opacity(frame.time / duration);
       
       // If we are at the end of the animation ensure we hit the final target position
       if (time >= duration){
         wordList[i].shape.position(wordList[i].finalPosition);
       }
     }  

     // If we are at the end of the animation then stop it !
     if (time > duration){
       anim.stop()
     }
   
  }, layer);
  
  anim.start();
}


$('#go').on('click', function(){
  
  animate();

})




function setup(){
  

stage = new Konva.Stage({ // Set up a stage
      container: "container",
      width:  window.innerWidth,
      height: window.innerHeight,
      scaleX: 2, 
      scaleY: 2,
      offset: {x: -70, y: -20}
    });
    
  // add a layer to draw on
  layer = new Konva.Layer();
     
let    posData = `{"id":"","fontInfo":{"fontName":"Arial","fontSize":"10","fontColor":"Black","fontBold":""},"paras":[{"id":"para-8","sentences":[{"id":"sentence-0","lines":[{"id":"line-0","words":[{"box":[25.0001,25.0001,33.3546,15.0001],"text":"Ground"},{"box":[58.3546,25.0001,2.7784,15.0001],"text":" "},{"box":[61.1329,25.0001,32.2364,15.0001],"text":"Control"},{"box":[93.3692,25.0001,2.7784,15.0001],"text":" "},{"box":[96.1476,25.0001,8.3399,15.0001],"text":"to"},{"box":[104.4874,25.0001,2.7784,15.0001],"text":" "},{"box":[107.2657,25.0001,25.005,15.0001],"text":"Major"},{"box":[132.2706,25.0001,2.7784,15.0001],"text":" "},{"box":[135.0489,25.0001,18.8917,15.0001],"text":"Tom"}]}]},{"id":"sentence-1","lines":[{"id":"line-0","words":[{"box":[25.0001,40.0001,33.3546,15.0001],"text":"Ground"},{"box":[58.3546,40.0001,2.7784,15.0001],"text":" "},{"box":[61.1329,40.0001,32.2364,15.0001],"text":"Control"},{"box":[93.3692,40.0001,2.7784,15.0001],"text":" "},{"box":[96.1476,40.0001,8.3399,15.0001],"text":"to"},{"box":[104.4874,40.0001,2.7784,15.0001],"text":" "},{"box":[107.2657,40.0001,25.005,15.0001],"text":"Major"},{"box":[132.2706,40.0001,2.7784,15.0001],"text":" "},{"box":[135.0489,40.0001,18.8917,15.0001],"text":"Tom"}]}]},{"id":"sentence-2","lines":[{"id":"line-0","words":[{"box":[25.0001,55.0001,21.1231,15.0001],"text":"Take"},{"box":[46.1231,55.0001,2.7784,15.0001],"text":" "},{"box":[48.9015,55.0001,19.4532,15.0001],"text":"your"},{"box":[68.3546,55.0001,2.7784,15.0001],"text":" "},{"box":[71.1329,55.0001,30.5763,15.0001],"text":"protein"},{"box":[101.7091,55.0001,2.7784,15.0001],"text":" "},{"box":[104.4874,55.0001,17.2267,15.0001],"text":"pills"},{"box":[121.714,55.0001,2.7784,15.0001],"text":" "},{"box":[124.4923,55.0001,16.6847,15.0001],"text":"and"},{"box":[141.1769,55.0001,2.7784,15.0001],"text":" "},{"box":[143.9552,55.0001,13.9015,15.0001],"text":"put"},{"box":[157.8565,55.0001,2.7784,15.0001],"text":" "},{"box":[160.6349,55.0001,19.4532,15.0001],"text":"your"},{"box":[180.088,55.0001,2.7784,15.0001],"text":" "},{"box":[182.8663,55.0001,30.0147,15.0001],"text":"helmet"},{"box":[212.881,55.0001,2.7784,15.0001],"text":" "},{"box":[215.6593,55.0001,11.1231,15.0001],"text":"on"}]}]},{"id":"sentence-3","lines":[{"id":"line-0","words":[{"box":[25.0001,70.0001,33.3546,15.0001],"text":"Ground"},{"box":[58.3546,70.0001,2.7784,15.0001],"text":" "},{"box":[61.1329,70.0001,32.2364,15.0001],"text":"Control"},{"box":[93.3692,70.0001,2.7784,15.0001],"text":" "},{"box":[96.1476,70.0001,8.3399,15.0001],"text":"to"},{"box":[104.4874,70.0001,2.7784,15.0001],"text":" "},{"box":[107.2657,70.0001,25.005,15.0001],"text":"Major"},{"box":[132.2706,70.0001,2.7784,15.0001],"text":" "},{"box":[135.0489,70.0001,18.8917,15.0001],"text":"Tom"}]}]},{"id":"sentence-4","lines":[{"id":"line-0","words":[{"box":[25.0001,85.0001,58.9112,15.0001],"text":"Commencing"},{"box":[83.9112,85.0001,2.7784,15.0001],"text":" "},{"box":[86.6896,85.0001,51.1476,15.0001],"text":"countdown,"},{"box":[137.837,85.0001,2.7784,15.0001],"text":" "},{"box":[140.6153,85.0001,35.0294,15.0001],"text":"engines"},{"box":[175.6446,85.0001,2.7784,15.0001],"text":" "},{"box":[178.423,85.0001,11.1231,15.0001],"text":"on"}]}]},{"id":"sentence-5","lines":[{"id":"line-0","words":[{"box":[25.0001,100.0001,28.3448,15.0001],"text":"Check"},{"box":[53.3448,100.0001,2.7784,15.0001],"text":" "},{"box":[56.1231,100.0001,31.6896,15.0001],"text":"ignition"},{"box":[87.8126,100.0001,2.7784,15.0001],"text":" "},{"box":[90.5909,100.0001,16.6847,15.0001],"text":"and"},{"box":[107.2755,100.0001,2.7784,15.0001],"text":" "},{"box":[110.0538,100.0001,18.8917,15.0001],"text":"may"},{"box":[128.9454,100.0001,2.7784,15.0001],"text":" "},{"box":[131.7237,100.0001,25.8106,15.0001],"text":"God's"},{"box":[157.5343,100.0001,2.7784,15.0001],"text":" "},{"box":[160.3126,100.0001,18.3448,15.0001],"text":"love"},{"box":[178.6573,100.0001,2.7784,15.0001],"text":" "},{"box":[181.4356,100.0001,11.1231,15.0001],"text":"be"},{"box":[192.5587,100.0001,2.7784,15.0001],"text":" "},{"box":[195.337,100.0001,17.7833,15.0001],"text":"with"},{"box":[213.1202,100.0001,2.7784,15.0001],"text":" "},{"box":[215.8985,100.0001,16.1231,15.0001],"text":"you"}]}]}]},{"id":"para-8","sentences":[{"id":"sentence-0","lines":[{"id":"line-0","words":[{"box":[25.0001,140.0001,56.7042,15.0001],"text":"[spoken]Ten,"},{"box":[81.7042,140.0001,2.7784,15.0001],"text":" "},{"box":[84.4825,140.0001,23.3448,15.0001],"text":"Nine,"},{"box":[107.8272,140.0001,2.7784,15.0001],"text":" "},{"box":[110.6056,140.0001,25.5714,15.0001],"text":"Eight,"},{"box":[136.1769,140.0001,2.7784,15.0001],"text":" "},{"box":[138.9552,140.0001,31.1329,15.0001],"text":"Seven,"},{"box":[170.088,140.0001,2.7784,15.0001],"text":" "},{"box":[172.8663,140.0001,16.67,15.0001],"text":"Six,"},{"box":[189.5362,140.0001,2.7784,15.0001],"text":" "},{"box":[192.3146,140.0001,21.67,15.0001],"text":"Five,"},{"box":[213.9845,140.0001,2.7784,15.0001],"text":" "},{"box":[216.7628,140.0001,22.7882,15.0001],"text":"Four,"},{"box":[239.5509,140.0001,2.7784,15.0001],"text":" "},{"box":[242.3292,140.0001,28.9015,15.0001],"text":"Three,"}]},{"id":"line-1","words":[{"box":[25.0001,155.0001,21.1183,15.0001],"text":"Two,"},{"box":[46.1183,155.0001,2.7784,15.0001],"text":" "},{"box":[48.8966,155.0001,21.6798,15.0001],"text":"One,"},{"box":[70.5763,155.0001,2.7784,15.0001],"text":" "},{"box":[73.3546,155.0001,24.2774,15.0001],"text":"Liftoff"}]}]}]},{"id":"para-8","sentences":[{"id":"sentence-0","lines":[{"id":"line-0","words":[{"box":[25.0001,195.0001,18.8917,15.0001],"text":"This"},{"box":[43.8917,195.0001,2.7784,15.0001],"text":" "},{"box":[46.67,195.0001,7.2218,15.0001],"text":"is"},{"box":[53.8917,195.0001,2.7784,15.0001],"text":" "},{"box":[56.67,195.0001,33.3546,15.0001],"text":"Ground"},{"box":[90.0245,195.0001,2.7784,15.0001],"text":" "},{"box":[92.8028,195.0001,32.2364,15.0001],"text":"Control"},{"box":[125.0392,195.0001,2.7784,15.0001],"text":" "},{"box":[127.8175,195.0001,8.3399,15.0001],"text":"to"},{"box":[136.1573,195.0001,2.7784,15.0001],"text":" "},{"box":[138.9356,195.0001,25.005,15.0001],"text":"Major"},{"box":[163.9405,195.0001,2.7784,15.0001],"text":" "},{"box":[166.7189,195.0001,18.8917,15.0001],"text":"Tom"}]}]},{"id":"sentence-1","lines":[{"id":"line-0","words":[{"box":[25.0001,210.0001,29.3458,15.0001],"text":"You've"},{"box":[54.3458,210.0001,2.7784,15.0001],"text":" "},{"box":[57.1241,210.0001,23.8966,15.0001],"text":"really"},{"box":[81.0206,210.0001,2.7784,15.0001],"text":" "},{"box":[83.7989,210.0001,25.0147,15.0001],"text":"made"},{"box":[108.8136,210.0001,2.7784,15.0001],"text":" "},{"box":[111.5919,210.0001,13.9015,15.0001],"text":"the"},{"box":[125.4933,210.0001,2.7784,15.0001],"text":" "},{"box":[128.2716,210.0001,25.5763,15.0001],"text":"grade"}]}]},{"id":"sentence-2","lines":[{"id":"line-0","words":[{"box":[25.0001,225.0001,17.7931,15.0001],"text":"And"},{"box":[42.7931,225.0001,2.7784,15.0001],"text":" "},{"box":[45.5714,225.0001,13.9015,15.0001],"text":"the"},{"box":[59.4728,225.0001,2.7784,15.0001],"text":" "},{"box":[62.2511,225.0001,30.5763,15.0001],"text":"papers"},{"box":[92.8272,225.0001,2.7784,15.0001],"text":" "},{"box":[95.6056,225.0001,21.1231,15.0001],"text":"want"},{"box":[116.7286,225.0001,2.7784,15.0001],"text":" "},{"box":[119.5069,225.0001,8.3399,15.0001],"text":"to"},{"box":[127.8468,225.0001,2.7784,15.0001],"text":" "},{"box":[130.6251,225.0001,23.3448,15.0001],"text":"know"},{"box":[153.9698,225.0001,2.7784,15.0001],"text":" "},{"box":[156.7481,225.0001,28.9064,15.0001],"text":"whose"},{"box":[185.6544,225.0001,2.7784,15.0001],"text":" "},{"box":[188.4327,225.0001,23.8917,15.0001],"text":"shirts"},{"box":[212.3243,225.0001,2.7784,15.0001],"text":" "},{"box":[215.1026,225.0001,16.1231,15.0001],"text":"you"},{"box":[231.2257,225.0001,2.7784,15.0001],"text":" "},{"box":[234.004,225.0001,21.6749,15.0001],"text":"wear"}]}]},{"id":"sentence-3","lines":[{"id":"line-0","words":[{"box":[25.0001,240.0001,20.005,15.0001],"text":"Now"},{"box":[45.005,240.0001,2.7784,15.0001],"text":" "},{"box":[47.7833,240.0001,11.9093,15.0001],"text":"it's"},{"box":[59.6925,240.0001,2.7784,15.0001],"text":" "},{"box":[62.4708,240.0001,18.8917,15.0001],"text":"time"},{"box":[81.3624,240.0001,2.7784,15.0001],"text":" "},{"box":[84.1407,240.0001,8.3399,15.0001],"text":"to"},{"box":[92.4806,240.0001,2.7784,15.0001],"text":" "},{"box":[95.2589,240.0001,23.9064,15.0001],"text":"leave"},{"box":[119.1651,240.0001,2.7784,15.0001],"text":" "},{"box":[121.9435,240.0001,13.9015,15.0001],"text":"the"},{"box":[135.8448,240.0001,2.7784,15.0001],"text":" "},{"box":[138.6231,240.0001,34.4679,15.0001],"text":"capsule"},{"box":[173.0909,240.0001,2.7784,15.0001],"text":" "},{"box":[175.8692,240.0001,5.0001,15.0001],"text":"if"},{"box":[180.8692,240.0001,2.7784,15.0001],"text":" "},{"box":[183.6476,240.0001,16.1231,15.0001],"text":"you"},{"box":[199.7706,240.0001,2.7784,15.0001],"text":" "},{"box":[202.5489,240.0001,20.0147,15.0001],"text":"dare"}]}]}]},{"id":"para-8","sentences":[{"id":"sentence-0","lines":[{"id":"line-0","words":[{"box":[25.0001,280.0001,18.8917,15.0001],"text":"This"},{"box":[43.8917,280.0001,2.7784,15.0001],"text":" "},{"box":[46.67,280.0001,7.2218,15.0001],"text":"is"},{"box":[53.8917,280.0001,2.7784,15.0001],"text":" "},{"box":[56.67,280.0001,25.005,15.0001],"text":"Major"},{"box":[81.6749,280.0001,2.7784,15.0001],"text":" "},{"box":[84.4532,280.0001,18.8917,15.0001],"text":"Tom"},{"box":[103.3448,280.0001,2.7784,15.0001],"text":" "},{"box":[106.1231,280.0001,8.3399,15.0001],"text":"to"},{"box":[114.463,280.0001,2.7784,15.0001],"text":" "},{"box":[117.2413,280.0001,33.3546,15.0001],"text":"Ground"},{"box":[150.5958,280.0001,2.7784,15.0001],"text":" "},{"box":[153.3741,280.0001,32.2364,15.0001],"text":"Control"}]}]},{"id":"sentence-1","lines":[{"id":"line-0","words":[{"box":[25.0001,295.0001,13.0177,15.0001],"text":"I'm"},{"box":[38.0177,295.0001,2.7784,15.0001],"text":" "},{"box":[40.796,295.0001,37.8077,15.0001],"text":"stepping"},{"box":[78.6036,295.0001,2.7784,15.0001],"text":" "},{"box":[81.3819,295.0001,33.9161,15.0001],"text":"through"},{"box":[115.298,295.0001,2.7784,15.0001],"text":" "},{"box":[118.0763,295.0001,13.9015,15.0001],"text":"the"},{"box":[131.9776,295.0001,2.7784,15.0001],"text":" "},{"box":[134.756,295.0001,20.0147,15.0001],"text":"door"}]}]},{"id":"sentence-2","lines":[{"id":"line-0","words":[{"box":[25.0001,310.0001,17.7931,15.0001],"text":"And"},{"box":[42.7931,310.0001,2.7784,15.0001],"text":" "},{"box":[45.5714,310.0001,13.0177,15.0001],"text":"I'm"},{"box":[58.589,310.0001,2.7784,15.0001],"text":" "},{"box":[61.3673,310.0001,32.2462,15.0001],"text":"floating"},{"box":[93.6134,310.0001,2.7784,15.0001],"text":" "},{"box":[96.3917,310.0001,7.7833,15.0001],"text":"in"},{"box":[104.1749,310.0001,2.7784,15.0001],"text":" "},{"box":[106.9532,310.0001,5.5616,15.0001],"text":"a"},{"box":[112.5147,310.0001,2.7784,15.0001],"text":" "},{"box":[115.2931,310.0001,21.67,15.0001],"text":"most"},{"box":[136.963,310.0001,2.7784,15.0001],"text":" "},{"box":[139.7413,310.0001,35.0196,15.0001],"text":"peculiar"},{"box":[174.7608,310.0001,2.7784,15.0001],"text":" "},{"box":[177.5392,310.0001,17.7833,15.0001],"text":"way"}]}]},{"id":"sentence-3","lines":[{"id":"line-0","words":[{"box":[25.0001,325.0001,17.7931,15.0001],"text":"And"},{"box":[42.7931,325.0001,2.7784,15.0001],"text":" "},{"box":[45.5714,325.0001,13.9015,15.0001],"text":"the"},{"box":[59.4728,325.0001,2.7784,15.0001],"text":" "},{"box":[62.2511,325.0001,21.67,15.0001],"text":"stars"},{"box":[83.921,325.0001,2.7784,15.0001],"text":" "},{"box":[86.6993,325.0001,18.3448,15.0001],"text":"look"},{"box":[105.044,325.0001,2.7784,15.0001],"text":" "},{"box":[107.8224,325.0001,18.8917,15.0001],"text":"very"},{"box":[126.714,325.0001,2.7784,15.0001],"text":" "},{"box":[129.4923,325.0001,35.9522,15.0001],"text":"different"},{"box":[165.4444,325.0001,2.7784,15.0001],"text":" "},{"box":[168.2228,325.0001,24.463,15.0001],"text":"today"}]}]}]}]}`,
  
    wordNo = 0;

    textInfo = JSON.parse(posData);

// Loading the text
let wordMax = 199, notAtLimit = true;
for (i = 0; i < textInfo.paras.length && notAtLimit; i++){
  let para = textInfo.paras[i]
  for (j = 0; j < para.sentences.length && notAtLimit; j++){
    let sentence = para.sentences[j]
    for (m = 0; m < sentence.lines.length && notAtLimit; m++){
      let line = sentence.lines[m]
      for (n = 0; n < line.words.length && notAtLimit; n++){
        let word = line.words[n];
        
 
        word.box[2] = word.text === "•" ? 10 : word.box[2];
        
        let wordShape = new Konva.Text({
          x: word.box[0],
          y: word.box[1],
          width:   word.box[2], 
          height: word.box[3],
          fontFamily: textInfo.fontInfo.fontName,
          fontSize: textInfo.fontInfo.fontSize,
          fill:  textInfo.fontInfo.fontColor,
          text:  word.text,
          perfectDrawEnabled: false, 
          transformsEnabled: 'position', 
          shadowEnabled: false, 
          shadowForStrokeEnabled: false
        })      

        wordNo = wordNo + 1;
        layer.add(wordShape);
        wordList.push({
          shape: wordShape,
          finalPosition: wordShape.position(),
          angle:  Math.random() * 720 // set a random starting angle different for each word.
        });
        notAtLimit = (wordNo < wordMax);
      }
    }
  }
  

  
  layer.batchDraw();
  stage.draw();
}
 
stage.add(layer);

stage.draw();

}


function Easings() {
  
  let easings = {
    
    //
    // Linear
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    linear : function(t, b, c, d) {
        return c * t / d + b;
    },
    
    
    //
    // Ease In Quadratic
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseInQuad: function(t, b, c, d) {
        t /= d;
        return c * t * t + b;
    },
    
    
    //
    // Ease Out Quadratic
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseOutQuad: function(t, b, c, d) {
        t /= d;
        return -c * t * (t - 2) + b;
    },
    
    
    //
    // Ease In Out Quadratic
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseInOutQuad: function(t, b, c, d) {
        t /= d / 2;
        if(t < 1) {
            return c / 2 * t * t + b;
        }
        t--;
        return -c / 2 * (t * (t - 2) - 1) + b;
    },
    
    
    //
    // Ease In Cubic
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseInCubic: function(t, b, c, d) {
        t /= d;
        return c * t * t * t + b;
    },
    
    
    //
    // Ease Out Cubic
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseOutCubic: function(t, b, c, d) {
        t /= d;
        t--;
        return c * (t * t * t + 1) + b;
    },
    
    
    //
    // Ease In Out Cubic
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseInOutCubic: function(t, b, c, d) {
        t /= d / 2;
        if (t < 1) {
            return c / 2 * t * t * t + b;
        }
        t -= 2;
        return c / 2 * (t * t * t + 2) + b;
    },
    
    
    //
    // Ease In Exponential
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseInExpo: function(t, b, c, d) {
        return c * Math.pow(2, 10 * (t / d - 1)) + b;
    },
    
    
    //
    // Ease Out Exponential
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseOutExpo: function(t, b, c, d) {
        return c * (-Math.pow(2, -10 * t / d) + 1) + b;
    },
    
    
    //
    // Ease In Out Exponential
    // @param t: current time
    // @param b: begin position
    // @param c: change from end to begin position
    // @param d: duration
    //
    EaseInOutExpo: function(t, b, c, d) {
        t /= d / 2;
        if (t < 1) {
            return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
        }
        t--;
        return c / 2 * (-Math.pow(2, -10 * t) + 2) + b;
    }
  }
  return easings;
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js