代码之家  ›  专栏  ›  技术社区  ›  Aenaon

d3.js:输入更新退出模式-未删除旧点

  •  3
  • Aenaon  · 技术社区  · 6 年前

    我正试图在下面的图表上执行进入更新退出模式(它是在某种非常好的PPL的巨大帮助下构建的,所以,现在我又陷入了不幸。我不能使模式工作,但我确信我找到了正确的对象(命名为 heatDotsGroup 在下面的代码中)。

    不过,我可以检查chrome的开发工具,该对象包含节点(省略号),但模式不起作用,因此显然我做错了什么。

    有什么想法吗?多谢!

    function heatmap(dataset) {
        
        var svg = d3.select("#chart")
            .select("svg")
        
        var xLabels = [],
            yLabels = [];
        for (i = 0; i < dataset.length; i++) {
            if (i==0){
                xLabels.push(dataset[i].xLabel);
                var j = 0;
                while (dataset[j+1].xLabel == dataset[j].xLabel){
                    yLabels.push(dataset[j].yLabel);
                    j++;
                }
                yLabels.push(dataset[j].yLabel);
            } else {
                if (dataset[i-1].xLabel == dataset[i].xLabel){
                    //do nothing
                } else {
                    xLabels.push(dataset[i].xLabel);                    
                }
            }
        };
    
        var margin = {top: 0, right: 25,
                      bottom: 60, left: 75};  
    
        var width = +svg.attr("width") - margin.left - margin.right,
            height = +svg.attr("height") - margin.top - margin.bottom;
    
        var dotSpacing = 0,
            dotWidth = width/(2*(xLabels.length+1)),
            dotHeight = height/(2*yLabels.length);
    
        var daysRange = d3.extent(dataset, function (d) {return d.xKey}),
            days = daysRange[1] - daysRange[0];
        
        var hoursRange = d3.extent(dataset, function (d) {return d.yKey}),
            hours = hoursRange[1] - hoursRange[0];    
        
        var tRange = d3.extent(dataset, function (d) {return d.val}),
            tMin = tRange[0],
            tMax = tRange[1];
    
        var colors = ['#2C7BB6', '#00A6CA', '#00CCBC', '#90EB9D', '#FFFF8C', '#F9D057', '#F29E2E', '#E76818', '#D7191C'];
        
        // the scale
        var scale = {
            x: d3.scaleLinear()
               .range([-1, width]),
            y: d3.scaleLinear()
               .range([height, 0]),
        };
        
        var xBand = d3.scaleBand().domain(xLabels).range([0, width]),
            yBand = d3.scaleBand().domain(yLabels).range([height, 0]);
        
        var axis = {
            x: d3.axisBottom(scale.x).tickFormat((d, e) => xLabels[d]),
            y: d3.axisLeft(scale.y).tickFormat((d, e) => yLabels[d]),
        };
    
    
        function updateScales(data){
            scale.x.domain([0, d3.max(data, d => d.xKey)]),
            scale.y.domain([ 0, d3.max(data, d => d.yKey)])
        }
    
        var colorScale = d3.scaleQuantile()
            .domain([0, colors.length - 1, d3.max(dataset, function (d) {return d.val;})])
            .range(colors);
    
        var zoom = d3.zoom()
            .scaleExtent([1, dotHeight])
            .on("zoom", zoomed);
    
        var tooltip = d3.select("body").append("div")
            .attr("id", "tooltip")
            .style("opacity", 0);
    
        // SVG canvas
        svg = d3.select("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .call(zoom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
        // Clip path
        svg.append("clipPath")
            .attr("id", "clip")
            .append("rect")
            .attr("width", width)
            .attr("height", height+dotHeight);
    
    
        // Heatmap dots
        var heatDotsGroup = svg.append("g")
            .attr("clip-path", "url(#clip)")
            .append("g");
            
    
        //Create X axis
        var renderXAxis = svg.append("g")
            .attr("class", "x axis")
            //.attr("transform", "translate(0," + scale.y(-0.5) + ")")
            //.call(axis.x)
    
        //Create Y axis
        var renderYAxis = svg.append("g")
            .attr("class", "y axis")
            .call(axis.y);
    
    
        function zoomed() {
            d3.event.transform.y = 0;
            d3.event.transform.x = Math.min(d3.event.transform.x, 5);
            d3.event.transform.x = Math.max(d3.event.transform.x, (1 - d3.event.transform.k) * width);
            // console.log(d3.event.transform)
    
            // update: rescale x axis
            renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));
    
            // Make sure that only the x axis is zoomed
            heatDotsGroup.attr("transform", d3.event.transform.toString().replace(/scale\((.*?)\)/, "scale($1, 1)"));
        }
        
        svg.call(renderPlot, dataset)
        
        function renderPlot(selection, dataset){
            
            //Do the axes
            updateScales(dataset)
            selection.select('.y.axis').call(axis.y)
            selection.select('.x.axis')
                    .attr("transform", "translate(0," + scale.y(-0.5) + ")")
                    .call(axis.x)
               
            
            // Do the chart
            const update = heatDotsGroup.selectAll("ellipse")
            .data(dataset);
            
            update
            .enter()
            .append("ellipse")
            .attr("cx", function (d) {return scale.x(d.xKey) - xBand.bandwidth();})
            .attr("cy", function (d) {return scale.y(d.yKey) + yBand.bandwidth();})
            .attr("rx", dotWidth)
            .attr("ry", dotHeight)
            .attr("fill", function (d) {
                return colorScale(d.val);}
                )
            .merge(update).transition().duration(800);   
            
            update.exit().remove();
            
        }
    
    
    };
    #clickMe{
        height:50px;
        width:150px;
        background-color:lavender;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="utf-8">
        
        <title>Heatmap Chart</title>
    
        <!-- Reference style.css -->
        <!--    <link rel="stylesheet" type="text/css" href="style.css">-->
    
        <!-- Reference minified version of D3 -->
        <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
        <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
        <script src='heatmap_v4.js' type='text/javascript'></script>
    </head>
    
    <body>
    <input id="clickMe" type="button" value="click me to push new data" onclick="run();" />
    
        <div id='chart'>
        <svg width="700" height="500">
          <g class="focus">
            <g class="xaxis"></g>
            <g class="yaxis"></g>
          </g>
        </svg>
       </div>
        
       
        <script>
            function run() {
                var dataset = [];
                    for (let i = 1; i < 360; i++) { //360
                        for (j = 1; j < 7; j++) {  //75
                            dataset.push({
                                xKey: i,
                                xLabel: "xMark " + i,
                                yKey: j,
                                yLabel: "yMark " + j,
                                val: Math.random() * 25,
                            })
                            }
                        };
    
                        heatmap(dataset)
            }
    
            $(document).ready(function() {});
        </script>
    </body>
    
    </html>
    2 回复  |  直到 6 年前
        1
  •  1
  •   Andrew Reid    6 年前

    1. g

    console.log(update.size(),update.exit().size()) // *Without any merge* 
    

    // Things to set/append once:
    var svg = d3.select("#chart")
      .select("svg")
    
    var margin = {top: 0, right: 25,bottom: 60, left: 75};  
    var width = +svg.attr("width") - margin.left - margin.right,
      height = +svg.attr("height") - margin.top - margin.bottom;
      
    svg = svg.attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
            .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    var clip = svg.append("clipPath")
            .attr("id", "clip")
            .append("rect")
    
    
    var heatDotsGroup = svg.append("g")
      .attr("clip-path", "url(#clip)")
      .append("g");
    
    var xAxis = svg.append("g").attr("class", "x axis");
    var yAxis = svg.append("g").attr("class", "y axis")
         
    
            
    function heatmap(dataset) {
        
        
        var xLabels = [],
            yLabels = [];
        for (i = 0; i < dataset.length; i++) {
            if (i==0){
                xLabels.push(dataset[i].xLabel);
                var j = 0;
                while (dataset[j+1].xLabel == dataset[j].xLabel){
                    yLabels.push(dataset[j].yLabel);
                    j++;
                }
                yLabels.push(dataset[j].yLabel);
            } else {
                if (dataset[i-1].xLabel == dataset[i].xLabel){
                    //do nothing
                } else {
                    xLabels.push(dataset[i].xLabel);                    
                }
            }
        };
    
    
    
        var dotSpacing = 0,
            dotWidth = width/(2*(xLabels.length+1)),
            dotHeight = height/(2*yLabels.length);
    
        var daysRange = d3.extent(dataset, function (d) {return d.xKey}),
            days = daysRange[1] - daysRange[0];
        
        var hoursRange = d3.extent(dataset, function (d) {return d.yKey}),
            hours = hoursRange[1] - hoursRange[0];    
        
        var tRange = d3.extent(dataset, function (d) {return d.val}),
            tMin = tRange[0],
            tMax = tRange[1];
    
        var colors = ['#2C7BB6', '#00A6CA', '#00CCBC', '#90EB9D', '#FFFF8C', '#F9D057', '#F29E2E', '#E76818', '#D7191C'];
        
        // the scale
        var scale = {
            x: d3.scaleLinear()
               .range([-1, width]),
            y: d3.scaleLinear()
               .range([height, 0]),
        };
        
        var xBand = d3.scaleBand().domain(xLabels).range([0, width]),
            yBand = d3.scaleBand().domain(yLabels).range([height, 0]);
        
        var axis = {
            x: d3.axisBottom(scale.x).tickFormat((d, e) => xLabels[d]),
            y: d3.axisLeft(scale.y).tickFormat((d, e) => yLabels[d]),
        };
    
    
        function updateScales(data){
            scale.x.domain([0, d3.max(data, d => d.xKey)]),
            scale.y.domain([ 0, d3.max(data, d => d.yKey)])
        }
    
        var colorScale = d3.scaleQuantile()
            .domain([0, colors.length - 1, d3.max(dataset, function (d) {return d.val;})])
            .range(colors);
    
        var zoom = d3.zoom()
            .scaleExtent([1, dotHeight])
            .on("zoom", zoomed);
    
        var tooltip = d3.select("body").append("div")
            .attr("id", "tooltip")
            .style("opacity", 0);
    
        // SVG canvas
        svg.call(zoom);
    
    
        // Clip path
      clip.attr("width", width)
            .attr("height", height+dotHeight);
    
    
    
            
    
        //Create X axis
        var renderXAxis = xAxis 
            //.attr("transform", "translate(0," + scale.y(-0.5) + ")")
            //.call(axis.x)
    
        //Create Y axis
        var renderYAxis = yAxis.call(axis.y);
    
    
        function zoomed() {
            d3.event.transform.y = 0;
            d3.event.transform.x = Math.min(d3.event.transform.x, 5);
            d3.event.transform.x = Math.max(d3.event.transform.x, (1 - d3.event.transform.k) * width);
            // console.log(d3.event.transform)
    
            // update: rescale x axis
            renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));
    
            // Make sure that only the x axis is zoomed
            heatDotsGroup.attr("transform", d3.event.transform.toString().replace(/scale\((.*?)\)/, "scale($1, 1)"));
        }
        
        svg.call(renderPlot, dataset)
        
        function renderPlot(selection, dataset){
            
            //Do the axes
            updateScales(dataset)
            selection.select('.y.axis').call(axis.y)
            selection.select('.x.axis')
                    .attr("transform", "translate(0," + scale.y(-0.5) + ")")
                    .call(axis.x)
               
            
            // Do the chart
            const update = heatDotsGroup.selectAll("ellipse")
            .data(dataset);
            
            update
            .enter()
            .append("ellipse")
            .merge(update)
            .attr("cx", function (d) {return scale.x(d.xKey) - xBand.bandwidth();})
            .attr("cy", function (d) {return scale.y(d.yKey) + yBand.bandwidth();})
            .attr("rx", dotWidth)
            .attr("ry", dotHeight)
            .attr("fill", function (d) {
                return colorScale(d.val);}
                )
             
            
            update.exit().remove();
            
        }
    
    
    };
    #clickMe{
        height:50px;
        width:150px;
        background-color:lavender;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="utf-8">
        
        <title>Heatmap Chart</title>
    
        <!-- Reference style.css -->
        <!--    <link rel="stylesheet" type="text/css" href="style.css">-->
    
        <!-- Reference minified version of D3 -->
        <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
        <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
        <script src='heatmap_v4.js' type='text/javascript'></script>
    </head>
    
    <body>
    <input id="clickMe" type="button" value="click me to push new data" onclick="run();" />
    
        <div id='chart'>
        <svg width="700" height="500">
          <g class="focus">
            <g class="xaxis"></g>
            <g class="yaxis"></g>
          </g>
        </svg>
       </div>
        
       
        <script>
            function run() {
                var dataset = [];
                    for (let i = 1; i < 360; i++) { //360
                        for (j = 1; j < 7; j++) {  //75
                            dataset.push({
                                xKey: i,
                                xLabel: "xMark " + i,
                                yKey: j,
                                yLabel: "yMark " + j,
                                val: Math.random() * 25,
                            })
                            }
                        };
    
                        heatmap(dataset)
            }
    
            $(document).ready(function() {});
        </script>
    </body>
    
    </html>

        2
  •  1
  •   rioV8    6 年前

    1. renderPlot(dataset)
    2. #clip
    3. heatmap(dataset)
      1. datum

    function heatmap(dataset) {
    
    var svg = d3.select("#chart")
        .select("svg");
    
    var xLabels = [],
        yLabels = [];
    for (i = 0; i < dataset.length; i++) {
        if (i==0){
            xLabels.push(dataset[i].xLabel);
            var j = 0;
            while (dataset[j+1].xLabel == dataset[j].xLabel){
                yLabels.push(dataset[j].yLabel);
                j++;
            }
            yLabels.push(dataset[j].yLabel);
        } else {
            if (dataset[i-1].xLabel == dataset[i].xLabel){
                //do nothing
            } else {
                xLabels.push(dataset[i].xLabel);                    
            }
        }
    };
    
    var margin = {top: 0, right: 25,
                  bottom: 60, left: 75};  
    
    var width = +svg.attr("width") - margin.left - margin.right,
        height = +svg.attr("height") - margin.top - margin.bottom;
    
    var dotSpacing = 0,
        dotWidth = width/(2*(xLabels.length+1)),
        dotHeight = height/(2*yLabels.length);
    
    var daysRange = d3.extent(dataset, function (d) {return d.xKey}),
        days = daysRange[1] - daysRange[0];
    
    var hoursRange = d3.extent(dataset, function (d) {return d.yKey}),
        hours = hoursRange[1] - hoursRange[0];    
    
    var tRange = d3.extent(dataset, function (d) {return d.val}),
        tMin = tRange[0],
        tMax = tRange[1];
    
    var colors = ['#2C7BB6', '#00A6CA', '#00CCBC', '#90EB9D', '#FFFF8C', '#F9D057', '#F29E2E', '#E76818', '#D7191C'];
    
    // the scale
    var scale = {
        x: d3.scaleLinear()
           .range([-1, width]),
        y: d3.scaleLinear()
           .range([height, 0]),
    };
    
    var xBand = d3.scaleBand().domain(xLabels).range([0, width]),
        yBand = d3.scaleBand().domain(yLabels).range([height, 0]);
    
    var axis = {
        x: d3.axisBottom(scale.x).tickFormat((d, e) => xLabels[d]),
        y: d3.axisLeft(scale.y).tickFormat((d, e) => yLabels[d]),
    };
    
    var colorScale = d3.scaleQuantile()
        .domain([0, colors.length - 1, d3.max(dataset, function (d) {return d.val;})])
        .range(colors);
    
    var zoom = d3.zoom()
        .scaleExtent([1, dotHeight])
        .on("zoom", zoomed);
    
    var tooltip = d3.select("body").append("div")
        .attr("id", "tooltip")
        .style("opacity", 0);
    
    // SVG canvas
    svg .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .call(zoom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    
    // Clip path
    svg.append("clipPath")
        .attr("id", "clip")
        .append("rect")
        .attr("width", width)
        .attr("height", height+dotHeight);
    
    
    // Heatmap dots
    var heatDotsGroup = svg.append("g")
        .attr("clip-path", "url(#clip)")
        .append("g");
    
    //Create X axis
    var renderXAxis = svg.append("g")
        .attr("class", "x axis")
        //.attr("transform", "translate(0," + scale.y(-0.5) + ")")
        //.call(axis.x)
    
    //Create Y axis
    var renderYAxis = svg.append("g")
        .attr("class", "y axis")
        .call(axis.y);
    
    
    function zoomed() {
        d3.event.transform.y = 0;
        d3.event.transform.x = Math.min(d3.event.transform.x, 5);
        d3.event.transform.x = Math.max(d3.event.transform.x, (1 - d3.event.transform.k) * width);
        // console.log(d3.event.transform)
    
        // update: rescale x axis
        renderXAxis.call(axis.x.scale(d3.event.transform.rescaleX(scale.x)));
    
        // Make sure that only the x axis is zoomed
        heatDotsGroup.attr("transform", d3.event.transform.toString().replace(/scale\((.*?)\)/, "scale($1, 1)"));
    }
    
    var chartData = {};
    chartData.scale = scale;
    chartData.axis = axis;
    chartData.xBand = xBand;
    chartData.yBand = yBand;
    chartData.colorScale = colorScale;
    chartData.heatDotsGroup = heatDotsGroup;
    chartData.dotWidth = dotWidth;
    chartData.dotHeight = dotHeight;
    
    svg.datum(chartData);
    
    //svg.call(renderPlot, dataset)
    }
    function updateScales(data, scale){
        scale.x.domain([0, d3.max(data, d => d.xKey)]),
        scale.y.domain([0, d3.max(data, d => d.yKey)])
    }
    
    function renderPlot(dataset){
        
        var svg = d3.select("#chart")
            .select("svg");
        if (svg.select("#clip").empty()) { heatmap(dataset); }
        chartData = svg.datum();
        //Do the axes
        updateScales(dataset, chartData.scale);
        svg.select('.y.axis').call(chartData.axis.y)
        svg.select('.x.axis')
                .attr("transform", "translate(0," + chartData.scale.y(-0.5) + ")")
                .call(chartData.axis.x)
        
        // Do the chart
        const update = chartData.heatDotsGroup.selectAll("ellipse")
        .data(dataset);
        
        update
        .enter()
        .append("ellipse")
        .attr("rx", chartData.dotWidth)
        .attr("ry", chartData.dotHeight)
        .merge(update)
        .transition().duration(800)
            .attr("cx", function (d) {return chartData.scale.x(d.xKey) - chartData.xBand.bandwidth();})
            .attr("cy", function (d) {return chartData.scale.y(d.yKey) + chartData.yBand.bandwidth();})
            .attr("fill", function (d) { return chartData.colorScale(d.val);} );
        
        update.exit().remove();
    }
    #clickMe{
    height:50px;
    width:150px;
    background-color:lavender;
    }
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
    <meta charset="utf-8">
    
    <title>Heatmap Chart</title>
    
    <!-- Reference style.css -->
    <!--    <link rel="stylesheet" type="text/css" href="style.css">-->
    
    <!-- Reference minified version of D3 -->
    <script src='https://d3js.org/d3.v4.min.js' type='text/javascript'></script>
    <script src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js'></script>
    <script src='heatmap_v4.js' type='text/javascript'></script>
    </head>
    
    <body>
    <input id="clickMe" type="button" value="click me to push new data" onclick="run();" />
    
    <div id='chart'>
    <svg width="700" height="500">
      <g class="focus">
        <g class="xaxis"></g>
        <g class="yaxis"></g>
      </g>
    </svg>
       </div>
    
       
    <script>
        function run() {
            var dataset = [];
            for (let i = 1; i < 360; i++) { //360
                for (j = 1; j < 7; j++) {  //75
                    dataset.push({
                        xKey: i,
                        xLabel: "xMark " + i,
                        yKey: j,
                        yLabel: "yMark " + j,
                        val: Math.random() * 25,
                    })
                }
            };
    
            renderPlot(dataset)
        }
    
        $(document).ready(function() {});
    </script>
    </body>
    
    </html>