Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!--
- 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.
- Feel free to edit and reuse for other stuff. Crediting (/u/NikinCZ or NoxZet) is welcome but not needed.
- 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
- Edit 2 April 2019: Added comments
- -->
- <svg id="chartSvg" viewBox="0 0 1200 800" width="1200" height="900" xmlns="http://www.w3.org/2000/svg">
- <style>
- .title { font: 36px calibri; }
- .username {
- font: 28px calibri;
- color: #fff;
- text-shadow: 1px 0 0 #000, 0 -1px 0 #000, 0 1px 0 #000, -1px 0 0 #000;
- }
- .textCount {
- color: black;
- font: 28px calibri;
- }
- </style>
- </svg>
- <script>
- var userColors = {
- 'SolidGoldMagikarp': '#008080',
- 'rschaosid': '#008080',
- 'live_mentions': 'Black',
- 'joinlivecounting': 'Black',
- 'livecounting_sidebar': 'Black',
- 'live_lc_bot': 'Black',
- 'piyushsharma301': '#FF0F19',
- 'Tranquilsunrise': 'Orange',
- 'dominodan123': 'Blue',
- 'smarvin6689':'#060647',
- 'TOP_6689':'#060647',
- 'rideride':'#069420',
- 'nomaur2':'#8A2BE2',
- 'VitaminB16': '#75CEAF',
- 'LeinadSpoon': '#520063',
- 'LeinadFork': '#520063',
- 'LeinadChopsticks': '#520063',
- 'Leinadont': '#520063',
- 'co3_carbonate': 'Grey',
- 'artbn': '#e66b00',
- 'amazingpikachu_38': '#FFFF00',
- 'qwertylool': "YellowGreen",
- 'TOP_20': '#ff00bf',
- '80sFan02': '#0505BB',
- 'AstroMagician': '#2d8bff',
- 'NobodyL0vesMe': '#730099',
- 'Flat-Mars-Society': '#730099',
- 'NeonL1vesMatter': '#730099',
- 'PrinceCrinkle': '#0d2d89',
- 'noduorg':'#0d2d89',
- 'yesduorg':'#0d2d89',
- 'MrBahr12': '#00f20a',
- 'parker_cube': '#FF69B4',
- 'QuestoGuy': 'Purple',
- 'SubSmartstocks': '#840d0d',
- 'Smartstocks': '#840d0d',
- 'DemonBurritoCat':'#890003',
- 'gordonpt8': '#00FF00',
- 'Mooraell': '#DAA520',
- 'randomusername123458': '#00CC99',
- 'TheNitromeFan': '#fb72b0',
- 'Capitanbublo' : '#ff531a',
- 'davidjl123': '#6495ED',
- 'abplows':'#2B0090',
- 'Iamspeedy36': '#00BFFF',
- 'Phoenixness': '#ff0000',
- 'jillis6': '#ffd700',
- 'Kris18': '#0000ff',
- 'Chalupa_Dad':'#F08080',
- 'Majestic_Bear':'#4682B4',
- 'Flat-Mars-Society':'#00FF7F',
- 'xHOCKEYx12': 'Lime',
- '_ntrpy': '#FF6600',
- 'o99o99': '#2BBDFF',
- 'afaintsmellofcurry': '#6799A0',
- 'KingCaspianX': '#191970',
- 'MewDP': '#FFFF33',
- 'DaveTheDave_': '#00BFFF',
- 'Luigi86101': '#006400',
- 'thetiredlemur': '#464942',
- 'TheGlobeIsRound': '#0080ff',
- 'CarbonSpectre': '#339933',
- 'Lonadont': '#a35252',
- 'TehVulpez': '#c42c0a',
- 'LC_Chats': '#dddddd',
- 'LC-3P0': 'Black',
- 'MaybeNotWrong': '#066666',
- 'ElliottB1': '#00FFDD',
- 'treje': '#ffc130',
- 'sakima11': '#0cd1ad',
- 'amazingpiakchu_38': '#FFFF00',
- 'royalpurplesky': '#800080',
- 'Mac_cy': '#FF8C00',
- 'iwannaplay5050': '#B22222',
- 'ludrol': '#191970',
- 'thegreatestminer': '#4F9D82',
- 'dogcatcowpig': '#1162f9',
- 'ItzTaken': '#32ff95',
- 'Noob2137': '#ff69ff',
- 'PaleRepresentative': '#8FBC8F',
- 'andrewtheredditor': '#2bdb6c',
- 'Whit4You': '#ff99ff',
- 'Rajalaxo': '#485432',
- 'NikinCZ': '#86D8CA',
- 'NeodymiumL1vesMatter': '#86D8CA',
- 'SecretAsianMa': '#373267',
- 'srmpass': '#ffeed6',
- };
- var dataDisplay = [];
- var data = {
- /*
- * DATA ARE TOO BIG TO FIT ON PASTEBIN {:'-D
- */
- };
- var step = 0;
- var framesPerDatapoint = 20;
- var rectSpeed = 3;
- var renderInterval;
- var totalHeight = 900;
- var totalWidth = 1200;
- var offsetTop = 100;
- var offsetColumn = 50;
- var columnHeight = 45;
- var timeInit = 1406078000;
- var timeStep = 86400;
- var topPad = 31;
- var increase = (14500000 + 16131) / 14500000;
- var monthNames = [
- "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
- ];
- /*
- * Returns number current closer to the target by speed. If the distance is too big, double the speed for more fluid animation.
- * Used for columns moving up and down
- */
- function moveNumberTowards(current, target, speed) {
- if (Math.abs(current - target) > 200)
- speed *= 2;
- if (current < target)
- return current = Math.min(target, current + speed);
- if (current > target)
- return current = Math.max(target, current - speed);
- return target;
- }
- /*
- * Checks if one of the underlying subarrays has <key:needle> pair
- */
- function isInArraySubarray(array, needle, key=0) {
- for (var i = 0; i < array.length; i++) {
- if (array[i][key] == needle) {
- return array[i];
- }
- }
- return false;
- }
- // Initiatializes time and count containers
- var timeTag = document.createElement("text");
- timeTag.setAttribute("class", "title");
- timeTag.setAttribute("x", 20);
- timeTag.setAttribute("y", 35);
- var totalCount = 0;
- var totalInc = 0;
- var totalTarget = 0;
- var countTag = document.createElement("text");
- countTag.setAttribute("class", "title");
- countTag.setAttribute("x", 20);
- countTag.setAttribute("y", 80);
- /*
- * Called once per <framesPerDatapoint> steps to load new data from <data> object
- */
- function introduceNewData() {
- dataI = Math.floor(step / framesPerDatapoint);
- // If there is no more data points
- if (dataI >= data[Object.keys(data)[0]].length) {
- for (var i = 0; i < dataDisplay.length; i++) {
- var dataArr = dataDisplay[i];
- dataArr.counts = dataArr.countTarget;
- dataArr.countInc = 0;
- }
- totalInc = 0;
- return -1;
- }
- // Sets content of the date container
- var date = new Date((timeInit + timeStep * dataI) * 1000);
- timeTag.innerHTML = date.getFullYear() + " " +
- date.getDate() + " " +
- monthNames[date.getMonth()];
- newSets = [];
- var totalNewTarget = 0;
- // Updates display data
- for (var author in data) {
- var counts = data[author][dataI];
- if (counts == 0)
- continue;
- // If person is not in the display array, add and mark for pushing (newSets)
- var dataArr = isInArraySubarray(dataDisplay, author, "author");
- if (dataArr === false) {
- dataArr = {
- countTarget: 0,
- author: author
- };
- newSets.push(dataArr);
- }
- // Load new count into display data and recalculate increase per frame
- dataArr.counts = dataArr.countTarget;
- dataArr.countTarget = counts;
- totalNewTarget += counts;
- dataArr.countInc = (counts - dataArr.counts) / framesPerDatapoint;
- // This should be moved to <newSets> loop lower down, idk how have I missed this
- // probably also makes the performance worse
- var rect = document.createElement("rect");
- rect.setAttribute("height", columnHeight);
- rect.setAttribute("width", 1);
- rect.setAttribute("style", "fill:"+(userColors[author] || "Black")+";");
- var uname = document.createElement("text");
- uname.setAttribute("class", "username");
- uname.setAttribute("fill", "white");
- uname.setAttribute("x", 20);
- uname.innerHTML = author === "undefined" ? "[deleted]" : author;
- var tnum = document.createElement("text");
- tnum.setAttribute("class", "textCount");
- dataArr.rect = rect;
- dataArr.uname = uname;
- dataArr.tnum = tnum;
- }
- // Recalculate increase of total counts
- totalCount = totalTarget;
- totalInc = (totalNewTarget - totalCount) / framesPerDatapoint;
- totalTarget = totalNewTarget;
- // Push new sets data to the display data in the right order (important for first few seconds of animation)
- if (newSets.length > 0) {
- newSets.sort(function(a, b) {return b.countInc - a.countInc;});
- for (var i = 0; i < newSets.length; i++) {
- newSets[i].svgY = Math.min(offsetTop + (i + dataDisplay.length) * offsetColumn, totalHeight);
- }
- dataDisplay = dataDisplay.concat(newSets);
- }
- }
- var defaultSvgInner = document.getElementById("chartSvg").innerHTML;
- /*
- * Renders each frame of the animation
- */
- function renderChart() {
- step++;
- if (step % framesPerDatapoint == 0) {
- introduceNewData();
- }
- for (var i = 0; i < dataDisplay.length; i++) {
- var dataArr = dataDisplay[i];
- dataArr.counts += dataArr.countInc;
- }
- dataDisplay.sort(function(a, b) {return b.counts - a.counts});
- chartTags = [];
- for (var i = 0; i < dataDisplay.length; i++) {
- var dataArr = dataDisplay[i];
- // Adjust the column display width, if it's #1 person, set it to max
- // Floating point arithmetics imprecision causes the row to flicker by pixel if we don't do this
- var rowWidth = (i == 0) ?
- 1000
- :
- Math.ceil(dataArr.counts * 1000 / dataDisplay[0].counts);
- dataArr.rect.setAttribute("width", rowWidth);
- // Moves the column but makes sure it stays at lowest just under the view, otherwise "new" counters that
- // make it to top15 take infinity to get up and the chart gets ugly
- dataArr.svgY = moveNumberTowards(dataArr.svgY, Math.min(offsetTop + i * offsetColumn, totalHeight), rectSpeed);
- dataArr.rect.setAttribute("y", dataArr.svgY);
- // Moves per-column text and updates individual person count display
- dataArr.uname.setAttribute("y", dataArr.svgY + topPad);
- dataArr.tnum.setAttribute("x", Math.max(rowWidth + 20, dataArr.author.length <= 15 ? 250 : 290));
- dataArr.tnum.setAttribute("y", dataArr.svgY + topPad);
- dataArr.tnum.innerHTML = Math.round(dataArr.counts);
- // Only display columns that are on-screen
- if (dataArr.svgY < totalHeight) {
- chartTags.push(dataArr.tnum.outerHTML);
- chartTags.push(dataArr.uname.outerHTML);
- chartTags.push(dataArr.rect.outerHTML);
- }
- }
- // To trigger svg redraw, we need to change the innerHTML, just doing appendChild or changing properties leaves
- // the svg frame blank
- chartTags.reverse();
- chartTags.push(timeTag.outerHTML);
- totalCount += totalInc;
- countTag.innerHTML = Math.round(totalCount * increase);
- chartTags.push(countTag.outerHTML);
- document.getElementById("chartSvg").innerHTML = defaultSvgInner + "\n" + chartTags.join("\n");
- }
- // Initiate the animation
- introduceNewData();
- renderInterval = setInterval(renderChart, 1000 / 60);
- </script>
Advertisement
Add Comment
Please, Sign In to add comment