平移或缩放后,D3数据不会正确重新缩放

时间:2017-01-05 20:59:16

标签: javascript d3.js svg

我正在使用D3 v4和JS。我有一个散点图,其中加载了一组预定义的数据以及具有平移和缩放功能的轴。我需要能够动态添加点并最终在数据空间而不是像素空间中输出它们。我正在使用缩放对象的“rescaleX”和“rescaleY”方法。它们可以很好地重新调整轴,但是当我尝试添加新点时,绘制点的位置确实对应于鼠标位置。以下是代码的简化版本:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>

    var data = [{x:17,y:3},
        {x:20,y:16},
        {x:2,y:13},
        {x:19,y:10},
        {x:13,y:15},
        {x:2,y:2},
        {x:5,y:8},
        {x:11,y:19},
        {x:20,y:12},
        {x:10,y:20}];

    var width = 600;
    var height = 600;
    var padding = 50;
    var newXscale, newYscale;

    var dataScale = d3.scaleLinear()
        .domain([0,21])
        .range([0, width]);

    var svg = d3.select('body').append('svg')
        .attr('width', width+2*padding)
        .attr('height', height+2*padding)
        .on('click', clicked);

    var xAxis = d3.axisTop()
        .scale(dataScale);

    var gX = svg.append('g')
        .attr('transform','translate(50,50)')
        .call(xAxis);

    var yAxis = d3.axisLeft()
        .scale(dataScale);

    var gY = svg.append('g')
        .attr('transform','translate(50,50)')
        .call(yAxis);

    var canvas = svg.append('g')

    var points = canvas.append('g');
    points.selectAll('circle').data(data)
        .enter().append('circle')
        .attr('cx', function(d) {return dataScale(d.x)+padding})
        .attr('cy', function(d) {return dataScale(d.y)+padding})
        .attr('r', 5);

    var zoom
    var zoomOn = false;
    window.addEventListener('keydown', function (event) {
        if (event.key=='z') {
            if (zoomOn) {
                d3.select('#zoomBox').remove();
                zoomOn = false;
            } else {
                zoom = d3.zoom()
                    .scaleExtent([0.1, 10])
                    .on('zoom', zoomed);

                svg.append("rect")
                    .attr('cursor','move')
                    .attr("width", width+padding*2)
                    .attr("height", height+padding*2)
                    .attr('id','zoomBox')
                    .style("fill", "none")
                    .style("pointer-events", "all")
                    .call(zoom);
                zoomOn = true;
            }

        }
    });

    function zoomed() {
        canvas.attr("transform", d3.event.transform)
        newXscale = d3.event.transform.rescaleX(dataScale);
        newYscale = d3.event.transform.rescaleY(dataScale);
        gX.call(xAxis.scale(newXscale));
        gY.call(yAxis.scale(newYscale));
    }

    function clicked() {
        var coords = d3.mouse(this);
        points.append('circle')
            .attr('cx',coords[0])
            .attr('cy',coords[1])
            .attr('r',5);
        var x = newXscale.invert(coords[0]-padding);
        var y = newYscale.invert(coords[1]-padding);
        console.log(x+' '+y);
    }

</script>

</body>
</html>

2 个答案:

答案 0 :(得分:1)

创建一个变量来存储缩放级别:

mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in
                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());
                } else {
                    // User is signed out
                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
                // [START_EXCLUDE]
                updateUI(user);
                // [END_EXCLUDE]
            }
};

并且,在newZscale = d3.event.transform.k; 函数中,使用clicked绘制新的圆圈,将填充除以缩放级别:

dateScale

以下是演示:

&#13;
&#13;
function clicked() {
    var coords = d3.mouse(this);
    if (newXscale && newYscale) {
        var x = newXscale.invert(coords[0] - padding);
        var y = newYscale.invert(coords[1] - padding);
    };
    console.log(newZscale);
    points.append('circle')
        .attr('cx', (!x) ? coords[0] : dataScale(x) + (padding / newZscale))
        .attr('cy', (!y) ? coords[1] : dataScale(y) + (padding / newZscale))
        .attr('r', 5);
    console.log(x + ' ' + y);
}
&#13;
var data = [{
    x: 17,
    y: 3
}, {
    x: 20,
    y: 16
}, {
    x: 2,
    y: 13
}, {
    x: 19,
    y: 10
}, {
    x: 13,
    y: 15
}, {
    x: 2,
    y: 2
}, {
    x: 5,
    y: 8
}, {
    x: 11,
    y: 19
}, {
    x: 20,
    y: 12
}, {
    x: 10,
    y: 20
}];

var width = 600;
var height = 600;
var padding = 50;
var newXscale, newYscale, newZscale;

var dataScale = d3.scaleLinear()
    .domain([0, 21])
    .range([0, width]);

var svg = d3.select('body').append('svg')
    .attr('width', width + 2 * padding)
    .attr('height', height + 2 * padding)
    .on('click', clicked);

var xAxis = d3.axisTop()
    .scale(dataScale);

var gX = svg.append('g')
    .attr('transform', 'translate(50,50)')
    .call(xAxis);

var yAxis = d3.axisLeft()
    .scale(dataScale);

