我有2个简单的d3(v4)示例。我的第一个例子显示了使用缩放和画笔与线性图表。它使用了不可变线性标度x.domain([0, POINTS])
。
const POINTS = 100
const coords = []
const wrapper = d3.select("#chart")
const brushWrap = d3.select('#brush')
const time = 1e2
let interval = null
var width = 500;
var height = 200;
var margin = {
left: 0, right: 0, top: 0, bottom: 0
};
// point to coordinate converter
var x = d3.scaleLinear()
.domain([0, POINTS - 1])
.range([0, width - margin.left - margin.right]);
var y = d3.scaleLinear()
.domain([0, POINTS - 1])
.range([height - margin.top - margin.bottom, 0]);
//create d3 path generation function
const line = d3.line()
.x((d) => x(d.x))
.y((d) => y(d.y))
.curve(d3.curveBasis)
;
const zoom = d3.zoom()
.scaleExtent([1, 10])
.translateExtent([ [0, 0], [width, height] ])
.extent([ [0, 0], [width, height] ])
const brush = d3.brushX().extent([ [0, 0], [width, 40] ])
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// reused chart func
function draw(selection) {
selection.each(function(data) {
var container = d3.select(this)
var svg = container.selectAll("svg").data([data]);
var gEnter = svg.enter().append("svg").append("g"); // virtual g elem
svg
.attr("width", width)
.attr('height', height)
// append path layer
gEnter
.append("g")
.attr("class", "path layer")
.append("path")
.attr("class", "line")
gEnter
.append('rect')
.attr('class', 'zoom')
.attr("width", width)
.attr('height', height)
.call(zoom)
var g = svg.select("g") // real g elem
.attr('transform', `translate(${margin.left},${margin.top})`)
g.select("path.line")
.attr('d', line(data))
})
}
function render() {
wrapper
.datum(coords.map((y, x) => ({x,y})))
.call(draw)
}
function task() {
if (coords.length > POINTS) {
coords.shift()
}
coords.push(random(0, POINTS - 1))
render()
}
function start() {
if (interval !== null) {
return
}
interval = d3.interval(task, time)
}
function stop() {
if (interval === null) {
return
}
interval.stop()
interval = null
}
function zoomIn() {
wrapper.select('rect.zoom').call(zoom.scaleBy, 2)
}
function zoomOut() {
wrapper.select('rect.zoom').call(zoom.scaleBy, 0.5)
}
function onzoom() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'brush') return;
const transform = d3.event.transform;
const scaleX = transform.rescaleX(x)
line.x(d => scaleX(d.x))
const points = scaleX.range().map(transform.invertX, transform)
brushWrap.select('.brush').call(brush.move, points)
render()
}
function onbrush() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === 'zoom') return;
const selection = d3.event.selection
const transform = d3.zoomIdentity
.scale(width / (selection[1] - selection[0]))
.translate(-selection[0], 0);
const scaleX = transform.rescaleX(x)
line.x(d => scaleX(d.x))
wrapper.select('rect.zoom').call(zoom.transform, transform)
render()
}
brushWrap
.append('svg')
.attr('width', width)
.attr('height', 40)
.append('g')
.attr('class', 'brush')
.call(brush);
zoom.on('zoom', onzoom)
brush.on('brush', onbrush)
brushWrap.select('.brush').call(brush.move, x.range())
d3.select('#start').on('click', start)
d3.select('#stop').on('click', stop)
d3.select('#zoomin').on('click', zoomIn)
d3.select('#zoomout').on('click', zoomOut)
start()
html, body {
height: 100%;
}
body {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
#wrap {
width: 500px;
height: 250px;
}
#chart, #brush {
margin-top: 10px;
border: 1px solid lightgrey;
}
/* Chart Styles */
.line {
fill: none;
stroke: #FF5722;
stroke-width: 1px;
}
.zoom {
fill: none;
cursor: move;
pointer-events: all;
}
#brush {
height: 40px;
line-height: 1;
}
<div id="wrap">
<div class="button-group">
<button id="start">start</button>
<button id="stop">stop</button>
<button id="zoomin">+</button>
<button id="zoomout">-</button>
</div>
<div id="chart"></div>
<div id="brush"></div>
</div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js'></script>
我的第二个例子显示了可变域的使用(每次显示当前时间刻度 x.domain([now, now - 5e3])
):
const coords = []
const POINTS = 100;
const MAX = 10;
const width = 500;
const height = 250;
const margin = {
left: 0, right: 0, top: 0, bottom: 15
};
const viewHeight = height - margin.top - margin.bottom;
// point to coordinate converter
const x = d3.scaleTime()
.range([0, width - margin.left - margin.right]);
const y = d3.scaleLinear()
.domain([0, MAX])
.range([viewHeight, 0]);
const xAxis = d3.axisBottom(x)
.tickSizeInner(-viewHeight)
.tickSizeOuter(0)
.tickPadding(4)
.tickFormat(d3.timeFormat(':%S.%L'));
//create d3 path generation function
const line = d3.line()
.curve(d3.curveBasis)
.defined(p => !!p)
.x((d) => x(d.x))
.y((d) => y(d.y));
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
// reused chart func
function draw(selection) {
// change domain every time
const now = Date.now();
x.domain([now, now - 5e3])
selection.each(function(data) {
const container = d3.select(this)
const svg = container.selectAll("svg").data([data]);
const gEnter = svg.enter().append("svg").append("g");
svg
.attr("width", width)
.attr('height', height)
// append path layer
gEnter
.append("g")
.attr("class", "path layer")
.append("path")
.attr("class", "line")
gEnter
.append('g')
.attr('transform', `translate(0, ${viewHeight})`)
.attr('class', 'x axis')
.call(xAxis);
const g = svg.select("g") // real g elem
.attr('transform', `translate(${margin.left},${margin.top})`)
g.select("path.line")
.attr('d', line(data))
g.select('.x.axis')
.call(xAxis)
.selectAll('line')
.attr('stroke-dasharray', 5)
.attr('y2', -viewHeight);
})
}
const wrapper = d3.select("#wrap");
d3.interval(() => {
coords.unshift({
y: random(0, MAX),
x: new Date()
})
if (coords.length > POINTS) {
coords.pop()
}
wrapper
.datum(coords)
.call(draw)
}, 100)
html, body {
height: 100%;
}
body {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
#wrap {
width: 500px;
height: 250px;
border: 1px solid lightgrey;
}
.axis {
font: 12px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #B0BEC5;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: #FF5722;
stroke-width: 1px;
}
<div id="wrap"></div>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js'></script>
如何加入2这些例子?主要困难是可变域。