From 74c85a4df355ff78377c270370158969ea57ff7e Mon Sep 17 00:00:00 2001 From: Joe Uhren Date: Mon, 9 Oct 2023 19:28:42 -0600 Subject: [PATCH] Chart and graph improvements -jqPlot has been completely removed and replaced with chart.js on all remaining charts and graphs (richlist pie chart and market candlestick chart) -chart.js has been updated to v4.4.0 -chartjs-plugin-crosshair has been updated to v2.0.0 -Added 2 new small libraries to enable the chart.js candlestick chart: chartjs-chart-financial v0.1.1 and chartjs-adapter-luxon v1.3.1 --- README.md | 7 ++- views/layout.pug | 24 +++----- views/market.pug | 143 ++++++++++++++++++++++++++++++++------------- views/richlist.pug | 74 +++++++++++------------ 4 files changed, 148 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 6b41191..eb1cf7d 100644 --- a/README.md +++ b/README.md @@ -76,9 +76,10 @@ Table of Contents - DataTables v1.11.3 - FontAwesome v5.15.4 - Luxon v2.1.1 - - jqPlot v1.0.9 - - Chart.js v3.6.1 - - chartjs-plugin-crosshair v1.2.0 ([https://github.com/abelheinsbroek/chartjs-plugin-crosshair](https://github.com/abelheinsbroek/chartjs-plugin-crosshair)) + - Chart.js v4.4.0 + - chartjs-plugin-crosshair v2.0.0 ([https://github.com/abelheinsbroek/chartjs-plugin-crosshair](https://github.com/abelheinsbroek/chartjs-plugin-crosshair)) + - chartjs-chart-financial v0.1.1 ([https://github.com/chartjs/chartjs-chart-financial](https://github.com/chartjs/chartjs-chart-financial)) + - chartjs-adapter-luxon v1.3.1 ([https://github.com/chartjs/chartjs-adapter-luxon](https://github.com/chartjs/chartjs-adapter-luxon)) - OverlayScrollbars v1.13.3 - flag-icon-css v4.1.4 ([https://github.com/lipis/flag-icon-css](https://github.com/lipis/flag-icon-css)) - Intl.js (uses the v3.111.0 polyfill service to only download if using a browser that doesn't already support the ECMAScript Internationalization API) diff --git a/views/layout.pug b/views/layout.pug index ba503c3..461382a 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -15,8 +15,6 @@ html(lang='en') link(rel='icon', href='/' + settings.shared_pages.favicons.favicon192, sizes='192x192') link(rel='stylesheet', href='/css/themes/' + settings.shared_pages.theme.toLowerCase() + '/bootstrap.min.css' + (themeHash == null ? '' : '?h=' + themeHash)) link(rel='stylesheet', href='https://use.fontawesome.com/releases/v5.15.4/css/all.css') - if active == 'markets' || active == 'richlist' - link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/jquery.jqplot.min.css') if active == 'network' link(rel='stylesheet', href='https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/4.1.4/css/flag-icons.min.css') link(rel='stylesheet', type='text/css', href='https://cdn.datatables.net/v/bs5/dt-1.11.3/datatables.min.css') @@ -26,14 +24,6 @@ html(lang='en') script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js', integrity='sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==', crossorigin='anonymous', referrerpolicy='no-referrer') script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js', integrity='sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p', crossorigin='anonymous') script(type='text/javascript', src='/js/custom.js') - if active == 'markets' || active == 'richlist' - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/jquery.jqplot.min.js') - if active == 'markets' - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.dateAxisRenderer.min.js') - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.ohlcRenderer.min.js') - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.highlighter.min.js') - if active == 'richlist' - script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/jqPlot/1.0.9/plugins/jqplot.pieRenderer.min.js') script(type='text/javascript', src='https://cdn.datatables.net/v/bs5/dt-1.11.3/datatables.min.js') script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/overlayscrollbars/1.13.3/js/jquery.overlayScrollbars.min.js', integrity="sha512-PviP63d43OXLyLjCv3TawK1Rw4LQQsnH6yschHgK63LBvLpd1U1+7LM/OESlV/cSze5lFI3+f7JwKFEBEWNp1w==", crossorigin="anonymous", referrerpolicy="no-referrer") - var showPanels = false @@ -138,10 +128,12 @@ html(lang='en') - showNethashChart = true if settings.error_page.show_difficulty_chart == true - showDifficultyChart = true - if active == 'reward' || (settings.network_history.enabled == true && ((showNethashChart == true && settings.shared_pages.page_header.network_charts.nethash_chart.enabled == true && settings.shared_pages.show_hashrate == true) || (showDifficultyChart == true && settings.shared_pages.page_header.network_charts.difficulty_chart.enabled == true))) - script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/chart.js@3.6.1/dist/chart.min.js') - if settings.network_history.enabled == true && ((showNethashChart == true && settings.shared_pages.page_header.network_charts.nethash_chart.enabled == true && settings.shared_pages.show_hashrate == true) || (showDifficultyChart == true && settings.shared_pages.page_header.network_charts.difficulty_chart.enabled == true)) - script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/chartjs-plugin-crosshair@1.2.0') + if active == 'markets' || active == 'richlist' || active == 'reward' || (settings.network_history.enabled == true && ((showNethashChart == true && settings.shared_pages.page_header.network_charts.nethash_chart.enabled == true && settings.shared_pages.show_hashrate == true) || (showDifficultyChart == true && settings.shared_pages.page_header.network_charts.difficulty_chart.enabled == true))) + script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js') + if active == 'markets' + script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/chartjs-chart-financial@0.1.1/dist/chartjs-chart-financial.min.js') + if active == 'markets' || (settings.network_history.enabled == true && ((showNethashChart == true && settings.shared_pages.page_header.network_charts.nethash_chart.enabled == true && settings.shared_pages.show_hashrate == true) || (showDifficultyChart == true && settings.shared_pages.page_header.network_charts.difficulty_chart.enabled == true))) + script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/chartjs-plugin-crosshair@2.0.0') - var sideBarClasses = []; if settings.shared_pages.page_header.bgcolor != null && settings.shared_pages.page_header.bgcolor != '' - sideBarClasses.push('bg-' + settings.shared_pages.page_header.bgcolor); @@ -440,7 +432,7 @@ html(lang='en') } }, scales: { - yAxis: { + y: { title: { display: true, text: 'Network ' + getNetHashUnits(), @@ -558,7 +550,7 @@ html(lang='en') } }, scales: { - yAxis: { + y: { title: { display: true, text: 'Difficulty', diff --git a/views/market.pug b/views/market.pug index 637c4f4..77b8ab8 100644 --- a/views/market.pug +++ b/views/market.pug @@ -2,6 +2,7 @@ extends layout block content include ./includes/common.pug + script(type='text/javascript', src='https://cdn.jsdelivr.net/npm/chartjs-adapter-luxon@1.3.1') script. $(document).ready(function() { if ('#{settings.markets_page.page_header.show_last_updated}' == 'true') { @@ -169,55 +170,115 @@ block content block market_view script. $(document).ready(function() { - var jqplot = null; var chartData = !{(marketdata.data.chartdata == null || marketdata.data.chartdata == 'null' || marketdata.data.chartdata == '' || marketdata.data.chartdata == '[]' ? 'null' : marketdata.data.chartdata)}; - if (chartData != null && chartData.length > 0) { - jqplot = $.jqplot('chart', [chartData], { - seriesDefaults: { - yaxis: 'y2axis' + if (chartData != null) { + chartData = chartData.map(function(item) { + return { + x: item[0], + o: item[1], + h: item[2], + l: item[3], + c: item[4] + }; + }); + + const backgroundPlugin = { + id: 'backgroundFill', + beforeDraw(chart) { + const ctx = chart.ctx; + ctx.save(); + ctx.fillStyle = '#FFFFFF'; // Change chart background color + const chartArea = chart.chartArea; + ctx.fillRect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top); + ctx.restore(); + } + }; + + Chart.register(backgroundPlugin); + + var ohlcvChart = new Chart('chart', { + type: 'candlestick', + data: { + datasets: [{ + label: 'Market Data', + data: chartData, + borderColor: { + up: 'rgba(80, 160, 115, 1)', + down: 'rgba(215, 85, 65, 1)', + unchanged: 'rgba(90, 90, 90, 1)' + }, + color: { + up: 'rgba(80, 160, 115, 1)', + down: 'rgba(215, 85, 65, 1)', + unchanged: 'rgba(90, 90, 90, 1)' + } + }] }, - axes: { - xaxis: { - renderer: $.jqplot.DateAxisRenderer, - tickOptions: { formatString: '%R' }, - tickInterval: '2 hours' + options: { + maintainAspectRatio: true, + aspectRatio: 3, + scales: { + x: { + type: 'time', + offset: true, + ticks: { + padding: 10 + } + }, + y: { + position: 'right', + grace: '10%', + ticks: { + callback: function(value, index, values) { + return parseFloat(value).toFixed(8); + } + } + } }, - y2axis: { - tickOptions: { formatString: '%.8f' }, - } - }, - series: [ - { - renderer: $.jqplot.OHLCRenderer, - rendererOptions: { - candleStick: true, - upBodyColor: '#2aa3a3', - downBodyColor: '#e2595b', - fillUpBody: true, - fillDownBody: true + plugins: { + legend: { + display: false + }, + tooltip: { + mode: 'nearest', + callbacks: { + title: function(context) { + return format_unixtime(context[0].raw.x / 1000); + }, + beforeBody: function(context) { + const dataset = context[0].dataset; + const data = dataset.data[context[0].dataIndex]; + + return [ + 'Open: ' + data.o.toFixed(8), + 'High: ' + data.h.toFixed(8), + 'Low: ' + data.l.toFixed(8), + 'Close: ' + data.c.toFixed(8) + ]; + }, + label: function(context) { + // Suppress the default body content + return null; + } + } + }, + crosshair: { + line: { + color: '#000000', + width: 1 + }, + sync: { + enabled: false + }, + zoom: { + enabled: false + } } } - ], - highlighter: { - show: true, - showMarker: false, - tooltipAxes: 'xy', - yvalues: 4, - formatString: ' \ - \ - \ - \ - \ -
time:%s
open:%s
hi:%s
low:%s
close:%s
' } }); - $('#chart').css('width', '100%'); } - $(window).resize(function () { - if (jqplot != null && $('#chart').is(':visible')) - jqplot.replot({ resetAxes: false }); - }); }); .row .col-md-12.cardSpacer @@ -351,7 +412,7 @@ block content img.align-top.market-logo(src='data:image/png;base64,' + marketdata.market_logo, title=marketdata.market_name + ' Logo', alt=marketdata.market_name + ' Logo') strong #{marketdata.market_name} - #{marketdata.coin}/#{marketdata.exchange} - #{settings.locale.mkt_hours} .card-body - div#chart(style='height:300px;') + canvas#chart(style='height:300px;') .row .col-md-6.col-xs-12.cardSpacer .card.card-default.border-0.wrapper-border-0(class=theadClasses) diff --git a/views/richlist.pug b/views/richlist.pug index f215720..8be9fa3 100644 --- a/views/richlist.pug +++ b/views/richlist.pug @@ -6,39 +6,42 @@ block content if settings.richlist_page.wealth_distribution.show_distribution_chart == true script. $(document).ready(function() { - var data = [ - ['Top 1-25', !{dista.percent}], - ['Top 26-50', !{distb.percent}], - ['Top 51-75', !{distc.percent}], - ['Top 76-100', !{distd.percent}], - ['101+', !{diste.percent}] - ]; + var xValues = ['Top 1-25', 'Top 26-50', 'Top 51-75', 'Top 76-100', '101+']; + var yValues = [!{dista.total}, !{distb.total}, !{distc.total}, !{distd.total}, !{diste.total}]; + var percentages = [!{dista.percent}, !{distb.percent}, !{distc.percent}, !{distd.percent}, !{diste.percent}]; var burned = '!{burned}'; - if ('#{settings.richlist_page.burned_coins.include_burned_coins_in_distribution}' == 'true' && burned != 'null' && burned != '' && burned != '0') - data.push(['Burned Coins', parseFloat(((burned / 100000000) / !{stats.supply}) * 100)]); - var piePlot = $.jqplot('pieChart', [data], - { - seriesColors: !{JSON.stringify(settings.richlist_page.wealth_distribution.colors)}, - series: [{ - renderer: $.jqplot.PieRenderer, - rendererOptions: { - diameter: 260, - padding: 0, - sliceMargin: 4, - showDataLabels: false - } - }], - grid: {borderWidth:0, shadow:false}, - legend: { - show: false, - rendererOptions: { - numberRows: 1, - border: 'none' + + if ('#{settings.richlist_page.burned_coins.include_burned_coins_in_distribution}' == 'true' && burned != 'null' && burned != '' && burned != '0') { + xValues.push('Burned Coins'); + yValues.push(burned / 100000000); + percentages.push(Number(parseFloat(((burned / 100000000) / !{stats.supply}) * 100).toFixed(2))); + } + + new Chart("pieChart", { + type: "pie", + data: { + labels: xValues, + datasets: [{ + backgroundColor: !{JSON.stringify(settings.richlist_page.wealth_distribution.colors)}, + data: yValues + }] + }, + options: { + responsive: true, + plugins: { + legend: { + display: false }, - location: 's' + tooltip: { + callbacks: { + label: function(context) { + return `${context.label}: ${Number((context.parsed || 0)).toLocaleString('en',{'minimumFractionDigits':0,'maximumFractionDigits':20,'useGrouping':true})} (${percentages[xValues.indexOf(context.label)]}%)`; + } + } + } } - } - ); + } + }); if ('#{settings.richlist_page.page_header.show_last_updated}' == 'true') { var lastUpdatedDate = #{(last_updated == null || last_updated == '0' ? 0 : last_updated)}; @@ -55,15 +58,6 @@ block content } if (#{settings.shared_pages.page_header.page_title_image.enable_animation} == true && #{settings.richlist_page.page_header.show_img} == true) startRotateElement('img#header-img'); - $(window).resize(function () { - if (piePlot != null && $('#pieChart').is(':visible')) { - let parentWidth = $('#pieChart').parent().outerWidth(); - - $('#pieChart').css('height', (parentWidth <= 300 ? parentWidth + 5 : 305).toString() + 'px'); - $('#pieChart').css('width', (parentWidth <= 300 ? parentWidth : 300).toString() + 'px'); - piePlot.replot({ resetAxes: false }); - } - }); }); - var theadClasses = []; if settings.shared_pages.table_header_bgcolor != null && settings.shared_pages.table_header_bgcolor != '' @@ -279,4 +273,4 @@ block content td.fw-bold.text-center #{percentParts[0]}. span.decimal #{percentParts[1]} if settings.richlist_page.wealth_distribution.show_distribution_chart == true - div#pieChart(style="max-width:300px;max-height:305px;margin:0 auto;") \ No newline at end of file + canvas#pieChart(style="margin-top:10px;max-height:305px;") \ No newline at end of file