var gY = svg.append('g')
    .attr('transform', 'translate(50,50)')
    .call(yAxis);

var canvas = svg.append('g')

var points = canvas.append('g');
points.selectAll('circle').data(data)
    .enter().append('circle')
    .attr('cx', function(d) {
        return dataScale(d.x) + padding
    })
    .attr('cy', function(d) {
        return dataScale(d.y) + padding
    })
    .attr('r', 5);

var zoom
var zoomOn = false;
window.addEventListener('keydown', function(event) {
    if (event.key == 'z') {
        if (zoomOn) {
            d3.select('#zoomBox').remove();
            zoomOn = false;
        } else {
            zoom = d3.zoom()
                .scaleExtent([0.1, 10])
                .on('zoom', zoomed);



            svg.append("rect")
                .attr('cursor', 'move')
                .attr("width", width + padding * 2)
                .attr("height", height + padding * 2)
                .attr('id', 'zoomBox')
                .style("fill", "none")
                .style("pointer-events", "all")
                .call(zoom);
            zoomOn = true;
        }

    }
});

function zoomed() {
    canvas.attr("transform", d3.event.transform)
    newXscale = d3.event.transform.rescaleX(dataScale);
    newYscale = d3.event.transform.rescaleY(dataScale);
    newZscale = d3.event.transform.k;
    gX.call(xAxis.scale(newXscale));
    gY.call(yAxis.scale(newYscale));
}

function clicked() {
    var coords = d3.mouse(this);
    if (newXscale && newYscale) {
        var x = newXscale.invert(coords[0] - padding);
        var y = newYscale.invert(coords[1] - padding);
    };
    points.append('circle')
        .attr('cx', (!x) ? coords[0] : dataScale(x) + (padding / newZscale))
        .attr('cy', (!y) ? coords[1] : dataScale(y) + (padding / newZscale))
        .attr('r', 5);
}
&#13;
&#13;
&#13;

答案 1 :(得分:0)

我明白了。问题在于我在切换缩放时移除了缩放框。我切换事件监听器只是隐藏框并取消绑定指针事件。这是最终的代码:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>

    var data = [{x:17,y:3},
        {x:20,y:16},
        {x:2,y:13},
        {x:19,y:10},
        {x:13,y:15},
        {x:2,y:2},
        {x:5,y:8},
        {x:11,y:19},
        {x:20,y:12},
        {x:10,y:20}];

    var width = 600;
    var height = 600;
    var padding = 50;
    var newXscale, newYscale;
    var zoomOn = false;

    var xScale = d3.scaleLinear()
        .domain([0,21])
        .range([0, width]);

    var yScale = d3.scaleLinear()
        .domain([0,21])
        .range([0, width]);

    var svg = d3.select('body').append('svg')
        .attr('width', width+2*padding)
        .attr('height', height+2*padding)
        .on('click', clicked)
        .attr('cursor','crosshair');

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

    var gX = svg.append('g')
        .attr('transform','translate(50,50)')
        .call(xAxis);

    var yAxis = d3.axisLeft()
        .scale(yScale);

    var gY = svg.append('g')
        .attr('transform','translate(50,50)')
        .call(yAxis);

    var canvas = svg.append('g')

    var points = canvas.append('g');
    points.selectAll('circle').data(data)
        .enter().append('circle')
        .attr('cx', function(d) {return xScale(d.x)+padding})
        .attr('cy', function(d) {return yScale(d.y)+padding})
        .attr('r', 5);

    var zoom = d3.zoom()
        .scaleExtent([0.1, 10])
        .on('zoom', zoomed);

    var zoombox = svg.append("rect")
        .attr("width", width+padding*2)
        .attr("height", height+padding*2)
        .attr('id','zoomBox')
        .style("fill", "none")
        .style("pointer-events", "none")
        .style('visibility','off')
        .call(zoom);

    window.addEventListener('keydown', function (event) {
        if (event.key=='z') {
            if (zoomOn) {
                d3.select('#zoomBox')
                    .attr('cursor','auto')
                    .style('pointer-events','none')
                    .style('visibility','off');
                zoomOn = false;
            } else {
                d3.select('#zoomBox')
                    .attr('cursor','move')
                    .style('pointer-events','all')
                    .style('visibilty','on')
                zoomOn = true;
            }

        }
    });

    function zoomed() {
        canvas.attr("transform", d3.event.transform)
        newXscale = d3.event.transform.rescaleX(xScale);
        newYscale = d3.event.transform.rescaleY(yScale);
        gX.call(xAxis.scale(newXscale));
        gY.call(yAxis.scale(newYscale));
        newZscale = d3.event.transform.k;

    }

    function clicked() {
        var coords = d3.mouse(this);
        if (newXscale && newYscale) {
            var x = newXscale.invert(coords[0] - padding);
            var y = newYscale.invert(coords[1] - padding);
        };
        console.log(newZscale);
        points.append('circle')
            .attr('cx', (!x) ? coords[0] : xScale(x) + (padding / newZscale))
            .attr('cy', (!y) ? coords[1] : yScale(y) + (padding / newZscale))
            .attr('r', 5);
        console.log(x + ' ' + y);
    }

</script>

</body>
</html>