Skip to content

Instantly share code, notes, and snippets.

@SevenChan07
Last active August 28, 2017 10:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SevenChan07/3fbb45be891cc3fda3bf139e4853535b to your computer and use it in GitHub Desktop.
Save SevenChan07/3fbb45be891cc3fda3bf139e4853535b to your computer and use it in GitHub Desktop.
zoomable histogram
license: gpl-3.0
height: 700

Histogram can be zoomabled.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect.zoom-panel {
cursor: move;
fill: #fff;
pointer-events: all;
}
.bar {
fill: steelblue;
}
</style>
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
let data = []
let ordinals = []
for(let i = 0;i < 100; i++){
data.push({
value: Math.random()*10,
city: 'test' + i
})
ordinals.push('test' + i)
}
let margin = {
top: 50,
right: 100,
bottom: 50,
left: 100
},
width = 1000 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom,
radius = (Math.min(width, height) / 2) - 10,
node
const svg = d3.select('body')
.append('svg')
.attr('width', 960)
.attr('height', 700)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`)
.call(
d3.zoom()
.translateExtent([[0,0], [width, height]])
.extent([[0, 0], [width, height]])
.on('zoom', zoom)
)
// the scale
let x = d3.scaleLinear().range([0, width])
let y = d3.scaleLinear().range([height, 0])
let color = d3.scaleOrdinal(d3.schemeCategory10)
let xScale = x.domain([-1, ordinals.length])
let yScale = y.domain([0, d3.max(data, function(d){return d.value})])
// for the width of rect
let xBand = d3.scaleBand().domain(d3.range(-1, ordinals.length)).range([0, width])
// zoomable rect
svg.append('rect')
.attr('class', 'zoom-panel')
.attr('width', width)
.attr('height', height)
// x axis
let xAxis = svg.append('g')
.attr('class', 'xAxis')
.attr('transform', `translate(0, ${height})`)
.call(
d3.axisBottom(xScale).tickFormat((d, e) => {
return ordinals[d]
})
)
// y axis
let yAxis = svg.append('g')
.attr('class', 'y axis')
.call(d3.axisLeft(yScale))
let bars = svg.append('g')
.attr('clip-path','url(#my-clip-path)')
.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr('class', 'bar')
.attr('x', function(d, i){
return xScale(i) - xBand.bandwidth()*0.9/2
})
.attr('y', function(d, i){
return yScale(d.value)
})
.attr('width', xBand.bandwidth()*0.9)
.attr('height', function(d){
return height - yScale(d.value)
})
let defs = svg.append('defs')
// use clipPath
defs.append('clipPath')
.attr('id', 'my-clip-path')
.append('rect')
.attr('width', width)
.attr('height', height)
let hideTicksWithoutLabel = function() {
d3.selectAll('.xAxis .tick text').each(function(d){
if(this.innerHTML === '') {
this.parentNode.style.display = 'none'
}
})
}
function zoom() {
if (d3.event.transform.k < 1) {
d3.event.transform.k = 1
return
}
xAxis.call(
d3.axisBottom(d3.event.transform.rescaleX(xScale)).tickFormat((d, e, target) => {
// has bug when the scale is too big
if (Math.floor(d) === d3.format(".1f")(d)) return ordinals[Math.floor(d)]
return ordinals[d]
})
)
hideTicksWithoutLabel()
// the bars transform
bars.attr("transform", "translate(" + d3.event.transform.x+",0)scale(" + d3.event.transform.k + ",1)")
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment