GithubHelp home page GithubHelp logo

mih4ajlo / d3kit Goto Github PK

View Code? Open in Web Editor NEW

This project forked from twitter/d3kit

0.0 2.0 0.0 452 KB

D3Kit is a set tools to speed D3 related project development

License: MIT License

JavaScript 100.00%

d3kit's Introduction

d3Kit

NPM version Build Status Dependency Status

d3Kit provides thin scaffold for creating reusable and responsive charts with D3. It aims to relieve you from the same groundwork tasks you found yourself doing again and again.

Introduction slides | Getting started guide | API Reference | All Documentation

For developers who have tried d3Kit v1-2, d3Kit v3 was rewritten to support D3 v4, consider several new use cases (<canvas>, for example) and use ES6 class for the implementation, making every chart can be extended easily. Documentation of version 1-2 can be found here

Install

npm install d3kit --save

See getting start guide for more details.

Examples

Here are a few examples of d3Kit in action:

Why should you use d3Kit?

๐Ÿ˜ซ You are tired of copying the boilerplate d3.select('body').append('svg')... from D3 examples.

There is SvgChart for that.

๐Ÿ˜ซ You want to create a chart on <canvas> but never remember how to handle different screen resolution (retina display).

There is CanvasChart for that.

๐Ÿค” You want to create a reusable chart in D3.

You can extends from SvgChart, CanvasChart, or AbstractChart.

๐Ÿ˜ซ You want to create a responsive chart, but are tired of listening to window resize or manually polling for changes of element size by yourself.

If your chart extends from SvgChart, CanvasChart or AbstractChart, you get that ability for free.

๐Ÿค” You want to make a responsive chart that maintains aspect ratio.

If your chart extends from SvgChart, CanvasChart or AbstractChart, you get that ability for free.

๐Ÿ˜ซ You use D3's margin convention and are tired of copy pasting from Mike's block.

If your chart extends from SvgChart, CanvasChart or AbstractChart, you get that ability for free.

๐Ÿค” You are familiar with creating charts in D3 and want to adapt them easily into React or angular components.

Currently there are react-d3kit and angular-d3kit-adapter that can convert charts written in d3Kit into React and angular components, respectively, in a few lines of code.

What is d3Kit?

The core of d3Kit are base classes for creating a chart. Currently there are SvgChart and CanvasChart, both extends from AbstractChart. (This was revised and improved from the Skeleton in d3Kit v1-2.)

AbstractChart

  • takes a target container (usually a <div>) and helps you build a chart inside.
  • encapsulates D3's margin convention. The dimension of each chart is defined by width, height and margin.
    • chart.width() get/set the total width (including margin)
    • chart.height() get/set the total height (including margin)
    • chart.margin() get/set the margin
    • chart.getInnerWidth() returns width excluding margin. This is usually used as the boundary of the x-axis.
    • chart.getInnerHeight() returns height excluding margin. This is usually used as the boundary of the y-axis.
  • can resize the chart to be percentage of a container and/or maintain aspect ratio
    • chart.fit(fitOptions:Object) Calling this function with single argument will resize the chart to fit into the container once. Please refer to slimfit documentation for fitOptions.
  • can listen to resize (either window or element) and update the chart size to fit container.
    • chart.fit(fitOptions:Object, watchOptions:Boolean/Object) Calling with two arguments, such as chart.fit({...}, true) or chart.fit({...}, {...}), will enable watching. Please refer to slimfit documentation for fitOptions and watchOptions
    • chart.stopFitWatcher() will disable the watcher.
  • dispatches event resize when the chart is resized.
    • chart.on('resize', listener) is then use to register what to do after the chart is resized.
  • defines two main input channels .data(...) and .options(...) and dispatches event data and options when they are changed, respectively.
    • chart.data(data) get/set data.
    • chart.options(options) get/merge options
    • chart.on('data', listener)
    • chart.on('options', listener)
  • assumes little about how you implement a chart. You can extends the class and implements it the way you want.

