[D3] Creating a D3 Force Layout in React

Learn how to leverage d3's layout module to create a Force Layout inside of React. We'll take a look at React's lifecycle methods, using them to bootstrap d3's force layout in order to render our visualization.

 

import React, {
    Component
} from 'react';
import * as d3 from 'd3';
const data = {
    "nodes": [
      {"id": "Myriel", "group": 1},
      ...
      {"id": "Mme.Hucheloup", "group": 8}
    ],
    "links": [
      {"source": "Napoleon", "target": "Myriel", "value": 1},
      ...
      {"source": "Mme.Hucheloup", "target": "Enjolras", "value": 1}
    ]
  }

export default class ForceLayoutIntro extends Component {

    componentDidMount() {
        const {
            width,
            height
        } = this.props;

        const color = d3.scaleOrdinal(d3.schemeCategory20);
        const linkColor = d3.rgb('#c3c3c3');

        function ticked (d) {
            link
                .attr("x1", (d) => d.source.x)
                .attr("y1", (d) => d.source.y)
                .attr("x2", (d) => d.target.x)
                .attr("y2", (d) => d.target.y);
            nodes
                .attr('cx', d => d.x)
                .attr('cy', d => d.y)
        }

      

        const simulation = d3.forceSimulation()
            .force("link", d3.forceLink().id(function (d) {
                return d.id;
            }))
            .force("charge", d3.forceManyBody())
            .force("center", d3.forceCenter(width / 2, height / 2));

        const svg = d3.select(this.refs.mountPoint)
            .append('svg')
            .attr('height', height)
            .attr('width', width);

        const link = svg
            .append('g')
                .attr('class', 'links')
            .selectAll('links')
            .data(data.links)
            .enter().append('line')
                .attr('stroke', linkColor.brighter(0.5))
                .attr('stroke-width', d => Math.sqrt(d.value))        

        const nodes = svg
            .append('g')
            .attr('class', 'nodes')
            .selectAll('circle')
            .data(data.nodes)
            .enter().append('circle')
            .attr('r', 5)
            .attr('fill', (d) => color(d.group))
                .call(d3.drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));

        simulation
            .nodes(data.nodes)
            .on('tick', ticked);

        simulation.force("link")
            .links(data.links);

            function dragstarted(d) {
                if (!d3.event.active) simulation.alphaTarget(0.3).restart();
                d.fx = d.x;
                d.fy = d.y;
              }
              
              function dragged(d) {
                d.fx = d3.event.x;
                d.fy = d3.event.y;
              }
              
              function dragended(d) {
                if (!d3.event.active) simulation.alphaTarget(0);
                d.fx = null;
                d.fy = null;
              }            
    }

    // In render, we just need to create the container graph
    // The only important thing is we need to set ref
    // So that we can access the container in componentDidMount lifeCycle
    render() {
        const {
            width,
            height
        } = this.props;
        const style = {
            width,
            height,
            margin: '10px auto',
            border: '1px solid #323232',
        };
        return ( 
            <div style = {style} ref = "mountPoint"></div>
        );
    }
}

 

 

posted @ 2017-08-28 21:00  Zhentiw  阅读(487)  评论(0编辑  收藏  举报