block by shimizu fed49212b162deaee6726d537b271fbb

Clustered Column Chart

Full Screen

分割チャート。

index.html

<!DOCTYPE html>
<html lang="jp">
<head>
<style>
html, body {
    margin: 0px;
    padding: 0px;
    width: 100%;
    height: 100%;
}
    
#chart {
    width: 80%;
    height: 80%;
    border: 8px dashed gray;
    border-left: none;
    border-top:none;
    cursor:all-scroll;
}
#chart svg{
    width: 100%;
    height: 100%;
    cursor: default;
}

.grid .tick line {
    stroke-dasharray:1;
}



.topBar {
    stroke:gray;
    stroke-width:1;
    fill:skyblue;
}

.bottomBar {
    stroke:gray;
    stroke-width:1;
    fill:skyblue;
}

.splitLine {
   stroke:black;
}
 
</style>
<script src="//unpkg.com/babel-standalone@6.26.0/babel.min.js"></script>
<script src="//unpkg.com/d3@4.12.2/build/d3.min.js"></script>    
</head>
<body>
<div id="chart">
    <svg></svg>
</div>



<script ="text/babel">
const data = [
    {title:"アメリカ", value1:86, value2:26},
    {title:"日本", value1:48, value2:62},
    {title:"中国", value1:62, value2:56},
]
    
const svg = d3.select("#chart").select("svg");
const grid = svg.append("g").classed("grid", true);
const plot = svg.append("g").classed("plot", true);
const axis = svg.append("g").classed("axis", true);
const yTopScale = d3.scaleBand().domain(data.map(d => d.title));
const yBottomScale = d3.scaleBand().domain(data.map(d => d.title));

const xScale = d3.scaleLinear().domain([0, 100]).nice();


render();

function render(){
    const m = {top:30, left:60, right:30, bottom:30};
    const w = svg.node().clientWidth || svg.node().parentNode.clientWidth;
    const h = svg.node().clientHeight || svg.node().parentNode.clientHeight;        
    const pw = w - (m.left + m.right);
    const ph = h - (m.top + m.bottom);
    const hph = ph/2;
    
    
    yTopScale.range([0, hph]).paddingInner(0.1).paddingOuter(0.25);
    yBottomScale.range([hph, ph]).paddingInner(0.1).paddingOuter(0.25);   
    xScale.range([0, pw]);
    
    //axis layer     
    axis.attr("transform", `translate(${m.left}, ${m.top})`);
    
    //top y axis    
    const yTopAxisUpdate = axis.selectAll(".yTopAxis").data([null]);
    const yTopAxisEnter = yTopAxisUpdate.enter().append("g").classed("yTopAxis", true);    
    const yTopAxis = yTopAxisUpdate.merge(yTopAxisEnter).call( d3.axisLeft().scale(yTopScale).tickSizeOuter(0) ); //ラベルを内向きにする
    
    yTopAxis.selectAll(".tick line").remove();  
    yTopAxis.selectAll(".tick text")
    

    const yBottomAxisUpdate = axis.selectAll(".yBottomAxis").data([null]);
    const yBottomAxisEnter = yBottomAxisUpdate.enter().append("g").classed("yBottomAxis", true);    
    const yBottomAxis = yBottomAxisUpdate.merge(yBottomAxisEnter).call( d3.axisLeft().scale(yBottomScale).tickSizeOuter(0) ); //ラベルを内向きにする
    
    yBottomAxis.selectAll(".tick line").remove();  
    yBottomAxis.selectAll(".tick text");

    const splitLineUpdata = axis.selectAll(".splitLine").data([null]);
    const splitLineEnter = splitLineUpdata.enter().append("line").classed("splitLine", true);
    const splitLine = splitLineUpdata.merge(splitLineEnter);
    
    splitLine
      .attr("x1", -(m.left/2))
      .attr("x2", pw + (m.right/2))
      .attr("y1", hph)
      .attr("y2", hph)
    
    
    
    //x axis
    const xAxisUpdate = axis.selectAll(".xAxis").data([null]);
    const xAxisEnter = xAxisUpdate.enter().append("g").classed("xAxis", true);
    
    const renderAxis =  d3.axisBottom().scale(xScale);
    
    const xAxis = xAxisUpdate.merge(xAxisEnter).call(renderAxis)
        .attr("transform", `translate(0, ${ph})`);
        
    xAxis.select(".domain").remove();
    xAxis.selectAll(".tick line").remove();
        
    
    //grid layer    
    grid.attr("transform", `translate(${m.left}, ${m.top})`);
    
    
    //x grid    
    const xGridUpdate = grid.selectAll(".xGrid").data([null]);
    const xGridEnter = xGridUpdate.enter().append("g").classed("xGrid", true);
    
    const xGrid = xGridUpdate.merge(xGridEnter)
        .call( d3.axisBottom().scale(xScale).tickSizeInner(-ph).tickFormat(() => null ) )
        .attr("transform", `translate(0, ${ph})`);    

    xGrid.select(".domain").remove();
    
    
    
   //plot layer 
    plot.attr("transform", `translate(${m.left}, ${m.top})`);
    
    //top bar
    const topBarsUpdate = plot.selectAll(".topBar").data(data);
    const topBarsEnter = topBarsUpdate.enter().append("rect").classed("topBar", true);
    
    const topBars = topBarsUpdate.merge(topBarsEnter)
        .attr("height", yTopScale.bandwidth())
        .attr("width", d => xScale(d.value1))
        .attr("y", d => yTopScale(d.title))
        .attr("x", 0.5) 
        ;
        
    //bottom bar
    const bottomBarsUpdate = plot.selectAll(".bottomBar").data(data);
    const bottomBarsEnter = bottomBarsUpdate.enter().append("rect").classed("bottomBar", true);
    
    const bottomBars = bottomBarsUpdate.merge(bottomBarsEnter)
        .attr("height", yBottomScale.bandwidth())
        .attr("width", d => xScale(d.value2))
        .attr("y", d => yBottomScale(d.title))
        .attr("x", 0.5) 
        ;
        
        
}

//divエレメントをドラッグでリサイズできるようにする。
const dispatch = d3.dispatch("resize");    
dispatch.on("resize", render);
setResizeControler();


function setResizeControler(){
    const drag = d3.drag()
        .on("drag", resized)
    
    d3.select("#chart")    
        .call(drag);
    
    function resized(e){
        const s = d3.event.sourceEvent;
        const w = (s.pageX < 300) ? 300 : s.pageX;
        const h = (s.pageY < 200) ? 200 : s.pageY;
        
        console.log(h)
        d3.select(this)
            .style("width", `${w}px`)
            .style("height", `${h}px`)
            .attr("data-test", "test")
        
        dispatch.call("resize");
        
    }
    
}
</script>


</body>
</html>