Most of the time you will not need to access AbstractChart directly, but you will use one of its children: SvgChart or CanvasChart.

SvgChart

This class creates <svg> boilerplate inside the container.

A. Scaffold and create something quickly

<div id="chart0"></div>
import { SvgChart } from 'd3kit';
const chart = new SvgChart('#chart0', {
  initialWidth: 720,
  initialHeight: 500,
  margin: { top: 30, right: 30, bottom: 30, left: 30 },
  offset: [0.5, 0.5] // add little offset for sharp-edge rendering
});

The output will looks like this.

<!--chart.container is a D3 selection of this element.-->
<div id="chart0">
  <!--chart.svg is a D3 selection of this element.-->
  <svg width="720" height="500">
    <!--chart.rootG is a D3 selection of this element.-->
    <g transform="translate(30.5,30.5)"></g>
  </svg>
</div>

So you can append a circle or do anything you usually do with D3.

chart.rootG.append('circle')
  .attr('cx', 10)
  .attr('cy', 10)
  .attr('r', 5)

B. Create a reusable chart

First create a chart by extending SvgChart.

import { SvgChart, helper } from 'd3kit';
import { scaleLinear, scaleOrdinal, schemeCategory10 } from 'd3-scale';
import { axisLeft, axisBottom } from 'd3-axis';
import { extent } from 'd3-array';

class SvgBubbleChart extends SvgChart {
  // Define default options for this chart
  static getDefaultOptions() {
    return helper.deepExtend(
      super.getDefaultOptions(),
      {
		  margin: {top: 60, right: 60, bottom: 60, left: 60},
		  initialWidth: 800,
		  initialHeight: 460
      }
    );
  }

  /**
   * Define the names of custom events that can be dispatched from this chart
   * @return {Array[String]} event names
   */
  static getCustomEventNames() {
    return ['bubbleClick'];
  }

  constructor(selector, options) {
    super(selector, options);

    // Add custom variables
    this.xScale = scaleLinear();
    this.yScale = scaleLinear();
    this.color = scaleOrdinal(schemeCategory10);
    this.xAxis = axisBottom().scale(this.xScale);
    this.yAxis = axisLeft().scale(this.yScale);
    this.xAxisG = this.rootG.append('g');
    this.yAxisG = this.rootG.append('g');

    // Add basic event listeners
    this.visualize = this.visualize.bind(this);
    this.on('resize.default', this.visualize);
    this.on('data.default', this.visualize);
  }

  // You can define a new function for this class.
  visualize() {
    if(!this.hasData()) return;

    const data = this.data();

    this.xScale.domain(extent(data, d => d.x))
      .range([0, this.getInnerWidth()]);
    this.yScale.domain(extent(data, d => d.y))
      .range([this.getInnerHeight(), 0]);

    this.xAxisG
      .attr('transform', `translate(0,${this.getInnerHeight()})`)
      .call(this.xAxis);

    this.yAxisG.call(this.yAxis);

    const selection = this.rootG.selectAll('circle')
      .data(data);

    selection.exit().remove();

    const sEnter = selection.enter().append('circle')
      .attr('cx', d => this.xScale(d.x))
      .attr('cy', d => this.yScale(d.y))
      .on('click', (...args) => {
        this.dispatcher.apply('bubbleClick', this, args);
      });

    selection.merge(sEnter)
      .attr('cx', d => this.xScale(d.x))
      .attr('cy', d => this.yScale(d.y))
      .attr('r', d => d.r)
      .style('fill', (d,i) => this.color(i));
  }
}

export default SvgBubbleChart;

Then use it

const chart1 = new SvgBubbleChart('#chart1', {
  margin: { top: 20 },
  initialWidth: 300,
  initialHeight: 300,
})
  .data(bubbles)
  // handle bubbleClick event
  .on('bubbleClick', d => { alert(JSON.stringify(d)); })
  // demonstrate auto resizing to maintain 16:9 aspect ratio
  .fit({
    mode: 'aspectRatio',
    ratio: 16/9,
  }, true);

