forked from ValIlya's block: Timetables
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
<title>JS Bin</title>
</head>
<style>
#table, #slope {display: inline-block; }
/* #table {
visibility: hidden;
} */
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.grid line {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
.axis path, .axis line {
stroke-width: .5px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 14px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.overlay {
fill: none,
pointer-events: all
}
</style>
<body>
<div id="container">
<div id="table"></div>
<div id="slope"></div>
</div>
<script id="jsbin-javascript">
// .attr('pointer-events', 'all') - элемент будет слушать события даже если у него нет заливки
// .attr('pointer-events', 'none') - элемент не будет слушать события
d3.json('shop.json', function(tt_list) {
// Slope
var data = tt_list.filter( d => d.process == 3)
var data2 = data // for mousemoving
var margin = {
top: 20,
right: 20,
bottom: 40,
left: 50
}
var fullWidth = 400
var fullHeight = 400
var width = fullWidth - margin.left - margin.right
var height = fullHeight - margin.top - margin.bottom
var xExtent = d3.extent(data, function(d) {
return d.tick
});
var yExtent =d3.extent(data, function(d) {
return d.cost
});
var xScale = d3.scaleLinear()
.domain(xExtent)
.range([0, width])
var yScale = d3.scaleLinear()
.domain(yExtent)
.range([height, 0])
var svg = d3.select('#slope')
.append('svg')
.attr('width', fullWidth)
.attr('height', fullHeight)
.append('g')
.attr('transform',
'translate(' +
margin.left + ',' +
margin.top +
')')
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var axisX = d3.axisBottom(xScale)
svg
.append('g')
.attr('transform', 'translate(0,' + height + ')')
.call(axisX)
.classed('axis x', true)
var axisY = d3.axisLeft(yScale)
svg
.append('g')
.call(axisY)
.classed('axis y', true)
var valueline = d3.line()
.x(function(d) { return xScale(d.tick); })
.y(function(d) { return yScale(d.cost); });
var line = svg
.append('g')
.selectAll('path')
.data([data])
.enter()
.append("path")
.attr("class", "line")
.attr("d", valueline);
var markers = svg
.append('g')
.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('id', 'marker')
.attr('transform',
d => 'translate(' +
xScale(d.tick) + ',' +
yScale(d.cost) +
')')
markers.append('circle')
.attr('r', 5)
.attr('fill', 'blue')
svg.append('rect')
.attr('class', 'overlay')
.attr('width', width)
.attr('height', height)
.attr('opacity', 0)
//.on('mouseover', () => focus.style('display', null))
.on('mouseout', function() {
markers
.select('circle')
.transition(0)
.attr('fill', 'blue');
tooltip.transition()
.duration(200)
.style("opacity", 0);
})
.on('mousemove', mousemove);
function mousemove() {
const x0 = xScale.invert(d3.mouse(this)[0]);
const t = Math.round(x0)
var cost = data2.filter(d => d.tick==t)[0].cost
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d3.format('.2f')(cost))
.style("left", d3.event.pageX + "px")
.style("top", margin.top+yScale(cost)-20 + "px");
var mouse_data = data2
.filter(d => d.tick == t)[0]
build_table(mouse_data)
markers
.select('circle')
.transition(0)
.attr('fill', 'blue');
markers
.filter(m => m.tick==t)
.select('circle')
.transition(0)
.attr('fill', 'red');
}
markers
.on('mouseover',
function(d){
var nodeSelection = d3
.select(this)
nodeSelection
.select('circle')
.transition(3000)
.attr('fill', 'red');
tooltip.transition()
.duration(200)
.style("opacity", .9);
tooltip.html(d3.format('.2f')(d.cost))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 30) + "px");
})
.on("mouseout", function(d){
var nodeSelection = d3
.select(this)
nodeSelection
.select('circle')
.transition(3000)
.attr('fill', 'blue')
tooltip
.transition(200)
.style('opacity', 0)
});
// Timetable
var build_table = function(data) {
var nempl = data.sol[0].length
var nslots = data.sol.length
var margin = {
top: 20,
right: 20,
bottom: 40,
left: 30
}
var fullWidth = 200
var fullHeight = 400
var width = fullWidth - margin.left - margin.right
var height = fullHeight - margin.top - margin.bottom
var xExtent = [0, nempl]
var yExtent = [0, nslots]
var xScale = d3.scaleLinear()
.domain(xExtent)
.range([0, width])
var yScale = d3.scaleLinear()
.domain(yExtent)
.range([0, height])
var color = d3.scaleLinear()
.domain([0, 1])
.range(['white', 'green'])
d3.select('#table').selectAll('*')
.remove()
var svg = d3.select('#table')
.append('svg')
.attr('width', fullWidth)
.attr('height', fullHeight)
.append('g')
.attr('transform',
'translate(' +
margin.left + ',' +
margin.top +
')')
var axisX = d3.axisTop(xScale)
.ticks(nempl)
svg
.append('g')
.classed('axis x', true)
.call(axisX)
var axisY = d3.axisLeft(yScale)
.ticks(nslots)
svg
.append('g')
.classed('axis y', true)
.call(axisY)
var row_groups = svg
.append('g')
.selectAll('g')
.data(data.sol)
.enter()
.append('g')
var rect_margin = -.7
var rect_width = xScale(1) - xScale(0)-rect_margin
var rect_height = yScale(1) - yScale(0)-rect_margin
var rects = row_groups
.selectAll('g')
.data(function (d, parentIndex) {
return d.map(function(data, index){
return {
parentIndex: parentIndex,
index: index,
data: data
}
})
})
.enter()
.append('rect')
.attr('x', function(d, i) {return xScale(d.index)+rect_margin;})
.attr('y', function(d, i) {return yScale(d.parentIndex)+rect_margin;})
.attr('width', rect_width)
.attr('height', rect_height)
.attr('fill', function(d, i) {return color(d.data);})
var text =
svg
.append('g')
.attr('transform', 'translate('+ width / 2 +',' + height + ')')
.append('text')
.text('cost: '+ d3.format('10.2f')(data.cost))
.attr('dy', 15)
.attr('text-anchor', 'middle')
}
var data = tt_list[20]
build_table(data)
})
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js