d3fc / d3fc

A collection of components that make it easy to build interactive charts with D3
https://d3fc.io/
MIT License
1.29k stars 193 forks source link

d3fc-webgl stacked bar chart with navigation brush #1648

Open alexdbugging opened 3 years ago

alexdbugging commented 3 years ago

I'm trying to add a navigation brush similar to the one in this bl.ocks: https://bl.ocks.org/ColinEberhardt/000b1721b69868f1ce5d. However I am confused on how to achieve this, as it's described that the brush navigation chart requires the main chart to be a Cartesian chart. Is adding a navigation brush to a WebGL stacked bar chart possible, and if so, could you help me understand how to do it, since I can't find any examples showing it.

Edited to fix URL

chrisprice commented 3 years ago

There's no coupling between the brush component and cartesianChart, that example just demonstrates one way they can be used together. That said, you can use the WebGL series with cartesianChart by setting it as the webglSeries property e.g. https://d3fc.io/examples/svg-canvas-and-webgl-chart/

alexdbugging commented 3 years ago

Thanks a lot for the information! On a quick note - is there a d3fc library for Angular? I've been looking around your github pages for one but wasn't able to find one.

ColinEberhardt commented 3 years ago

Thanks a lot for the information! On a quick note - is there a d3fc library for Angular?

We don't provide any framework specific bindings - D3FC is a collection of high-level D3 components, so the process for integrating it with Angular (Or React, Vue etc ...) is just the same as it is for D3.

alexdbugging commented 3 years ago

I'm having trouble plotting my webgl series stacked bar chart using the cartesian chart component. I've specified the webgl bar series as well as a canvas grid line, but when I load the page my x-axis and y-axis show the correct data ranges, but the bars are not rendered. Can you help with identifying what I'm doing wrong here?

index.html

<!doctype html>
<html>

<head>
    <script src="../../node_modules/seedrandom/seedrandom.js"></script>
    <script>
        Math.seedrandom('a22ebc7c488a3a47');
    </script>
    <script src="../../node_modules/mockdate/src/mockdate.js"></script>
    <script>
        MockDate.set('2000-01-01', 0);
    </script>
    <script src="../../node_modules/d3/dist/d3.js"></script>
    <script src="../../packages/d3fc/build/d3fc.js"></script>
    <script src="../index.js"></script>
    <link rel="stylesheet" href="../index.css">
    <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgo=">

    <style>
        body {
            flex-direction: column;
        }

        #main-chart {
            flex: 4;
        }

        #navigator-chart {
            flex: 1;
        }

        #navigator-chart .y-axis {
            opacity: 0;
        }
    </style>
</head>

<body>
    <div id="chart"></div>
    <script src="index.js"></script>
</body>

</html>

index.js

function generateData(amount) {
    var dataToReturn = [];
    var startDate = 1579478400 * 1000;
    for (i = 0; i < amount; i++) {
        startDate += (24 * 60 * 60 * 1000);
        var objectToPush = {
            Date: new Date(startDate),
            'App check-in': Math.random() * (150 - 20) + 20,
            'Snap check-in': Math.random() * (150 - 20) + 20,
            'Grab check-in': Math.random() * (150 - 20) + 20,
            'Web check-in': Math.random() * (150 - 20) + 20,
            'Reception check-in': Math.random() * (150 - 20) + 20,
            'Additional guests': Math.random() * (150 - 20) + 20
        };
        dataToReturn.push(objectToPush)
    }

    return dataToReturn;
}

const data = generateData(100);

const color = d3.scaleOrdinal(d3.schemeCategory10);
const stack = d3.stack().keys(Object.keys(data[0]).filter(k => k !== 'Date'));
const series = stack(data);
console.log(data, series);

const xScale = d3
    .scaleTime()
    .domain(fc.extentDate().accessors([d => d.Date])(data));

const yScale = d3
    .scaleLinear()
    .domain(fc.extentLinear().accessors([d => d[1]])(series));

const webglBar = fc.seriesWebglBar()
    .xScale(xScale)
    .yScale(yScale)
    .crossValue(d => d.Date)
    .mainValue(d => d[1])
    .baseValue(d => d[0])

series.forEach((s, i) => {
    webglBar.decorate(program => {
        fc
            .webglFillColor()
            .value(() => {
                const {
                    r,
                    g,
                    b,
                    opacity
                } = d3.color(color(i));
                return [r / 255, g / 255, b / 255, opacity];
            })
            .data(data)(program);
    })(s);
});

const gridline = fc.annotationCanvasGridline();

const chart = fc
    .chartCartesian(xScale, yScale)
    .webglPlotArea(webglBar)
    .canvasPlotArea(gridline);

d3.select('#chart')
    .datum(data)
    .call(chart);

Here's a picture of the result: webgl_stacked_bar

alexdbugging commented 3 years ago

Could you provide an example of how to plot a stacked bar chart Webgl series in a cartesian chart using the latest version of d3fc? My own version only works on version d3fc 14.1.0 and d3 version 5.14.2.