在d3.js中的拖放操作期间,不会触发鼠标悬停事件

时间:2014-02-03 09:44:28

标签: javascript d3.js

我正在尝试将圆圈拖过其他圆圈以连接两条路径。问题是当我将一个圆圈(c2)拖到另一个圆圈(c1)上时,如果在圆圈c2之前创建圆圈c1,则不会触发c1鼠标悬停事件。

Here the js fiddle link

    var closedRoad = true;

    var width = 960,
        height = 500;

    var points = d3.range(1, 5).map(function(i) {
        return [i * width / 5, 50 + Math.random() * (height - 100)];
    });

    var points1 = d3.range(1, 5).map(function(i) {
        return [i * width / 5, 50 + Math.random() * (height - 100)];
    });

    var points2 = d3.range(1, 5).map(function(i) {
        return [i * width / 5, 50 + Math.random() * (height - 100)];
    });

    var count = 0;
    var ways =  [];

    var currentWay = null;

    var selected = null;

    var line = d3.svg.line();

    var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

    var rect = svg.append("rect")
    .attr("width", width)
    .attr("height", height);

    ways.forEach(function(way, i) {
        svg.append("path")
        .datum(way)
        .attr("id", "p" + way.id)
        .attr("class", "line")
        .call(redraw);
    });

    d3.select(window)
    .on("keydown", keydown)
    //.on("mousemove", mousemove)
    //.on("mouseup", mouseup)
    .on("mousedown", mousedown)
    .on("dblclick", dblclick);

    function redraw(way) {
        way.attr("d", function(d) { return line(d.pts); });

        var circle = svg.selectAll(".way" + way.data()[0].id)
        .data(way.data()[0].pts, function(d) { return d; });
        circle.enter().append("circle")
        .attr("class", "way" + way.data()[0].id)
        .attr("r", 1e-6)
        .on("mousedown", function(d) { 
            if (closedRoad) {
                currentWay = way.data()[0];
                selected = d; 

                if (d3.event) {
                    d3.event.preventDefault();
                    d3.event.stopPropagation();
                }
            }
        })
        .on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
        .on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); })
        .transition()
        .duration(750)
        .ease("elastic")
        .attr("r", 6.5);

        circle
        .attr("cx", function(d) { return d[0]; })
        .attr("cy", function(d) { return d[1]; });

        ////
        var drag = d3.behavior.drag();
        drag.on("dragstart", function(d) {
            console.log('START');
        })
        .on("drag", function(d) {
            console.log('MOVE');

            var m = d3.mouse(svg.node());
            d[0] = Math.max(0, Math.min(width, m[0]));
            d[1] = Math.max(0, Math.min(height, m[1]));

            //redraw(way);
            redrawAll();
        })
        .on("dragend", function(d) {
            console.log('END');
        });

        circle.call(drag);
        ////

        circle.exit().remove();

    }

    function dblclick() {
        currentWay.pts.pop();
        //redraw(svg.select("#p" + currentWay.id));
        redrawAll();

        closedRoad = true;
    }

    function mousedown() {
        if (closedRoad) {
            currentWay = { id: ++count, pts: [] };
            ways.push(currentWay); 

            svg.append("path")
            .datum(currentWay)
            .attr("id", "p" + currentWay.id)
            .attr("class", "line")
            .on("mouseover", function() { 
                d3.select(d3.event.target).classed("highlight", true); 
            })
            .on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); })
            .call(redraw);

            closedRoad = false;
        }

        currentWay.pts.push(selected = d3.mouse(svg.node()));
        //redraw(svg.select("#p" + currentWay.id));
        redrawAll();
    }

    function redrawAll() {
        ways.forEach(function(way, i) {
            redraw(svg.select("#p" + way.id));
        });
    }

    function mousemove() {

    }


    function keydown() {
        if (!selected) return;
        switch (d3.event.keyCode) {
            case 8: // backspace
            case 46: { // delete
                var i = currentWay.pts.indexOf(selected);
                currentWay.pts.splice(i, 1);
                selected = currentWay.pts.length ? currentWay.pts[i > 0 ? i - 1 : 0] : null;
                redraw(svg.select("#p" + currentWay.id));
                break;
            }
        }
    }

你能帮助我吗?

谢谢! 前!

2 个答案:

答案 0 :(得分:5)

问题很简单,只有当两个元素一个在另一个上面绘制时,才会在最顶层的元素上触发'mouseover'事件。无论您是否正在处理最顶层元素的鼠标事件,都是如此。改变这种行为需要大量的解决方案,其中没有一个是理想的。

