D3'mouseenter'并同时缩放

时间:2017-05-01 08:27:53

标签: javascript d3.js

我有一个散射图,它具有完美的变焦功能。我正在尝试添加工具提示,以便在'mouseenter' <circle>元素上,工具提示会触发。我有这个工作,即'mouseenter'事件被调用,但当鼠标停留在此<circle>时我无法缩放。有没有办法让它们同时发生?

以下是重现此问题的最低版本代码。

<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<button id="resetBtn">Reset</button>
<div id="chart"></div>
<script>
    var data = [
        {
            x: 0.5, y: 0.5
        },
        {
            x: 0.6, y: 0.6
        },
        {
            x: 0.45, y: 0.65
        },
        {
            x: 0.76, y: 0.61
        },
        {
            x: 0.51, y: 0.05
        },
        {
            x: 0.16, y: 6.8
        }
    ];

    var plot = volcanoPlot()
        .xColumn("x")
        .yColumn("y");

    d3.select('#chart')
        .data([data])
        .call(plot);

    function volcanoPlot() {
        var width = 960,
            height = 500,
            margin = {top: 20, right: 20, bottom: 40, left: 50},
            xColumn,
            dotRadius = 10,
            yColumn,
            xScale = d3.scaleLinear(),
            yScale = d3.scaleLog();

        function chart(selection){
            var innerWidth = width - margin.left - margin.right, // set the size of the chart within its container
                innerHeight = height - margin.top - margin.bottom;

            selection.each(function(data) {

                xScale.range([0, innerWidth])
                    .domain(d3.extent(data, function(d) { return d[xColumn]; }))
                    .nice();

                yScale.range([0, innerHeight])
                    .domain(d3.extent(data, function(d) { return d[yColumn]; }))
                    .nice();

                var zoom = d3.zoom()
                    .scaleExtent([1, 20])
                    .translateExtent([[0, 0], [width, height]])
                    .on('zoom', zoomFunction);

                var svg = d3.select(this).append('svg')
                    .attr('height', height)
                    .attr('width', width)
                  .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                d3.select('#resetBtn')
                    .style('top', margin.top * 1.5 + 'px')
                    .style('left', margin.left * 1.25 + 'px')
                    .on('click', reset);

                svg.append('defs').append('clipPath')
                    .attr('id', 'clip')
                  .append('rect')
                    .attr('height', innerHeight)
                    .attr('width', innerWidth);

                // add the axes
                var xAxis = d3.axisBottom(xScale);
                var yAxis = d3.axisLeft(yScale);

                var gX = svg.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(0,' + innerHeight + ')')
                    .call(xAxis);

                gX.append('text')
                    .attr('class', 'label')
                    .attr('transform', 'translate(' + width / 2 + ',' + (margin.bottom - 6) + ')')
                    .attr('text-anchor', 'middle')
                    .text(xColumn);

                var gY = svg.append('g')
                    .attr('class', 'y axis')
                    .call(yAxis);

                gY.append('text')
                    .attr('class', 'label')
                    .attr('transform', 'translate(' + (0 - margin.left / 1.5) + ',' + (height / 2) + ') rotate(-90)')
                    .style('text-anchor', 'middle')
                    .text(yColumn);

                var zoomBox = svg.append('rect')
                    .attr('class', 'zoom')
                    .attr('height', innerHeight)
                    .attr('width', innerWidth)
                    .attr('fill', 'none')
                    .attr('pointer-events', 'all')
                    .call(zoom);

                var circles = svg.append('g')
                    .attr('class', 'circlesContainer')
                    .attr('clip-path', 'url(#clip)');

                circles.selectAll(".dot")
                    .data(data)
                    .enter().append('circle')
                    .attr('r', dotRadius)
                    .attr('cx', function(d) { return xScale(d[xColumn]); })
                    .attr('cy', function(d) { return yScale(d[yColumn]); })
                    .attr('class', 'dot')
                    .attr('stroke', 'none')
                    .on('mouseenter', function(){
                        console.log("hi");
                    });

                function zoomFunction() {
                    var transform = d3.zoomTransform(this);
                    d3.selectAll('.dot')
                        .attr('transform', transform)
                        .attr('r', dotRadius / Math.sqrt(transform.k));
                    gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
                    gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
                }

                function reset() {
                    var ease = d3.easePolyIn.exponent(4.0);
                    d3.select('.zoom')
                        .transition().duration(750)
                        .ease(ease)
                        .call(zoom.transform, d3.zoomIdentity);
                }
            });
        }

        chart.width = function(value) {
            if (!arguments.length) return width;
            width = value;
            return chart;
        };

        chart.height = function(value) {
            if (!arguments.length) return height;
            height = value;
            return chart;
        };

        chart.margin = function(value) {
            if (!arguments.length) return margin;
            margin = value;
            return chart;
        };

        chart.xColumn = function(value) {
            if (!arguments.length) return xColumn;
            xColumn = value;
            return chart;
        };

        chart.yColumn = function(value) {
            if (!arguments.length) return yColumn;
            yColumn = value;
            return chart;
        };
        return chart;
    }
</script>
</body>
</html>

1 个答案:

答案 0 :(得分:1)

