tokenChart.js (8871B)
1 import { 2 Chart, 3 Legend, 4 LinearScale, 5 LineController, 6 LineElement, 7 PointElement, 8 TimeSeriesScale, 9 Title, 10 Tooltip 11 } from 'chart.js'; 12 import 'chartjs-adapter-dayjs-4'; 13 14 Chart.register( 15 LineElement, 16 PointElement, 17 LineController, 18 LinearScale, 19 TimeSeriesScale, 20 Legend, 21 Title, 22 Tooltip 23 ) 24 25 import {updateHighVal} from "./highTime"; 26 import {updateLowVal} from "./lowTime"; 27 import {isOverlaySelected, getOverlayTime, setOverlayLabelTime} from "./overlay"; 28 import Datum from "./datum"; 29 30 function lookupTimeUnit(query){ 31 const lookup = { 32 'h': 'day', 33 'd': 'week', 34 'm': 'month', 35 'y': 'month', 36 'l': 'year' 37 } 38 return lookup[query.charAt(query.length - 1)] 39 } 40 41 function timeDeltaInMilliseconds(time) { 42 let timeDigits = (parseInt(time.slice(0, time.length - 1))).toFixed(0); 43 let timeUnit = time.slice(time.length - 1); 44 45 switch (timeUnit) { 46 case 'h': 47 return timeDigits * (60 * 60) * 1000; 48 case 'd': 49 return timeDigits * (24 * 60 * 60) * 1000; 50 case 'm': 51 return (timeDigits * (30.437 * 24 * 60 * 60)).toFixed(0) * 1000; 52 case 'y': 53 return (timeDigits * (365.25 * 24 * 60 * 60)).toFixed(0) * 1000; 54 case 'l': 55 console.warn("This path should not happen, this warning is an error in logic") 56 } 57 } 58 59 export default class TokenChart { 60 constructor() { 61 this._context = document.getElementById("token-chart").getContext('2d'); 62 this._chartActive = false; 63 this._lastDatum = null; 64 this._highDatum = null; 65 this._lowDatum = null; 66 this._lateUpdate = false; 67 } 68 69 get highDatum() { 70 return this._highDatum; 71 } 72 73 get lowDatum() { 74 return this._lowDatum; 75 } 76 77 async #newChart(chartConfig) { 78 this._chart = new Chart(this._context, chartConfig); 79 } 80 81 async #updateHighLow(datum) { 82 if (this._highDatum === null) { 83 this._highDatum = new Datum(datum.getTime(), 0); 84 this._lowDatum = datum; 85 return; 86 } 87 if (datum.getPrice() > this._highDatum.getPrice()) { 88 this._highDatum = datum; 89 updateHighVal(this.highDatum); 90 } 91 else if (datum.getPrice() < this._lowDatum.getPrice()) { 92 this._lowDatum = datum; 93 updateLowVal(this.lowDatum); 94 } 95 } 96 97 async #createOverlayChart(region, time, yLevel, data){ 98 const chartData = []; 99 const overlayData = []; 100 const overlayDelta = timeDeltaInMilliseconds(time); 101 102 for (let i = 0; i < data.length; i++) { 103 const originalDate = data[i].getX(); 104 if (i < (data.length / 2)) { 105 overlayData.push({ 106 x: new Date(originalDate.getTime() + overlayDelta), 107 y: data[i].getY(), 108 }); 109 } 110 else { 111 await this.#updateHighLow(data[i]); 112 113 chartData.push({ 114 x: data[i].getX(), 115 y: data[i].getY(), 116 }) 117 } 118 } 119 120 const chartConfig = { 121 type: 'line', 122 data: { 123 datasets: [ 124 { 125 borderColor: 'gold', 126 label: region.toUpperCase() + " WoW Token Price", 127 data: chartData, 128 cubicInterpolationMode: 'monotone', 129 pointRadius: 0 130 }, 131 { 132 borderColor: 'red', 133 label: `Previous ${getOverlayTime()} ${region.toUpperCase()} WoW Token Price`, 134 data: overlayData, 135 cubicInterpolationMode: 'monotone', 136 pointRadius: 0 137 } 138 ] 139 }, 140 options: { 141 interaction: { 142 intersect: false, 143 mode: "index" 144 }, 145 scales: { 146 x: { 147 type: 'time', 148 grid: { 149 color: '#625f62', 150 }, 151 ticks: { 152 color: '#a7a4ab', 153 font: { 154 size: 18, 155 } 156 }, 157 time: { 158 unit: lookupTimeUnit(time) 159 } 160 }, 161 y: { 162 beginAtZero: yLevel, 163 grid: { 164 color: '#2f2c2f', 165 }, 166 ticks: { 167 color: '#a7a4ab', 168 font: { 169 size: 18, 170 } 171 } 172 } 173 }, 174 } 175 } 176 177 await this.#newChart(chartConfig) 178 } 179 180 async #createNormalChart(region, time, yLevel, data) { 181 const chartData = []; 182 183 184 for (let i = 0; i < data.length; i++) { 185 this._lastDatum = data[i]; 186 await this.#updateHighLow(data[i]); 187 188 chartData.push({ 189 x: data[i].getX(), 190 y: data[i].getY(), 191 }) 192 } 193 194 const chartConfig = { 195 type: 'line', 196 data: { 197 datasets: [{ 198 borderColor: 'gold', 199 label: region.toUpperCase() + " WoW Token Price", 200 data: chartData, 201 cubicInterpolationMode: 'monotone', 202 pointRadius: 0 203 }] 204 }, 205 options: { 206 interaction: { 207 intersect: false, 208 mode: "index" 209 }, 210 scales: { 211 x: { 212 type: 'time', 213 grid: { 214 color: '#625f62', 215 }, 216 ticks: { 217 color: '#a7a4ab', 218 font: { 219 size: 18, 220 } 221 }, 222 time: { 223 unit: lookupTimeUnit(time) 224 } 225 }, 226 y: { 227 beginAtZero: yLevel, 228 grid: { 229 color: '#2f2c2f', 230 }, 231 ticks: { 232 color: '#a7a4ab', 233 font: { 234 size: 18, 235 } 236 } 237 } 238 }, 239 } 240 } 241 242 await this.#newChart(chartConfig) 243 } 244 245 async createChart(region, time, yLevel, data) { 246 let lateUpdateData = this._lastDatum; 247 248 if (isOverlaySelected()) { 249 await this.#createOverlayChart(region, time, yLevel, data) 250 } 251 else { 252 await this.#createNormalChart(region, time, yLevel, data) 253 } 254 255 setOverlayLabelTime(); 256 257 updateHighVal(this.highDatum); 258 updateLowVal(this.lowDatum); 259 260 if (this._lateUpdate) { 261 if (this._lastDatum.getPrice() !== lateUpdateData.getPrice() && 262 this._lastDatum.getTime() < lateUpdateData.getTime()) { 263 await this.addDataToChart(lateUpdateData); 264 } 265 this._lateUpdate = false 266 } 267 268 this._chartActive = true; 269 } 270 271 async destroyChart() { 272 await this._chart.destroy(); 273 this._chartActive = false; 274 this._lastDatum = null; 275 this._highDatum = null; 276 this._lowDatum = null; 277 this._lateUpdate = false; 278 } 279 280 async lateUpdate(datum){ 281 this._lastDatum = datum; 282 this._lateUpdate = true; 283 } 284 285 async addDataToChart(datum) { 286 this._lastDatum = datum; 287 if (datum.getPrice() > this._highDatum.getPrice()) { 288 this._highDatum = datum; 289 updateHighVal(this.highDatum); 290 } 291 else if (datum.getPrice() < this._lowDatum.getPrice()) { 292 this._lowDatum = datum; 293 updateLowVal(this.lowDatum); 294 } 295 this._chart.data.datasets[0].data.push({ 296 x: datum.getX(), 297 y: datum.getY(), 298 }); 299 this._chart.update(); 300 } 301 302 active() { 303 return this._chartActive; 304 } 305 306 toggleYStart(startYAtZero) { 307 this._chart.options.scales.y.beginAtZero = startYAtZero; 308 this._chart.update(); 309 } 310 311 }