D3:缓慢的可缩放热图 [英] D3: slow zoomable heatmap

查看:110
本文介绍了D3:缓慢的可缩放热图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个可缩放的热图,在放大或缩小时看起来太慢了。是否有任何东西可以使它更快/更顺畅,或者只是太多点,这是我能拥有的最好的。我想知道是否有一些技巧可以让浏览器更轻松,同时保持工具提示等增强功能。或者我处理缩放功能的代码可能不是很好。

I have this zoomable heatmap, which looks too slow when zooming-in or out. Is there anything to make it faster/smoother or it is just too many points and that is the best I can have. I was wondering if there is some trick to make it lighter for the browser please while keeping enhancements like tooltips. Or maybe my code handling the zoom feature is not great .

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <style>
        .axis text {
            font: 10px sans-serif;
        }

        .axis path,
        .axis line {
            fill: none;
            stroke: #000000;
        }

        .x.axis path {
            //display: none;
        }

        .chart rect {
            fill: steelblue;
        }

        .chart text {
            fill: white;
            font: 10px sans-serif;
            text-anchor: end;
        }
        
        #tooltip {
          position:absolute;
          background-color: #2B292E;
          color: white;
          font-family: sans-serif;
          font-size: 15px;
          pointer-events: none; /*dont trigger events on the tooltip*/
          padding: 15px 20px 10px 20px;
          text-align: center;
          opacity: 0;
          border-radius: 4px;
        }
    </style>
    <title>Bar 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>
</head>

<body>
    <div id="chart" style="width: 700px; height: 500px"></div>
    <script>
        var dataset = [];
        for (let i = 1; i < 360; i++) {
            for (j = 1; j < 75; j++) {
                dataset.push({
                    day: i,
                    hour: j,
                    tOutC: Math.random() * 25,
                })
            }
        };


        var days = d3.max(dataset, function(d) {
                return d.day;
            }) -
            d3.min(dataset, function(d) {
                return d.day;
            });
        var hours = d3.max(dataset, function(d) {
                return d.hour;
            }) -
            d3.min(dataset, function(d) {
                return d.hour;
            });

        var tMin = d3.min(dataset, function(d) {
                return d.tOutC;
            }),
            tMax = d3.max(dataset, function(d) {
                return d.tOutC;
            });

        var dotWidth = 1,
            dotHeight = 3,
            dotSpacing = 0.5;

        var margin = {
                top: 0,
                right: 25,
                bottom: 40,
                left: 25
            },
            width = (dotWidth * 2 + dotSpacing) * days,
            height = (dotHeight * 2 + dotSpacing) * hours; 


        var colors = ['#2C7BB6', '#00A6CA','#00CCBC','#90EB9D','#FFFF8C','#F9D057','#F29E2E','#E76818','#D7191C'];

        var xScale = d3.scaleLinear()
            .domain(d3.extent(dataset, function(d){return d.day}))
            .range([0, width]);

        var yScale = d3.scaleLinear()
            .domain(d3.extent(dataset, function(d){return d.hour}))
            .range([(dotHeight * 2 + dotSpacing) * hours, dotHeight * 2 + dotSpacing]);

        var colorScale = d3.scaleQuantile()
            .domain([0, colors.length - 1, d3.max(dataset, function(d) {
                return d.tOutC;
            })])
            .range(colors);

        var xAxis = d3.axisBottom().scale(xScale);



        // Define Y axis
        var yAxis = d3.axisLeft().scale(yScale);


        var zoom = d3.zoom()
            .scaleExtent([dotWidth, dotHeight])
            .translateExtent([
                [80, 20],
                [width, height]
            ])
            .on("zoom", zoomed);

        var tooltip = d3.select("body").append("div")
        .attr("id", "tooltip")
        .style("opacity", 0);

        // SVG canvas
        var svg = d3.select("#chart")
            .append("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);


        // Heatmap dots
        svg.append("g")
            .attr("clip-path", "url(#clip)")
            .selectAll("ellipse")
            .data(dataset)
            .enter()
            .append("ellipse")
            .attr("cx", function(d) {
                return xScale(d.day);
            })
            .attr("cy", function(d) {
                return yScale(d.hour);
            })
            .attr("rx", dotWidth)
            .attr("ry", dotHeight)
            .attr("fill", function(d) {
                return colorScale(d.tOutC);
            })
            .on("mouseover", function(d){
                $("#tooltip").html("X: "+d.day+"<br/>Y:"+d.hour+"<br/>Value:"+Math.round(d.tOutC*100)/100);
                var xpos = d3.event.pageX +10;
                var ypos = d3.event.pageY +20;
                $("#tooltip").css("left",xpos+"px").css("top",ypos+"px").animate().css("opacity",1);
            }).on("mouseout", function(){
                $("#tooltip").animate({duration: 500}).css("opacity",0);
            }); 

        //Create X axis
        var renderXAxis = svg.append("g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + yScale(0) + ")")
            .call(xAxis)

        //Create Y axis
        var renderYAxis = svg.append("g")
            .attr("class", "y axis")
            .call(yAxis);


        function zoomed() {
            // update: rescale x axis
            renderXAxis.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));

            update();
        }

        function update() {
            // update: cache rescaleX value
            var rescaleX = d3.event.transform.rescaleX(xScale);
            svg.selectAll("ellipse")
                .attr('clip-path', 'url(#clip)')
                // update: apply rescaleX value
                .attr("cx", function(d) {
                    return rescaleX(d.day);
                })
//                .attr("cy", function(d) {
//                    return yScale(d.hour);
//                })
                // update: apply rescaleX value
                .attr("rx", function(d) {
                    return (dotWidth * d3.event.transform.k);
                })
                .attr("fill", function(d) {
                    return colorScale(d.tOutC);
                });
        }
        

        
        
    </script>
