1<!DOCTYPE html> 2<html> 3<head> 4 <title>Lottie Filmstrip Capture</title> 5 <meta charset="utf-8" /> 6 <meta http-equiv="X-UA-Compatible" content="IE=egde,chrome=1"> 7 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 8 <script src="/lottie.js" type="text/javascript" charset="utf-8"></script> 9 <style type="text/css" media="screen"> 10 body, 11 main, 12 .anim { 13 margin: 0; 14 padding: 0; 15 } 16 17 main { 18 display: flex; 19 width: 1000px; 20 height: 1000px; 21 flex-flow: row wrap; 22 } 23 </style> 24</head> 25<body> 26 <main> 27 <div class=anim></div> 28 </main> 29 <script type="text/javascript" charset="utf-8"> 30 (function () { 31 const TILE_COUNT = 5; // Number of tiles in x or y direction. 32 const TARGET_SIZE = 1000; // Image size in pixels both x and y direction. 33 const PATH = '/lottie.json'; 34 35 let renderer = 'svg'; 36 let hash = window.location.hash; 37 if (hash) { 38 renderer = hash.slice(1); 39 } 40 41 // This global is used by puppeteer to determine if all tiles have finished drawing. 42 window._tileCount = 0; 43 44 // First load the animation for just a single tile 45 // so we can read out some values and calculate what 46 // the filmstrip should look like. 47 let anim = lottie.loadAnimation({ 48 container: document.querySelector('.anim'), 49 renderer: renderer, 50 loop: false, 51 autoplay: true, 52 path: PATH, 53 rendererSettings: { 54 preserveAspectRatio:'xMidYMid meet' 55 }, 56 }); 57 58 anim.addEventListener('data_ready', (e) => { 59 // Once the first tile is loaded, calculate what 60 // the filmstrip should look like. 61 let animationData = anim.animationData; 62 let totalFrames = anim.totalFrames; 63 // t_rate mimics DMSrcSink.cpp::SkottieSrc::draw 64 let t_rate = 1.0 / (TILE_COUNT * TILE_COUNT - 1); 65 66 let main = document.querySelector('main'); 67 68 // Clear out the first div now that our measurements are done. 69 main.firstElementChild.remove(); 70 71 // Add in all the tiles. 72 for (let i = 0; i < TILE_COUNT*TILE_COUNT; i++) { 73 let div = document.createElement('div'); 74 div.classList.add('anim'); 75 div.style.width = (TARGET_SIZE / TILE_COUNT) + 'px'; 76 div.style.height = (TARGET_SIZE / TILE_COUNT) + 'px'; 77 main.appendChild(div); 78 79 // create a new animation for each tile. It is tempting to try having 80 // one animation and "clone" each frame, but that doesn't work 81 // because of how bodymovin cleans up the URLObjects that are the path 82 // data for the svgs. 83 // We can re-use the animationData to avoid having to hit the 84 // (local) network a bunch of times. 85 let anim = lottie.loadAnimation({ 86 container: div, 87 renderer: renderer, 88 loop: false, 89 autoplay: false, 90 animationData: animationData, 91 rendererSettings: { 92 preserveAspectRatio:'xMidYMid meet' 93 }, 94 }); 95 96 let t = Math.max(Math.min(t_rate * i, 1.0), 0.0); 97 let seekToFrame = totalFrames * t; 98 if (seekToFrame >= totalFrames) { 99 // bodymovin player sometimes draws blank when requesting 100 // to draw the very last frame. Subtracting a small value 101 // seems to fix this and make it draw the last frame. 102 seekToFrame -= .001; 103 } 104 105 // don't need to wait for data_ready because it's instantly ready. 106 console.log(`t = ${t}, go to frame ${seekToFrame}`); 107 anim.goToAndStop(seekToFrame, true); 108 window._tileCount += 1; 109 } 110 }); 111 })(); 112 </script> 113</body> 114</html> 115