CanvasChart

While SvgChart creates necessary element for building chart with <svg>. This class creates <canvas> inside the container. It also handles different screen resolution for you (retina display vs. standard display).

A. Scaffold and create something quickly

<div id="chart0"></div>
import { SvgChart } from 'd3kit';
const chart = new CanvasChart('#chart0', {
  initialWidth: 720,
  initialHeight: 500,
  margin: { top: 30, right: 30, bottom: 30, left: 30 }
});

The output will looks like this.

<!--chart.container is a D3 selection of this element.-->
<div id="chart0">
  <!--chart.canvas is a D3 selection of this element.-->
  <!--notice that width/height are handled to ensure that it will look nice on retina display-->
  <canvas width="1440" height="1000" style="width: 720px; height: 500px;"></canvas>
</div>

So you can draw on the canvas

const ctx = chart.getContext2d();
ctx.fillRect(10, 10, 10, 10);

B. Create a reusable chart

import { CanvasChart, helper } from 'd3kit';
import { scaleLinear, scaleOrdinal, schemeCategory10 } from 'd3-scale';
import { extent } from 'd3-array';

class CanvasBubbleChart extends CanvasChart {
  // Define default options for this chart
  static getDefaultOptions() {
    return helper.deepExtend(
      super.getDefaultOptions(),
      {
		  margin: {top: 60, right: 60, bottom: 60, left: 60},
		  initialWidth: 800,
		  initialHeight: 460
      }
    );
  }

  /**
   * Define the names of custom events that can be dispatched from this chart
   * @return {Array[String]} event names
   */
  static getCustomEventNames() {
    return [];
  }

  constructor(selector, options) {
    super(selector, options);

    // add custom variables
    this.xScale = scaleLinear();
    this.yScale = scaleLinear();
    this.color = scaleOrdinal(schemeCategory10);

    // add basic event listeners
    this.visualize = this.visualize.bind(this);
    this.on('resize.default', this.visualize);
    this.on('data.default', this.visualize);
  }

  // You can define a new function for this class.
  visualize() {
    this.clear();

    if(!this.hasData()){
      return;
    }

    const data = this.data();

    this.xScale.domain(extent(data, d => d.x))
      .range([0, this.getInnerWidth()]);
    this.yScale.domain(extent(data, d => d.y))
      .range([this.getInnerHeight(), 0]);

    const ctx = this.getContext2d();
    data.forEach((d,i) => {
      ctx.fillStyle = this.color(i);
      ctx.fillRect(
        this.xScale(d.x) - d.r,
        this.yScale(d.y) - d.r,
        d.r * 2,
        d.r * 2
      );
    });

  }
}

export default CanvasBubbleChart;

Other features

LayerOrganizer

This was created from a habit of creating many <g>s inside the root <g>.

Input

<svg>
  <g class="container"></g>
</svg>
const layers = new LayerOrganizer(d3.selection('.container'));
layers.create(['content', 'x-axis', 'y-axis']);

Output

<svg>
  <g class="container">
    <!-- layers.get('content') is a D3 selection of this g -->
    <g class="content-layer"></g>
    <!-- layers.get('x-axis') is a D3 selection of this g -->
    <g class="x-axis-layer"></g>
    <!-- layers.get('y-axis') is a D3 selection of this g -->
    <g class="y-axis-layer"></g>
  </g>
</svg>

All SvgChart includes chart.layers by default, which is new LayerOrganizer(chart.container).

There are more features. Read more here.

Chartlet

d3Kit v1-2 also helps you create reusable subcomponent (a.k.a. Chartlet). We have not ported it to v3 yet.

Documentation

Want to learn more? Follow these links.

(We are still updating them to reflect the latest API, so some pages may be a bit outdated at the moment.)

Appendix

A diagram explaining D3's margin convention

Authors

License

ยฉ 2015-2016 Twitter, Inc. MIT License

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.