</body>

</html>

谢谢

推荐答案

解决方法不是更新缩放的所有点,而是将缩放变换应用于组包含点。
需要在额外的父母 g heatDotsGroup 上完成小组剪辑。

The solution is not to update all the dots for the zoom but to apply the zoom transform to the group containing the dots. Clipping of the group needs to be done on an additional parent g heatDotsGroup.

使用正则表达式替换来处理y的缩放比例(将其设置为固定为1),通过设置 transform.y 为0,并根据当前比例限制x的平移。

The zoom scale of y is taken care of (set it fixed to 1) with a regex replace, limit translate in y by setting the transform.y to 0, and limit the translate of x based on the current scale.

允许稍微翻译过去 0 在放大时显示第一个点完成。

Allow a little translate past 0 to show the first dot complete when zoomed in.

    var zoom = d3.zoom()
        .scaleExtent([dotWidth, dotHeight])
        .on("zoom", zoomed);

    // Heatmap dots
    var heatDotsGroup = svg.append("g")
        .attr("clip-path", "url(#clip)")
        .append("g");

    heatDotsGroup.selectAll("ellipse")
        .data(dataset)
        .enter()
        .append("ellipse")
        .attr("cx", function(d) { return xScale(d.day); })
        .attr("cy", function(d) { return yScale(d.hour); })
        .attr("rx", dotWidth)
        .attr("ry", dotHeight)
        .attr("fill", function(d) { return colorScale(d.tOutC); })
        .on("mouseover", function(d){
            $("#tooltip").html("X: "+d.day+"<br/>Y:"+d.hour+"<br/>Value:"+Math.round(d.tOutC*100)/100);
            var xpos = d3.event.pageX +10;
            var ypos = d3.event.pageY +20;
            $("#tooltip").css("left",xpos+"px").css("top",ypos+"px").animate().css("opacity",1);
        }).on("mouseout", function(){
            $("#tooltip").animate({duration: 500}).css("opacity",0);
        }); 

    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 );

        // update: rescale x axis
        renderXAxis.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));

        heatDotsGroup.attr("transform", d3.event.transform.toString().replace(/scale\((.*?)\)/, "scale($1, 1)"));
    }

这篇关于D3:缓慢的可缩放热图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