一些可能的解决方案:

  • 在拖动功能中,反复检查此时是否有另一个圆圈。

    您可以使用SVGSVGElement.getIntersectionList()方法查找给定矩形中的所有元素。请注意,此方法在SVG 节点上调用,而不是在d3选择上调用。

  • 拖动节点时,通过在其上设置样式pointer-events:none;,使其对鼠标事件“透明”。

    当然,这也会使它对拖动事件透明,因此您必须将拖动事件添加到容器而不是节点,然后找出在'dragstart'事件中拖动的圈子,更改它的指针 - 事件样式,并将圆形选择存储在drag函数可访问的变量中(将其移动与当前代码相同)和dragend函数(将重新设置其指针) - 事件风格)。您还需要向分配了拖动行为的<svg><g>元素添加背景矩形,这样即使鼠标只是“透明”,它也会响应鼠标事件圈。

  • 拖动节点时,将其移动到绘制顺序的底部,以便将所有其他节点绘制为顶部。

    由于SVG没有像HTML那样的“z-index”属性,因此实现此目的的唯一方法是使用selection.order()或普通Javascript insertBefore()实际重新排序DOM。

  • 或者,接受这只是一个小的美学缺陷,并没有改变你的代码的功能,所以不值得上述方法的麻烦和性能影响。

    如果您不喜欢某些圈子在拖动时改变颜色这一事实,您可以在svg上设置一个“拖动”类,用于拖动持续时间,并让你的css用于如果不是'svg.dragging'的孩子,'highlight'类只会做出明显的改变。

答案 1 :(得分:1)

如果用户正在移动圈子,我添加了一些代码,如果他不打算创建一个新圈子,我希望这会有所帮助。

    var closedRoad = true;
    var width = 960,
        height = 500;

    var points = d3.range(1, 5).map(function(i) {
        return [i * width / 5, 50 + Math.random() * (height - 100)];
    });

    var points1 = d3.range(1, 5).map(function(i) {
        return [i * width / 5, 50 + Math.random() * (height - 100)];
    });

    var points2 = d3.range(1, 5).map(function(i) {
        return [i * width / 5, 50 + Math.random() * (height - 100)];
    });

    var count = 0;
    var ways =  [];

    var currentWay = null;

    var selected = null;

    var line = d3.svg.line();

    var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

    var rect = svg.append("rect")
    .attr("width", width)
    .attr("height", height);

    ways.forEach(function(way, i) {
        svg.append("path")
        .datum(way)
        .attr("id", "p" + way.id)
        .attr("class", "line")
        .call(redraw);
    });

    d3.select(window)
    .on("keydown", keydown)
    //.on("mousemove", mousemove)
    .on("mouseup", mouseup)
    .on("mousedown", mousedown)
    .on("dblclick", dblclick);

    function redraw(way) {
        way.attr("d", function(d) { return line(d.pts); });

        var circle = svg.selectAll(".way" + way.data()[0].id)
        .data(way.data()[0].pts, function(d) { return d; });
        circle.enter().append("circle")
        .attr("class", "way" + way.data()[0].id)
        .attr("r", 1e-6)
        .on("mousedown", function(d) { 
            if (closedRoad) {
                currentWay = way.data()[0];
                selected = d; 

                if (d3.event) {
                    d3.event.preventDefault();
                    d3.event.stopPropagation();
                }
            }
        })
        .on("mouseover", function() { d3.select(d3.event.target).classed("highlight", true); })
        .on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); })
        .transition()
        .duration(750)
        .ease("elastic")
        .attr("r", 6.5);

        circle
        .attr("cx", function(d) { return d[0]; })
        .attr("cy", function(d) { return d[1]; });

        ////
        var drag = d3.behavior.drag();
        drag.on("dragstart", function(d) {
            console.log('START');
        })
        .on("drag", function(d) {
            console.log('MOVE');

            var m = d3.mouse(svg.node());
            d[0] = Math.max(0, Math.min(width, m[0]));
            d[1] = Math.max(0, Math.min(height, m[1]));

            //redraw(way);
            redrawAll();
        })
        .on("dragend", function(d) {
            console.log('END');
        });

        circle.call(drag);
        ////

        circle.exit().remove();

        /*   if (d3.event) {
            d3.event.preventDefault();
            d3.event.stopPropagation();
        }*/
    }

    function dblclick() {
        currentWay.pts.pop();
        //redraw(svg.select("#p" + currentWay.id));
        redrawAll();

        closedRoad = true;
    }

    function mousedown() {
        start = event.pageX + event.pageY;
        console.log(start);

    }
    function mouseup(){
        if(start >= (event.pageX + event.pageY) - 10 && start <= (event.pageX + event.pageY) + 10){
        if (closedRoad) {
            currentWay = { id: ++count, pts: [] };
            ways.push(currentWay); 

            svg.append("path")
            .datum(currentWay)
            .attr("id", "p" + currentWay.id)
            .attr("class", "line")
            .on("mouseover", function() { 
                d3.select(d3.event.target).classed("highlight", true); 
            })
            .on("mouseout", function() { d3.select(d3.event.target).classed("highlight", false); })
            .call(redraw);

            closedRoad = false;
        }

        currentWay.pts.push(selected = d3.mouse(svg.node()));
        //redraw(svg.select("#p" + currentWay.id));
        redrawAll();
        }else{

        closedRoad = true;
        }
    }
    function redrawAll() {
        ways.forEach(function(way, i) {
            redraw(svg.select("#p" + way.id));
        });
    }

    function mousemove() {

    }


    function keydown() {
        if (!selected) return;
        switch (d3.event.keyCode) {
            case 8: // backspace
            case 46: { // delete
                var i = currentWay.pts.indexOf(selected);
                currentWay.pts.splice(i, 1);
                selected = currentWay.pts.length ? currentWay.pts[i > 0 ? i - 1 : 0] : null;
                redraw(svg.select("#p" + currentWay.id));
                break;
            }
        }
    }