Skip to content

Instantly share code, notes, and snippets.

@ckinmind
Last active December 13, 2017 09:34
Show Gist options
  • Save ckinmind/d5908e979a8b503813a094f871a2f350 to your computer and use it in GitHub Desktop.
Save ckinmind/d5908e979a8b503813a094f871a2f350 to your computer and use it in GitHub Desktop.
Bar——zoomable histogram

说明

知识点

  • d3.zoom - 创建缩放行为
  • zoom.translateExtent - 设置平移范围
  • zoom.extent - 设置viewport的尺寸
<!DOCTYPE html>
<meta charset="utf-8">
<style>
rect.zoom-panel {
cursor: move;
fill: #fff;
pointer-events: all;
}
.bar {
fill: #43d1b6;
}
</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: 'Item' + i
})
ordinals.push('Item' + i)
}
const margin = {top: 50, right: 100, bottom: 50, left: 100},
width = 1000 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom
// d3.zoom - 创建缩放行为
// zoom.translateExtent - 设置平移范围
// zoom.extent - 设置viewport的尺寸
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 xScale = x.domain([-1, ordinals.length])
let yScale = y.domain([0, d3.max(data, d => 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轴
let xAxis = svg.append('g')
.attr('class', 'xAxis')
.attr('transform', `translate(0, ${height})`)
.call(
d3.axisBottom(xScale)
.tickFormat((d, i) => ordinals[d])
)
// 绘制y轴
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', (d, i) => xScale(i) - xBand.bandwidth() * 0.9 / 2)
.attr('y', (d, i) => yScale(d.value))
.attr('width', xBand.bandwidth() * 0.9)
.attr('height', d => height - yScale(d.value))
// .attr('fill', (d,i) => color(i))
let defs = svg.append('defs')
// use clipPath
defs.append('clipPath')
.attr('id', 'my-clip-path')
.append('rect')
.attr('width', width)
.attr('height', height)
// 如果没有文字则隐藏tick节点
let hideTicksWithoutLabel = function() {
d3.selectAll('.xAxis .tick text').each(function(d){
if(this.innerHTML === '') {
this.parentNode.style.display = 'none'
}
})
}
// 比如初始柱子数目为100, 缩放的k值为1,缩放到柱子数目为5, 放大大概20倍,k值差不多是20
function zoom() {
// 限制不能缩放的过小
if (d3.event.transform.k < 1) {
d3.event.transform.k = 1
return
}
// 限制不能缩放的过大
if (d3.event.transform.k > 20) {
d3.event.transform.k = 20
return
}
// 这里的意思大概是随着缩放,调整x坐标轴的显示,但是有点看不懂
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()
// 对bar进行缩放
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