I want to get the effect of something like this:
And here is my best attempt as a combo graph:
The problem is I need to vastly ramp up the number of bars in this chart. However, when I try to do this the bars disappear.
Here is my code as a typescript reactjs setup:
import './App.css';
import React from 'react';
import { Bar, Line } from 'react-chartjs-2';
const createRandomFollowersData = () => {
const maxDate = new Date();
const minDate = new Date(maxDate.valueOf() - 5 * 365 * 24 * 60 * 60 * 1000);
const dataPoints = Array.from({ length: 500 }).map(() => ({
timestamp: new Date(
Math.floor(Math.random() * (maxDate.valueOf() - minDate.valueOf())) +
minDate.valueOf()
).toISOString(),
followers: Math.floor(Math.random() * 1000000) + 0,
}));
return dataPoints.sort(
(a, b) => new Date(a.timestamp).valueOf() - new Date(b.timestamp).valueOf()
);
};
const createRandomAssetData = () => {
const maxDate = new Date();
const minDate = new Date(maxDate.valueOf() - 5 * 365 * 24 * 60 * 60 * 1000);
const dataPoints = Array.from({ length: 500 }).map(() => ({
timestamp: new Date(
Math.floor(Math.random() * (maxDate.valueOf() - minDate.valueOf())) +
minDate.valueOf()
).toISOString(),
price: Math.floor(Math.random() * 45) + 1,
}));
return dataPoints.sort(
(a, b) => new Date(a.timestamp).valueOf() - new Date(b.timestamp).valueOf()
);
};
const followersData = createRandomFollowersData();
const yAxisFollowers = {
type: 'linear',
id: 'followers',
};
const yAxisDelta = {
type: 'linear',
position: 'right',
id: 'change',
};
const yAxisRank = {
type: 'linear',
id: 'rank',
ticks: {
reverse: true,
},
};
const yAxisAssets = {
type: 'linear',
position: 'right',
id: 'assets',
};
const selectChartAxes = (
containsFollowers: boolean,
containsRank: boolean,
showDelta: boolean,
showAssets: boolean
) => {
const yAxes = [];
if (containsFollowers) yAxes.push(yAxisFollowers);
if (containsRank) yAxes.push(yAxisRank);
if (showDelta) yAxes.push(yAxisDelta);
if (showAssets) yAxes.push(yAxisAssets);
return yAxes;
};
const decimateChart = (
data: {
t: Date;
y: number;
}[],
numBuckets: number,
startDate?: Date,
endDate?: Date
) => {
if (!startDate) {
startDate = data[0].t;
}
if (!endDate) {
endDate = data[data.length - 1].t;
}
// create evenly spaced dates
const dt = endDate.valueOf() - startDate.valueOf();
const startValue = startDate.valueOf();
const spacedDates = Array.from({ length: numBuckets + 1 }).map((_, idx) => {
return new Date(startValue + (idx * dt) / numBuckets);
});
// make buckets
const buckets = Array.from({ length: numBuckets + 2 }).map(() => []) as {
t: Date;
y: number;
}[][];
const filteredData = data.filter(
(e) => e.t >= spacedDates[0] && e.t <= spacedDates[spacedDates.length - 1]
);
// place data into buckets
let jdx = 0;
spacedDates.forEach((e, idx) => {
for (; jdx < filteredData.length; ) {
const e = filteredData[jdx];
const date = new Date(e.t);
if (date >= spacedDates[idx] && date <= spacedDates[idx + 1]) {
buckets[idx].push({
t: date,
y: e.y,
});
++jdx;
} else break;
}
});
// one plot per bucket
return buckets.map((bucket, idx) => {
const date = spacedDates[idx];
if (bucket.length === 0) {
return {
t: date,
y: NaN,
};
}
return bucket[bucket.length - 1];
});
};
const chartMappedFollowersData = followersData.map((followerData) => ({
t: new Date(followerData.timestamp),
y: followerData.followers,
}));
// const decimatedData = decimateChart(chartMappedFollowersData, 75);
const decimatedData = decimateChart(chartMappedFollowersData, 75).map(
(e, idx) => {
if (idx > 1 && idx < 10) {
return {
t: e.t,
y: NaN,
};
}
if (idx > 30 && idx < 45) {
return {
t: e.t,
y: NaN,
};
}
return e;
}
);
const decimatedDataToBars = (
data: {
t: Date;
y: number;
}[]
) => {
if (data.length < 2) {
return {
t: data[0].t,
y: data[0].y,
};
}
const bars = [];
const indexedData = data.map((e, idx) => ({
...e,
idx,
}));
const filteredIndexedData = indexedData.filter((e) => !isNaN(e.y));
for (let idx = 0; idx < filteredIndexedData.length - 1; ++idx) {
const dt = data[idx + 1].t.valueOf() - data[idx].t.valueOf();
for (
let idy = 0;
idy < filteredIndexedData[idx + 1].idx - filteredIndexedData[idx].idx;
++idy
) {
const t = new Date(filteredIndexedData[idx].t.valueOf() + idy * dt);
const deltaY =
(filteredIndexedData[idx + 1].y - filteredIndexedData[idx].y) /
(filteredIndexedData[idx + 1].idx - filteredIndexedData[idx].idx);
bars.push({
t,
y: deltaY,
});
}
}
return bars;
};
const chartOptionsLinear = {
scales: {
yAxes: selectChartAxes(true, false, true, true),
xAxes: [
{
type: 'time',
time: {
unit: 'day',
displayFormats: { day: 'MMM DD, Y' },
min: chartMappedFollowersData[0].t,
max: chartMappedFollowersData[chartMappedFollowersData.length - 1].t,
},
ticks: {
source: 'labels',
},
},
],
maintainAspectRatio: false,
},
};
const chartData = {
labels: decimatedData.map((e) => e.t).filter((_, idx) => idx % 3 === 0),
datasets: [
{
yAxisID: 'followers',
cubicInterpolationMode: 'monotone',
backgroundColor: 'rgb(54, 162, 235)',
borderColor: 'rgb(88, 88, 88)',
fill: false,
type: 'line',
label: 'followers',
spanGaps: true,
data: decimatedData,
},
{
yAxisID: 'change',
type: 'bar',
backgroundColor: 'rgb(235, 54, 162)',
label: 'delta',
data: decimatedDataToBars(decimatedData),
barThickness: 1,
},
],
};
function App(): JSX.Element {
return (
<div style={{ margin: '1em' }}>
<Bar data={chartData} options={chartOptionsLinear} />
</div>
);
}
export default App;
If you swap out data: decimatedDataToBars(decimatedData), to data: decimatedDataToBars(chartMappedFollowersData), you can see the effect; The bars disappear. Does anyone have any insight into this problem and how I can fix it?
So the issue was a bug in 2.8.0 that caused the bars to not show. Upgrading to 2.9.4 fixed the issue for me (but broke some other functionality of why I was using 2.8.0 in the first place.)
Related
Actual behaviour Now a series of all days of the week is being built on the X-axis. like now: 2022-5-30 2022-5-31 2022-6-1 2022-6-2 2022-6-3 2022-6-4 2022-6-5 2022-6-6
Live demo with steps to reproduce
https://jsfiddle.net/5z1b9xnp/
let globalData = []; let finRez = [];
let chart;
let duration = 500; // Determines how long the animation between new points should be take
let startIterator = 1; // Determines how many points will be rendered on chart's init
let currentIterator = startIterator;
let maxIterator = 1;
let guiButton = document.getElementById('start');
let guiButtonState = 'Start';
let intervalId;
// Fetch data:
fetch('https://raw.githubusercontent.com/veleg/trade/main/trade.json')
.then(response => response.json())
.then(data => {
parseData(data);
createChart();
initEvents();
});
function initEvents() {
guiButton.addEventListener('click', function() {
if (guiButtonState === 'Stop') {
// User clicked "Stop" -> stop animation and allow to resume
intervalId = clearInterval(intervalId);
guiButton.innerText = guiButtonState = 'Resume';
} else {
// If animation has finished, recreate chart
if (guiButtonState === 'Restart') {
createChart();
}
guiButton.innerText = guiButtonState = 'Stop';
// Start animation:
redrawChart(currentIterator += 1);
intervalId = setInterval(function() {
// If we reached last available point, stop animation:
if (currentIterator === maxIterator) {
intervalId = clearInterval(intervalId);
currentIterator = startIterator;
guiButton.innerText = guiButtonState = 'Restart';
} else {
redrawChart(currentIterator += 1);
}
}, duration);
}
});
}
function redrawChart(index) {
// Set new subtitle on every redraw
chart.setTitle(null, {
text: ' Заработано: ' + globalData[0].data[index][1]
}, false);
const newValues = globalData.map(series => series.data[index][1]);
const maxIndex = newValues.indexOf(Math.max.apply(null, newValues));
// To each series, add a point:
chart.series.forEach(
(series, seriesIndex) => {
const enabled = maxIndex === seriesIndex && ((index < 5) || (index % 5 === 0));
series.addPoint(
{
x: globalData[seriesIndex].data[index][0],
y: globalData[seriesIndex].data[index][1]
<!-- dataLabels: { -->
<!-- enabled -->
<!-- }, -->
<!-- marker: { -->
<!-- enabled -->
<!-- } -->
},
false,
false,
false
);
}
);
// Now, once everything is updated, redraw chart:
chart.redraw({
duration
});
}
function parseData(data) {
Highcharts.objectEach(
data,
// Prepare Highcharts data-format:
// series: [{
// data: [ [x, y], [x, y], ..., [x, y]]
// }]
(countryData, country) => {
if (country !== 'Diamond Princess' && country !== 'Holy See') {
globalData.push({
name: country,
data: countryData.map(p => [Date.parse(p.date), p.recovered / getCountryPopulation(country)])
<!-- data: countryData.map(p => [p.date, p.recovered / getCountryPopulation(country)]), -->
<!-- datas: countryData.date, -->
<!-- datass: countryData.map(p => [p.date, p.date]) -->
});
}
}
);
// Sort and limit dataset:
globalData = globalData
.sort((countryA, countryB) => {
let countryALen,
countryBLen;
if (!countryA || !countryA.data || countryA.data.length === 0) {
return 1;
}
if (!countryB || !countryB.data || countryB.data.length === 0) {
return -1;
}
return countryB.data[countryB.data.length - 1][1] - countryA.data[countryA.data.length - 1][1];
})
.splice(0, 8);
maxIterator = Math.max.apply(null, globalData.map(series => series.data.length - 1));
}
function createChart() {
function format(y) {
return y.toFixed(2);
}
chart = Highcharts.chart('container', {
chart: {
type: 'spline',
marginLeft: 100
},
legend: {
layout: 'proximate',
align: 'right'
},
title: {
floating: true,
align: 'left',
x: 93,
y: 20,
text: 'Confirmed cases per country per 1000 inhabitants'
},
subtitle: {
floating: true,
align: 'left',
y: 60,
x: 90,
text: ' Заработано: ' + globalData[0].data[0][1],
style: {
fontSize: '20px'
}
},
tooltip: {
split: true,
pointFormatter: function() {
<!-- var value = format(this.y); -->
<!-- return `<span style="color:${this.color}">●</span> ${this.series.name}: <b>${value}</b><br/>`; -->
}
},
yAxis: {
title: {
text: ''
},
maxPadding: 0.2,
softMax: 1
},
xAxis: {
gridLineWidth: 2,
min: globalData[0].data[0][0],
minRange: 7 * 24 * 3600 * 1000,
type: 'datetime'
<!-- categories: [1] -->
},
plotOptions: {
series: {
animation: {
duration
},
<!-- marker: { -->
<!-- symbol: 'circle' -->
<!-- }, -->
dataLabels: {
formatter: function() {
return format(this.y);
}
}
}
},
series: globalData.map(series => {
return {
name: series.name,
data: series.data.slice(0, startIterator).map(point => {
return { x: point[0], y: point[1] }
})
}
})
});
}
function getCountryPopulation(country) {
return {
"Заработано": 1,
"Финансовый результат": 1
}[country];
}
Product version Highcharts JS v10.3.2 (2022-11-28)
It is necessary to skip days on the X axis that are not in the JSON file. need: 2022-5-30 2022-5-31 2022-6-1 2022-6-2 2022-6-3 2022-6-6
I don't need to filter JSON. The JSON file just indicates what I need, but the script itself substitutes dates that are not in JSON.
Be careful, dates are skipped in JSON, and the library inserts missing dates. I need only those dates that are specified in JSON, it can be weekends or two days a week.
thanks for anybody reading me here ,
I have got this code from my prof and I want ,need to calculate automatically the sum , the total of the players in the html I mean being displayed.
I'm really new to this , can anybody help me out ?
Want to understand The Vue.js math functions in order to progress in my studies and in math as well.
Last..so full of doubt here , sorry guys , Vue.js is a js library ..so I cannot put a js script inside vue.js right ?
Thanks
var players = [
{ label: 'A', value: 100 },
{ label: 'B', value: 100 },
{ label: 'C', value: 100 },
{ label: 'D', value: 100 },
{ label: 'E', value: 100 },
{ label: 'F', value: 100 }
]
Vue.component("polygraph", {
template: "#polygraphComponent",
props: ["stats", "width"],
computed: {
radius() { return (this.width / 2); },
viewbox() {
var size = this.width;
var min = -(size / 2);
return [min, min, size, size].join(" ");
},
points() {
var total = this.stats.length
return this.stats.map((stat, i) => {
var point = valueToPoint(stat.value, i, total)
return point.x + "," + point.y
}).join(" ")
}
},
components: {
axisLabel: {
template: "#axisLabelComponent",
props: {
stat: Object,
index: Number,
total: Number
},
computed: {
point() {
return valueToPoint(
this.stat.value,
this.index,
this.total
)
}
}
}
}
})
new Vue({
el: "#app",
data: {
newLabel: "",
players: players,
width: 600
},
methods: {
add(evt) {
evt.preventDefault()
if (!this.newLabel) return
this.players.push({
label: this.newLabel,
value: 100
})
this.newLabel = ""
},
remove(stat) {
if (this.players.length > 3) {
this.players.$remove(stat)
} else {
alert("Can't remove more!")
}
}
},
components: {
scoreControl: {
template: "#scoreControlComponent",
props: {
stat: Object,
index: Number,
total: Number
},
computed: {
point() {
return valueToPoint(
this.stat.value,
this.index,
this.total
)
},
style() {
var angle = this.point.angle;
return {
transform: "rotate(" + angle + "rad)"
}
}
}
}
}
})
// math helper...
function valueToPoint(value, index, total) {
var maxV = 100
var maxR = 300 * 0.9
var r = maxR / maxV * value
var angle = Math.PI * 2 / total * index + Math.PI / 2
var cos = Math.cos(angle)
var sin = Math.sin(angle)
var tx = r * cos
var ty = r * sin
return {
angle: angle,
radius: r,
x: tx,
y: ty
}
}
So I have ChartData Component. I am taking the data from an API, the data is being displayed through Chart. I have determine Format Logic, which it's the main thing is to determine the time of the data, I have 3 buttons, the thing that I am struggling to achieve is when let's say I wanna see the data of 7days when I press the button. I can see data is changing in the yAxes but I can't see time being changed on thexAxes, it's still set to hours, it should display the days.
ChartData
import React, { useRef, useEffect, useState } from "react";
import 'chartjs-adapter-moment';
import annotationPlugin from 'chartjs-plugin-annotation';
import { Chart, registerables } from 'chart.js';
Chart.register(...registerables);
Chart.register(annotationPlugin);
const determineTimeFormat = (
timeFormat: string,
day: any,
week: any,
year: any
) => {
switch (timeFormat) {
case "24h":
return day;
case "7d":
return week;
case "1y":
return year;
default:
return day;
}
};
interface Props {
data: any
}
const ChartData: React.FC<Props> = ({ data }) => {
const chartCanvasRef = useRef<HTMLCanvasElement | null>(null);
const { day, week, year, detail } = data;
const [timeFormat, setTimeFormat] = useState("24h");
const [isRebuildingCanvas, setIsRebuildingCanvas] = useState(false);
useEffect(() => {
setIsRebuildingCanvas(true);
}, [timeFormat]);
useEffect(() => {
if (isRebuildingCanvas) {
setIsRebuildingCanvas(false);
}
}, [isRebuildingCanvas]);
useEffect(() => {
if (chartCanvasRef && chartCanvasRef.current && detail) {
const chartCanvas = chartCanvasRef.current
if (isRebuildingCanvas || !chartCanvas) {
return;
}
const chartInstance = new Chart(chartCanvasRef.current, {
type: "line",
data: {
datasets: [
{
label: `${detail.name} price`,
data: determineTimeFormat(timeFormat, day, week, year),
backgroundColor: "rgba(134,159,152, 1)",
borderColor: "rgba(174, 305, 194, 0.4",
},
],
},
Options
options: {
plugins: {
annotation: {
annotations: {
}
}
},
animations: {
tension: {
duration: 1000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
},
maintainAspectRatio: false,
responsive: true,
scales: {
x:
{
type: 'time',
},
},
}
});
return () => {
chartInstance.destroy();
}
}}, [day, isRebuildingCanvas,timeFormat, week, year, detail]);
Rest of the Component
return (
<div className='chart__container'>
{renderPrice()}
{isRebuildingCanvas ? undefined : (
<canvas ref={chartCanvasRef} width={250} height={250} id='myChart'></canvas>
)}
<button className='time__format' onClick={() => setTimeFormat("24h")}>24h</button>
<button className='time__format' onClick={() => setTimeFormat("7d")}>7d</button>
<button className='time__format' onClick={() => setTimeFormat("1y")}>1y</button>
</div>
);
};
export default ChartData;
I have created a similar working example without reactjs. This can be useful for you to understand. You can refer the fiddle as well. A limitation that am currently facing is if there is data more than 100 then am getting errors while updating the data, performed slicing of data upto 100 to make it working.
var coinData = {};
var fetchData = async() => {
api = "https://api.coingecko.com/api/v3";
id = "bitcoin";
var [day1, week1, year1, detail1] = await Promise.all([
fetch(api + `/coins/${id}/market_chart/?vs_currency=usd&days=1`).then(data => {
return data.json()
}),
fetch(api + `/coins/${id}/market_chart/?vs_currency=usd&days=7`).then(data => {
return data.json()
}),
fetch(api + `/coins/${id}/market_chart/?vs_currency=usd&days=365`).then(data => {
return data.json()
}),
fetch(api + `/coins/markets/?vs_currency=usd&ids=${id}`).then(data => {
return data.json()
})
]);
coinData = {
day: formatData(day1.prices).slice(0, 100),
week: formatData(week1.prices).slice(0, 100),
year: formatData(year1.prices).slice(0, 100),
detail: detail1[0]
}
}
const formatData = (data) => {
return data.map((el) => {
return {
t: el[0],
y: el[1].toFixed(2)
};
});
};
const determineTimeFormat = (timeFormat) => {
switch (timeFormat) {
case "24h":
return coinData.day;
case "7d":
return coinData.week;
case "1y":
return coinData.year;
default:
return coinData.day;
}
};
var chartInstance;
fetchData().then(() => {
/* console.log(coinData); */
var ctx = document.getElementById('chartJSContainer');
chartInstance = new Chart(ctx, {
type: "line",
labels: false,
data: {
datasets: [{
label: `${coinData.detail.name} price`,
data: determineTimeFormat('1d'),
parsing: {
yAxisKey: 'y',
xAxisKey: 't',
},
backgroundColor: "rgba(134,159,152, 1)",
borderColor: "rgba(174, 305, 194, 0.4)"
}],
},
options: {
scales: {
x: {
ticks: {
source: "data"
},
type: 'time',
time: {
unit: "day"
}
}
},
}
});
});
function setTimeFormat(format) {
Chart.instances[0].data.datasets[0].data = determineTimeFormat(format);
Chart.instances[0].update();
}
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.3.1/dist/chart.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment#1.0.0/dist/chartjs-adapter-moment.min.js"></script>
<body>
<button className='time__format' onclick="setTimeFormat( '24h')">24h</button>
<button className='time__format' onclick="setTimeFormat( '7d')">7d</button>
<button className='time__format' onclick="setTimeFormat( '1y')">1y</button><br><br>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
</body>
When I dragged the datapoint out of chart's ticks limit like from max x and y axis value, the canvas increase the limits too fast. How can I control this scaling speed? So that it increase with specific number defined in the config of chart.
Here is the js fiddle link.
https://jsfiddle.net/rz7pw6j0/67/
JS
(function () {
let chartInstance;
let chartElement;
function createChart(chartElement) {
const chartConfig = {
type: 'scatter',
data: {
datasets: [
{
data: [
{
x: 0,
y:0
}
],
fill: true,
showLine: true,
lineTension: 0
}
]
},
options: {
legend: {
display: false,
},
layout: {
padding: {
left: 50,
right: 50,
top: 0,
bottom: 0
}
},
title: {
display: false,
text: 'Chart.js Interactive Points',
},
scales: {
xAxes: [
{
type: 'linear',
display: true,
padding: 20,
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
scaleLabel: {
display: true,
labelString: 'Time',
},
ticks: {
suggestedMin: -5,
suggestedMax: 5,
stepValue: 1,
}
}
],
yAxes: [
{
type: 'linear',
display: true,
scaleLabel: {
display: true,
labelString: 'Weight'
},
paddingLeft: 10,
paddingRight: 10,
paddingTop: 10,
paddingBottom: 10,
ticks: {
suggestedMin: 0,
suggestedMax: 3,
stepValue: 1
},
}
]
},
responsive: true,
maintainAspectRatio: true,
tooltips: {
intersect: true,
}
}
};
chartInstance = new Chart(chartElement.getContext('2d'), chartConfig);
let element = null;
let scaleY = null;
let scaleX = null;
let datasetIndex = null;
let index = null;
let valueY = null;
let valueX = null;
function onGetElement() {
const event = d3.event.sourceEvent;
element = chartInstance.getElementAtEvent(event)[0];
if (!element) {
chartClickHandler(event);
return;
}
if (event.shiftKey) {
const tempDatasetIndex = element['_datasetIndex'];
const tempIndex = element['_index'];
chartInstance.data.datasets[tempDatasetIndex].data = chartInstance
.data.datasets[tempDatasetIndex].data.filter((v, i) => i !== tempIndex);
chartInstance.update();
return;
}
scaleY = element['_yScale'].id;
scaleX = element['_xScale'].id;
}
function onDragStart() {
const event = d3.event.sourceEvent;
datasetIndex = element['_datasetIndex'];
index = element['_index'];
valueY = chartInstance.scales[scaleY].getValueForPixel(event.offsetY);
valueX = chartInstance.scales[scaleX].getValueForPixel(event.offsetX);
chartInstance.data.datasets[datasetIndex].data[index] = {
x: valueX,
y: valueY
};
chartInstance.update(0);
}
function onDragEnd() {
if (
chartInstance.data.datasets[datasetIndex] &&
chartInstance.data.datasets[datasetIndex].data) {
chartInstance.data.datasets[datasetIndex].data.sort((a, b) => a.x - b.x > 0 ? 1 : -1);
chartInstance.update(0);
}
element = null;
scaleY = null;
scaleX = null;
datasetIndex = null;
index = null;
valueY = null;
valueX = null;
}
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', onGetElement)
.on('drag', onDragStart)
.on('end', onDragEnd)
);
}
function chartClickHandler (event) {
let scaleRef;
let valueX = 0;
let valueY = 0;
Object.keys(chartInstance.scales).forEach((scaleKey) => {
scaleRef = chartInstance.scales[scaleKey];
if (scaleRef.isHorizontal() && scaleKey === 'x-axis-1') {
valueX = scaleRef.getValueForPixel(event.offsetX);
} else if (scaleKey === 'y-axis-1') {
valueY = scaleRef.getValueForPixel(event.offsetY);
}
});
const newPoint = {
x: valueX,
y: valueY
};
const dataSeries = chartInstance.data.datasets[0].data;
for (let i = 0; i < dataSeries.length; i++) {
if (dataSeries[i].x === newPoint.x) {
dataSeries.y = newPoint.y;
chartInstance.update();
return;
}
}
let inserted = false;
for (let j = dataSeries.length - 1; j >= 0; j--) {
if (dataSeries[j].x > newPoint.x) {
dataSeries[j + 1] = dataSeries[j];
} else {
dataSeries[j + 1] = newPoint;
inserted = true;
break;
}
}
if (!inserted) {
dataSeries.push(newPoint);
}
chartInstance.update();
}
chartElement = document.getElementById("chart");
createChart(chartElement);
})();
HTML
<body>
<div>Chart Drag and Click Test</div>
<div class="wrapper">
<canvas id="chart"></canvas>
</div>
</body>
CSS
.wrapper {
display: "block";
}
I want to control the scaling speed.
If a dragStart event occurs beyond the scale limits, the increment should be a fixed value to avoid the issue you mentioned. Also, ticks.min and ticks.max should be set for the same purpose. Below is a sample jsfiddle and code (you can control speed by step).
https://jsfiddle.net/oLrk3fb2/
function onDragStart() {
const event = d3.event.sourceEvent;
const scales = chartInstance.scales;
const scaleInstanceY = scales[scaleY];
const scaleInstanceX = scales[scaleX];
const scalesOpts = chartInstance.options.scales;
const ticksOptsX = scalesOpts.xAxes[0].ticks;
const ticksOptsY = scalesOpts.yAxes[0].ticks;
const step = 1;
datasetIndex = element['_datasetIndex'];
index = element['_index'];
valueY = scaleInstanceY.getValueForPixel(event.offsetY);
valueX = scaleInstanceX.getValueForPixel(event.offsetX);
if (valueY < scaleInstanceY.min) {
ticksOptsY.min = valueY = scaleInstanceY.min - step;
}
if (valueY > scaleInstanceY.max) {
ticksOptsY.max = valueY = scaleInstanceY.max + step;
}
if (valueX < scaleInstanceX.min) {
ticksOptsX.min = valueX = scaleInstanceX.min - step;
}
if (valueX > scaleInstanceX.max) {
ticksOptsX.max = valueX = scaleInstanceX.max + step;
}
chartInstance.data.datasets[datasetIndex].data[index] = {
x: valueX,
y: valueY
}
chartInstance.update(0);
}
I have a map object: As you can see it is nesting by year-month-day.
I would like to create a bar chart where you can see those numbers for "Keszeg", "Ponty"..etc based on the year-month-day.
My code is ready but i can't get it working. At this part i am getting undefinied error for the yearVal.entries.
This is the sulyhavonta.entries:
for (let [yearKey, yearVal] of sulyhavonta.entries()) {
for (let [monthKey, monthVal] of yearVal.entries())
let labels = [];
let datasets = [];
let fishData = {};
this.firebasedb.list("/fogasok/").subscribe(_data => {
// this.osszesfogasadatok = _data.filter(item => item.publikus == true);
// let fogasSzam=this.osszesfogasadatok.length;
let sulySum = _data.reduce((sum, item) => sum + parseInt(item.suly), 0);
let sulySumMap = _data.map((item, index) => {
var n = new Date(item.datum);
return {
ev: n.getFullYear(),
honap: n.getMonth() + 1,
nap: n.getDate(),
suly: item.suly,
halfaj: item.halfaj,
eteto: item.etetoanyag1,
csali: item.hasznaltcsali,
helyszin: item.helyszin
}
});
var sulySumByDate = d3.nest()
.key(function (d) {
return d.ev;
})
.key(function (d) {
return d.honap;
})
.key(function (d) {
return d.nap;
})
.key(function (d) {
return d.halfaj;
})
.rollup(function (values) {
return d3.sum(values, function (d) {
return parseInt(d.suly);
});
})
.map(sulySumMap)
var sulyhavonta=sulySumByDate;
console.log("sulyhavonta",sulyhavonta)
for (let [yearKey, yearVal] of sulyhavonta.entries()) {
for (let [monthKey, monthVal] of yearVal.entries()) {
for (let [dayKey, dayVal] of monthVal.entires()) {
labels.push(yearKey + '.' + monthKey + '.' + dayKey);
for (let [fish, fishVal] of dayVal.entires()) {
if (fishData[fish] === undefined) {
fishData[fish] = [];
}
fishData[fish].push(fishVal);
console.log("fishdata",fishData);
}
}
}
}
var colors = [
["#ce8d00", "#ffae00"],
["#007bce", "#84ceff"]
];
var i = 0;
for (let key in fishData) {
datasets.push({
label: key,
data: fishData[key],
backgroundColor: colors[i % 2][0],
hoverBackgroundColor: colors[i % 2][1],
hoverBorderWidth: 0
});
i++;
}
console.log("dataset",datasets)
});
var bar_ctx = document.getElementById('bar-chart');
var bar_chart = new Chart(bar_ctx, {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
animation: {
duration: 10,
},
scales: {
xAxes: [{
stacked: true,
gridLines: {
display: false
},
}],
yAxes: [{
stacked: true
}],
}, // scales
legend: {
display: true
}
} // options
});
Just a quick note, instead of the reading from firebase and d3 nesting, it is working with static data, but i would like to use the code above to directly read from my database.
var sulyhavonta = new Map([ [2018, new Map([ [1,new Map([[15, new
Map([['keszeg',3],['ponty',5]])], [29, new
Map([['keszeg',1],['ponty',1]])]])], [5,new Map([[24, new
Map([['keszeg',9],['ponty',7]])]])] ] )] ]);