您在矩形上调用缩放功能,因此,当您将鼠标悬停在圆圈上时,缩放功能将无效。它与mouseenter功能无关:您可以移除mouseenter,当您将鼠标悬停在圆圈上时,缩放功能仍然无效。

一个简单的解决方案是调用SVG组上的缩放,而不是矩形:

var svg = d3.select(this).append('svg')
    .attr('height', height)
    .attr('width', width)
    .append('g')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
    .call(zoom);

以下是仅包含此更改的代码:

&#13;
&#13;
<!DOCTYPE html>
<html lang="en">
<head>
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<button id="resetBtn">Reset</button>
<div id="chart"></div>
<script>
    var data = [
        {
            x: 0.5, y: 0.5
        },
        {
            x: 0.6, y: 0.6
        },
        {
            x: 0.45, y: 0.65
        },
        {
            x: 0.76, y: 0.61
        },
        {
            x: 0.51, y: 0.05
        },
        {
            x: 0.16, y: 6.8
        }
    ];

    var plot = volcanoPlot()
        .xColumn("x")
        .yColumn("y");

    d3.select('#chart')
        .data([data])
        .call(plot);

    function volcanoPlot() {
        var width = 960,
            height = 500,
            margin = {top: 20, right: 20, bottom: 40, left: 50},
            xColumn,
            dotRadius = 10,
            yColumn,
            xScale = d3.scaleLinear(),
            yScale = d3.scaleLog();

        function chart(selection){
            var innerWidth = width - margin.left - margin.right, // set the size of the chart within its container
                innerHeight = height - margin.top - margin.bottom;

            selection.each(function(data) {

                xScale.range([0, innerWidth])
                    .domain(d3.extent(data, function(d) { return d[xColumn]; }))
                    .nice();

                yScale.range([0, innerHeight])
                    .domain(d3.extent(data, function(d) { return d[yColumn]; }))
                    .nice();

                var zoom = d3.zoom()
                    .scaleExtent([1, 20])
                    .translateExtent([[0, 0], [width, height]])
                    .on('zoom', zoomFunction);

                var svg = d3.select(this).append('svg')
                    .attr('height', height)
                    .attr('width', width)
                  .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
                    .call(zoom);

                d3.select('#resetBtn')
                    .style('top', margin.top * 1.5 + 'px')
                    .style('left', margin.left * 1.25 + 'px')
                    .on('click', reset);

                svg.append('defs').append('clipPath')
                    .attr('id', 'clip')
                  .append('rect')
                    .attr('height', innerHeight)
                    .attr('width', innerWidth);

                // add the axes
                var xAxis = d3.axisBottom(xScale);
                var yAxis = d3.axisLeft(yScale);

                var gX = svg.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(0,' + innerHeight + ')')
                    .call(xAxis);

                gX.append('text')
                    .attr('class', 'label')
                    .attr('transform', 'translate(' + width / 2 + ',' + (margin.bottom - 6) + ')')
                    .attr('text-anchor', 'middle')
                    .text(xColumn);

                var gY = svg.append('g')
                    .attr('class', 'y axis')
                    .call(yAxis);

                gY.append('text')
                    .attr('class', 'label')
                    .attr('transform', 'translate(' + (0 - margin.left / 1.5) + ',' + (height / 2) + ') rotate(-90)')
                    .style('text-anchor', 'middle')
                    .text(yColumn);

                var zoomBox = svg.append('rect')
                    .attr('class', 'zoom')
                    .attr('height', innerHeight)
                    .attr('width', innerWidth)
                    .attr('fill', 'none')
                    .attr('pointer-events', 'all');

                var circles = svg.append('g')
                    .attr('class', 'circlesContainer')
                    .attr('clip-path', 'url(#clip)');

                circles.selectAll(".dot")
                    .data(data)
                    .enter().append('circle')
                    .attr('r', dotRadius)
                    .attr('cx', function(d) { return xScale(d[xColumn]); })
                    .attr('cy', function(d) { return yScale(d[yColumn]); })
                    .attr('class', 'dot')
                    .attr('stroke', 'none')
                    .on('mouseenter', function(){
                        console.log("hi");
                    });

                function zoomFunction() {
                    var transform = d3.zoomTransform(this);
                    d3.selectAll('.dot')
                        .attr('transform', transform)
                        .attr('r', dotRadius / Math.sqrt(transform.k));
                    gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
                    gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
                }

                function reset() {
                    var ease = d3.easePolyIn.exponent(4.0);
                    d3.select('.zoom')
                        .transition().duration(750)
                        .ease(ease)
                        .call(zoom.transform, d3.zoomIdentity);
                }
            });
        }

        chart.width = function(value) {
            if (!arguments.length) return width;
            width = value;
            return chart;
        };

        chart.height = function(value) {
            if (!arguments.length) return height;
            height = value;
            return chart;
        };

        chart.margin = function(value) {
            if (!arguments.length) return margin;
            margin = value;
            return chart;
        };

        chart.xColumn = function(value) {
            if (!arguments.length) return xColumn;
            xColumn = value;
            return chart;
        };

        chart.yColumn = function(value) {
            if (!arguments.length) return yColumn;
            yColumn = value;
            return chart;
        };
        return chart;
    }
</script>
</body>
</html>
&#13;
&#13;
&#13;