Nik333

Top 15 counter progression animation script

Apr 1st, 2019
152
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <!--
  2.     This code should work in most modern browsers. Tested in latest (as of 1 April 2019) Chrome and Edge). May run not-so-smooth on not-so-strong computers.
  3.     Feel free to edit and reuse for other stuff. Crediting (/u/NikinCZ or NoxZet) is welcome but not needed.
  4.     Version with data for download here (if the link breaks, contact /u/NikinCZ on Reddit or @NoxZet#1320 on Discord): https://www.dropbox.com/s/ej7xnrv6kmnxr7k/svgChartAnimator.htm?dl=1
  5.     Edit 2 April 2019: Added comments
  6. -->
  7.  
  8. <svg id="chartSvg" viewBox="0 0 1200 800" width="1200" height="900" xmlns="http://www.w3.org/2000/svg">
  9. <style>
  10.     .title { font: 36px calibri; }
  11.     .username {
  12.         font: 28px calibri;
  13.         color: #fff;
  14.         text-shadow: 1px 0 0 #000, 0 -1px 0 #000, 0 1px 0 #000, -1px 0 0 #000;
  15.     }
  16.     .textCount {
  17.         color: black;
  18.         font: 28px calibri;
  19.     }
  20. </style>
  21. </svg>
  22.  
  23. <script>
  24.  
  25. var userColors = {
  26.     'SolidGoldMagikarp': '#008080',
  27.     'rschaosid': '#008080',
  28.     'live_mentions': 'Black',
  29.     'joinlivecounting': 'Black',
  30.     'livecounting_sidebar': 'Black',
  31.     'live_lc_bot': 'Black',
  32.     'piyushsharma301': '#FF0F19',
  33.     'Tranquilsunrise': 'Orange',
  34.     'dominodan123': 'Blue',
  35.     'smarvin6689':'#060647',
  36.     'TOP_6689':'#060647',
  37.     'rideride':'#069420',
  38.     'nomaur2':'#8A2BE2',
  39.     'VitaminB16': '#75CEAF',
  40.     'LeinadSpoon': '#520063',
  41.     'LeinadFork': '#520063',
  42.     'LeinadChopsticks': '#520063',
  43.     'Leinadont': '#520063',
  44.     'co3_carbonate': 'Grey',
  45.     'artbn': '#e66b00',
  46.     'amazingpikachu_38': '#FFFF00',
  47.     'qwertylool': "YellowGreen",
  48.     'TOP_20': '#ff00bf',
  49.     '80sFan02': '#0505BB',
  50.     'AstroMagician': '#2d8bff',
  51.     'NobodyL0vesMe': '#730099',
  52.     'Flat-Mars-Society': '#730099',
  53.     'NeonL1vesMatter': '#730099',
  54.     'PrinceCrinkle': '#0d2d89',
  55.     'noduorg':'#0d2d89',
  56.     'yesduorg':'#0d2d89',
  57.     'MrBahr12': '#00f20a',
  58.     'parker_cube': '#FF69B4',
  59.     'QuestoGuy': 'Purple',
  60.     'SubSmartstocks': '#840d0d',
  61.     'Smartstocks': '#840d0d',
  62.     'DemonBurritoCat':'#890003',
  63.     'gordonpt8': '#00FF00',
  64.     'Mooraell': '#DAA520',
  65.     'randomusername123458': '#00CC99',
  66.     'TheNitromeFan': '#fb72b0',
  67.     'Capitanbublo' : '#ff531a',
  68.     'davidjl123': '#6495ED',
  69.     'abplows':'#2B0090',
  70.     'Iamspeedy36': '#00BFFF',
  71.     'Phoenixness': '#ff0000',
  72.     'jillis6': '#ffd700',
  73.     'Kris18': '#0000ff',
  74.     'Chalupa_Dad':'#F08080',
  75.     'Majestic_Bear':'#4682B4',
  76.     'Flat-Mars-Society':'#00FF7F',
  77.     'xHOCKEYx12': 'Lime',
  78.     '_ntrpy': '#FF6600',
  79.     'o99o99': '#2BBDFF',
  80.     'afaintsmellofcurry': '#6799A0',
  81.     'KingCaspianX': '#191970',
  82.     'MewDP': '#FFFF33',
  83.     'DaveTheDave_': '#00BFFF',
  84.     'Luigi86101': '#006400',
  85.     'thetiredlemur': '#464942',
  86.     'TheGlobeIsRound': '#0080ff',
  87.     'CarbonSpectre': '#339933',
  88.     'Lonadont': '#a35252',
  89.     'TehVulpez': '#c42c0a',
  90.     'LC_Chats': '#dddddd',
  91.     'LC-3P0': 'Black',
  92.     'MaybeNotWrong': '#066666',
  93.     'ElliottB1': '#00FFDD',
  94.     'treje': '#ffc130',
  95.     'sakima11': '#0cd1ad',
  96.     'amazingpiakchu_38': '#FFFF00',
  97.     'royalpurplesky': '#800080',
  98.     'Mac_cy': '#FF8C00',
  99.     'iwannaplay5050': '#B22222',
  100.     'ludrol': '#191970',
  101.     'thegreatestminer': '#4F9D82',
  102.     'dogcatcowpig': '#1162f9',
  103.     'ItzTaken': '#32ff95',
  104.     'Noob2137': '#ff69ff',
  105.     'PaleRepresentative': '#8FBC8F',
  106.     'andrewtheredditor': '#2bdb6c',
  107.     'Whit4You': '#ff99ff',
  108.     'Rajalaxo': '#485432',
  109.     'NikinCZ': '#86D8CA',
  110.     'NeodymiumL1vesMatter': '#86D8CA',
  111.     'SecretAsianMa': '#373267',
  112.     'srmpass': '#ffeed6',
  113. };
  114.  
  115. var dataDisplay = [];
  116. var data = {
  117. /*
  118.  * DATA ARE TOO BIG TO FIT ON PASTEBIN {:'-D
  119.  */
  120. };
  121. var step = 0;
  122. var framesPerDatapoint = 20;
  123. var rectSpeed = 3;
  124. var renderInterval;
  125. var totalHeight = 900;
  126. var totalWidth = 1200;
  127. var offsetTop = 100;
  128. var offsetColumn = 50;
  129. var columnHeight = 45;
  130. var timeInit = 1406078000;
  131. var timeStep = 86400;
  132. var topPad = 31;
  133. var increase = (14500000 + 16131) / 14500000;
  134.  
  135. var monthNames = [
  136.   "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
  137. ];
  138.  
  139.  
  140. /*
  141.  * Returns number current closer to the target by speed. If the distance is too big, double the speed for more fluid animation.
  142.  * Used for columns moving up and down
  143.  */
  144. function moveNumberTowards(current, target, speed) {
  145.     if (Math.abs(current - target) > 200)
  146.         speed *= 2;
  147.     if (current < target)
  148.         return current = Math.min(target, current + speed);
  149.     if (current > target)
  150.         return current = Math.max(target, current - speed);
  151.     return target;
  152. }
  153.  
  154. /*
  155.  * Checks if one of the underlying subarrays has <key:needle> pair
  156.  */
  157. function isInArraySubarray(array, needle, key=0) {
  158.     for (var i = 0; i < array.length; i++) {
  159.         if (array[i][key] == needle) {
  160.             return array[i];
  161.         }
  162.     }
  163.     return false;
  164. }
  165.  
  166. // Initiatializes time and count containers
  167. var timeTag = document.createElement("text");
  168. timeTag.setAttribute("class", "title");
  169. timeTag.setAttribute("x", 20);
  170. timeTag.setAttribute("y", 35);
  171. var totalCount = 0;
  172. var totalInc = 0;
  173. var totalTarget = 0;
  174. var countTag = document.createElement("text");
  175. countTag.setAttribute("class", "title");
  176. countTag.setAttribute("x", 20);
  177. countTag.setAttribute("y", 80);
  178.  
  179. /*
  180.  * Called once per <framesPerDatapoint> steps to load new data from <data> object
  181.  */
  182. function introduceNewData() {
  183.     dataI = Math.floor(step / framesPerDatapoint);
  184.     // If there is no more data points
  185.     if (dataI >= data[Object.keys(data)[0]].length) {
  186.         for (var i = 0; i < dataDisplay.length; i++) {
  187.             var dataArr = dataDisplay[i];
  188.             dataArr.counts = dataArr.countTarget;
  189.             dataArr.countInc = 0;
  190.         }
  191.         totalInc = 0;
  192.         return -1;
  193.     }
  194.     // Sets content of the date container
  195.     var date = new Date((timeInit + timeStep * dataI) * 1000);
  196.     timeTag.innerHTML = date.getFullYear() + " " +
  197.             date.getDate() + " " +
  198.             monthNames[date.getMonth()];
  199.     newSets = [];
  200.     var totalNewTarget = 0;
  201.     // Updates display data
  202.     for (var author in data) {
  203.         var counts = data[author][dataI];
  204.         if (counts == 0)
  205.             continue;
  206.         // If person is not in the display array, add and mark for pushing (newSets)
  207.         var dataArr = isInArraySubarray(dataDisplay, author, "author");
  208.         if (dataArr === false) {
  209.             dataArr = {
  210.                 countTarget: 0,
  211.                 author: author
  212.             };
  213.             newSets.push(dataArr);
  214.         }
  215.         // Load new count into display data and recalculate increase per frame
  216.         dataArr.counts = dataArr.countTarget;
  217.         dataArr.countTarget = counts;
  218.         totalNewTarget += counts;
  219.         dataArr.countInc = (counts - dataArr.counts) / framesPerDatapoint;
  220.         // This should be moved to <newSets> loop lower down, idk how have I missed this
  221.         // probably also makes the performance worse
  222.         var rect = document.createElement("rect");
  223.         rect.setAttribute("height", columnHeight);
  224.         rect.setAttribute("width", 1);
  225.         rect.setAttribute("style", "fill:"+(userColors[author] || "Black")+";");
  226.         var uname = document.createElement("text");
  227.         uname.setAttribute("class", "username");
  228.         uname.setAttribute("fill", "white");
  229.         uname.setAttribute("x", 20);
  230.         uname.innerHTML = author === "undefined" ? "[deleted]" : author;
  231.         var tnum = document.createElement("text");
  232.         tnum.setAttribute("class", "textCount");
  233.         dataArr.rect = rect;
  234.         dataArr.uname = uname;
  235.         dataArr.tnum = tnum;
  236.     }
  237.     // Recalculate increase of total counts
  238.     totalCount = totalTarget;
  239.     totalInc = (totalNewTarget - totalCount) / framesPerDatapoint;
  240.     totalTarget = totalNewTarget;
  241.     // Push new sets data to the display data in the right order (important for first few seconds of animation)
  242.     if (newSets.length > 0) {
  243.         newSets.sort(function(a, b) {return b.countInc - a.countInc;});
  244.         for (var i = 0; i < newSets.length; i++) {
  245.             newSets[i].svgY = Math.min(offsetTop + (i + dataDisplay.length) * offsetColumn, totalHeight);
  246.         }
  247.         dataDisplay = dataDisplay.concat(newSets);
  248.     }
  249. }
  250.  
  251. var defaultSvgInner = document.getElementById("chartSvg").innerHTML;
  252.  
  253. /*
  254.  * Renders each frame of the animation
  255.  */
  256. function renderChart() {
  257.     step++;
  258.     if (step % framesPerDatapoint == 0) {
  259.         introduceNewData();
  260.     }
  261.     for (var i = 0; i < dataDisplay.length; i++) {
  262.         var dataArr = dataDisplay[i];
  263.         dataArr.counts += dataArr.countInc;
  264.     }
  265.     dataDisplay.sort(function(a, b) {return b.counts - a.counts});
  266.     chartTags = [];
  267.     for (var i = 0; i < dataDisplay.length; i++) {
  268.         var dataArr = dataDisplay[i];
  269.         // Adjust the column display width, if it's #1 person, set it to max
  270.         // Floating point arithmetics imprecision causes the row to flicker by pixel if we don't do this
  271.         var rowWidth = (i == 0) ?
  272.             1000
  273.         :
  274.             Math.ceil(dataArr.counts * 1000 / dataDisplay[0].counts);
  275.         dataArr.rect.setAttribute("width", rowWidth);
  276.         // Moves the column but makes sure it stays at lowest just under the view, otherwise "new" counters that
  277.         // make it to top15 take infinity to get up and the chart gets ugly
  278.         dataArr.svgY = moveNumberTowards(dataArr.svgY, Math.min(offsetTop + i * offsetColumn, totalHeight), rectSpeed);
  279.         dataArr.rect.setAttribute("y", dataArr.svgY);
  280.        
  281.         // Moves per-column text and updates individual person count display
  282.         dataArr.uname.setAttribute("y", dataArr.svgY + topPad);
  283.        
  284.         dataArr.tnum.setAttribute("x", Math.max(rowWidth + 20, dataArr.author.length <= 15 ? 250 : 290));
  285.         dataArr.tnum.setAttribute("y", dataArr.svgY + topPad);
  286.         dataArr.tnum.innerHTML = Math.round(dataArr.counts);
  287.        
  288.         // Only display columns that are on-screen
  289.         if (dataArr.svgY < totalHeight) {
  290.             chartTags.push(dataArr.tnum.outerHTML);
  291.             chartTags.push(dataArr.uname.outerHTML);
  292.             chartTags.push(dataArr.rect.outerHTML);
  293.         }
  294.     }
  295.     // To trigger svg redraw, we need to change the innerHTML, just doing appendChild or changing properties leaves
  296.     // the svg frame blank
  297.     chartTags.reverse();
  298.     chartTags.push(timeTag.outerHTML);
  299.     totalCount += totalInc;
  300.     countTag.innerHTML = Math.round(totalCount * increase);
  301.     chartTags.push(countTag.outerHTML);
  302.     document.getElementById("chartSvg").innerHTML = defaultSvgInner + "\n" + chartTags.join("\n");
  303. }
  304.  
  305. // Initiate the animation
  306. introduceNewData();
  307. renderInterval = setInterval(renderChart, 1000 / 60);
  308.  
  309. </script>
Advertisement
Add Comment
Please, Sign In to add comment