【发布时间】:2018-02-18 16:01:48
【问题描述】:
【问题讨论】:
-
请附上您迄今为止编写的所有相关代码。
标签: javascript reactjs d3.js charts
【问题讨论】:
标签: javascript reactjs d3.js charts
我们使用 NPM 添加模块,虽然我们可以包含整个 D3 库并像我们一样继续编码,但您最好安装各个模块并了解导入这些模块的工作原理。在您的项目目录中运行以下命令来安装 d3-scale 模块:
npm i –SE d3-scale
此命令(npm i 是 npm install 的缩写)安装最新版本的 d3-scale(这使我们能够访问我们在过去八章中使用过的所有精彩音阶)和 –SE标签将确切的版本保存到您的package.json,以便当您想在其他地方部署此应用程序时,d3-scale 已安装。除了 d3-scale,对以下模块做同样的事情:
d3-shape
d3-svg-legend
d3-array
d3-geo
d3-selection
d3-transition
d3-brush
d3-axis
通过像这样单独安装模块,您可以减少随应用程序部署的代码量,减少加载时间并提高可维护性。
使用 React 进行 D3 渲染 -
将 D3 与 React 集成的挑战在于 React 和 D3 都想控制 DOM。带有 D3 的整个 select/enter/exit/update 模式与 React 及其虚拟 DOM 直接冲突。如果您是从 D3 开始使用 React,那么放弃对 DOM 的控制是那些“冷酷无情”的时刻之一。大多数人使用 D3 和 React 的方式是使用 React 来构建应用程序的结构,并渲染传统的 HTML 元素,然后在数据可视化部分,他们将一个 DOM 容器(通常是一个 )传递给 D3并使用 D3 创建、销毁和更新元素。在某种程度上,它类似于我们过去使用 Java 小程序或 Flash 在您的页面中运行一个黑匣子,而页面的其余部分是单独呈现的方式。这种集成 React 和 D3 的方法的好处是,您可以使用在所有核心 D3 示例中看到的所有相同类型的代码。主要困难在于您需要在各种 React 生命周期事件中创建函数以确保您的 viz 更新。
下面的清单显示了一个使用此方法构建的简单条形图组件。
示例 - 1
BarChart.js
import React, { Component } from 'react'
import './App.css'
import { scaleLinear } from 'd3-scale’
import { max } from 'd3-array'
import { select } from 'd3-selection'
class BarChart extends Component {
constructor(props){
super(props)
this.createBarChart = this.createBarChart.bind(this)
}
componentDidMount() {
this.createBarChart()
}
componentDidUpdate() {
this.createBarChart()
}
createBarChart() {
const node = this.node
const dataMax = max(this.props.data)
const yScale = scaleLinear()
.domain([0, dataMax])
.range([0, this.props.size[1]])
select(node)
.selectAll('rect')
.data(this.props.data)
.enter()
.append('rect')
select(node)
.selectAll('rect')
.data(this.props.data)
.exit()
.remove()
select(node)
.selectAll('rect')
.data(this.props.data)
.style('fill', '#fe9922')
.attr('x', (d,i) => i * 25)
.attr('y', d => this.props.size[1] — yScale(d))
.attr('height', d => yScale(d))
.attr('width', 25)
}
render() {
return <svg ref={node => this.node = node}
width={500} height={500}>
</svg>
}
}
export default BarChart
进行这些更改并保存它们不会立即显示任何效果,因为您没有在 app.js 中导入和呈现此组件,这是您的应用最初呈现的组件。更改 app.js 以匹配以下列表。
在 App.js 中引用 BarChart.js
import React, { Component } from 'react'
import './App.css'
import BarChart from './BarChart'
class App extends Component {
render() {
return (
<div className='App'>
<div className='App-header'>
<h2> D3 Dashboard</h2>
</div>
<div>
<BarChart data={[5,10,1,3]} size={[500,500]} />
</div>
</div>
)
}
}
export default App
这是你的 React + D3 示例,呈现了一个简单的条形图。
还有其他使用 D3 和 React 呈现数据可视化的方法。
React 用于元素创建,D3 作为可视化内核 - 您可以使用 D3 生成所有必要的绘图指令并使用 React 创建实际的 DOM 元素,而不是使用 ref 获取实际的 DOM 节点并将该 DOM 节点传递给 D3。这种方法在创建动画过渡和可拖动元素方面存在挑战,但除此之外它更可取,因为它会创建更易于维护的代码。
示例 - 2
WorldMap.js
import React, { Component } from 'react'
import './App.css'
import worlddata from './world'
import { geoMercator, geoPath } from 'd3-geo'
class WorldMap extends Component {
render() {
const projection = geoMercator()
const pathGenerator = geoPath().projection(projection)
const countries = worlddata.features
.map((d,i) => <path
key={'path' + i}
d={pathGenerator(d)}
className='countries'
/>)
return <svg width={500} height={500}>
{countries}
</svg>
}
}
export default WorldMap
这是一个通过 React 和 JSX 渲染的基本地图,D3 提供绘图指令。
示例 - 3
StreamGraph.js
import React, { Component } from 'react'
import './App.css'
import { stack, area, curveBasis, stackOrderInsideOut, stackOffsetSilhouette } from 'd3-shape'
import { range } from 'd3-array'
import { scaleLinear } from 'd3-scale'
class StreamGraph extends Component {
render() {
const stackData = range(30).map(() => ({}))
for (let x = 0; x<30; x++) {
this.props.data.forEach(country => {
stackData[x][country.id] = country.data[x]
})
}
const xScale = scaleLinear().domain([0, 30])
.range([0, this.props.size[0]])
const yScale = scaleLinear().domain([0, 60])
.range([this.props.size[1], 0])
const stackLayout = stack()
.offset(stackOffsetSilhouette)
.order(stackOrderInsideOut)
.keys(Object.keys(stackData[0]))
const stackArea = area()
.x((d, i) => xScale(i))
.y0(d => yScale(d[0]))
.y1(d => yScale(d[1]))
.curve(curveBasis)
const stacks = stackLayout(stackData).map((d, i) => <path
key={"stack" + i}
d={stackArea(d)}
style={{ fill: this.props.colorScale(this.props.data[i].launchday),
stroke: "black", strokeOpacity: 0.25 }}
/>)
return <svg width={this.props.size[0]} height={this.props.size[1]}>
<g transform={"translate(0," + (-this.props.size[1] / 2) + ")"}>
{stacks}
</g>
</svg>
}
}
export default StreamGraph
在 App.js 中引用 StreamGraph.js
import React, { Component } from 'react'
import './App.css'
import StreamGraph from './StreamGraph'
class App extends Component {
render() {
return (
<div className='App'>
<div className='App-header'>
<h2> D3 Dashboard</h2>
</div>
<div>
<StreamGraph colorScale={colorScale} data={appdata} size={[1000,250]} />
</div>
</div>
)
}
}
export default App
或
【讨论】: