D3画笔和缩放:如果缩放多次,则2+变换基于原始参数,不更新

pepwfjgg  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(100)

我尝试使用D3创建一个具有可悬停点的交互式可缩放Map。用户可以在Map上拖动一个矩形来放大该区域。
到目前为止,我已经实现了它,所以第一次刷和缩放工程,一旦缩放,如果我点击“重置缩放”按钮重置它,然后刷和缩放再次,这也工程。
不起作用的部分是如果我刷和缩放,然后一旦它已经缩放,我刷和缩放再次。似乎在这种情况下,刷检测到svg在其预缩放的范围,而更新的范围,我不知道如何处理它,所以它实际上放大到刷的区域。
下面是codepen的例子:https://codepen.io/jhjanicki-the-scripter/pen/ExeWgML
下面是相关代码(data = json of the points,land = geojson of the land area)

let width = 1200;
let height = 800;

const svg = d3
  .select("#chart1")
  .append("svg")
  .attr("width", width)
  .attr("height", height)

const projection = d3.geoMercator()
  .scale(width / 2.5 / Math.PI)
  .rotate([0, 0])
  .center([0, 0])
  .translate([width / 2, height / 2]);

const path = d3.geoPath().projection(projection);

const colorScale = 
  d3.scaleOrdinal().domain(["class1","class2","class3","class4","class5"])
  .range(["#a6cee3", "#1f78b4", "#b2df8a", "#33a02c", "#fb9a99"]);

let brush = d3.brush()
  .on('brush', brushMove)
  .on("end", brushEnd);

let brushExtent;
let brushedSufficient = true; //check if dragged area big enough

const zoom = d3.zoom()
  .scaleExtent([1, Infinity])
  .translateExtent([[0, 0], [width, height]])
  .extent([[0, 0], [width, height]])
  .on("zoom", zoomed);

function initBrush() {
  svg.call(brush);
}

//brushMove related functions
function brushMove(e) {
  brushExtent = e.selection;
  //update points style within the rect
  svg.selectAll("circle.point")
    .attr("fill", d => isInBrushExtent(d) ? 'white' : colorScale(d.cat))
    .raise(); // in order to be able to hover

  brushedSufficient = isSufficient();
}

function isInBrushExtent(d) {
  return brushExtent &&
    projection([d.lon, d.lat])[0] >= brushExtent[0][0] &&
    projection([d.lon, d.lat])[0] <= brushExtent[1][0] &&
    projection([d.lon, d.lat])[1] >= brushExtent[0][1] &&
    projection([d.lon, d.lat])[1] <= brushExtent[1][1];
}

//
function isSufficient() { // make sure user can't zoom by clicking
  return brushExtent[1][0] - brushExtent[0][0] > 10 && brushExtent[1][1] - brushExtent[0][1] > 10;
}

//brushEnd function
function brushEnd() {

  //get the four corner coordinates
  const x0 = brushExtent[0][0];
  const x1 = brushExtent[1][0];
  const y0 = brushExtent[0][1];
  const y1 = brushExtent[1][1];

  if (brushedSufficient) {

    const x = -(x0 + x1) / 2;
    const y = -(y0 + y1) / 2;
    const k = Math.min(8, 0.9 / Math.max((x1 - x0) / width, (y1 - y0) / height));

    svg.transition().duration(750).call(
      zoom.transform,
      d3.zoomIdentity
        .translate(width / 2, height / 2)
        .scale(k)
        .translate(x, y),
    )
  }

}

function zoomed(e) {

  const transform = e.transform; //k,x,y
  svg.selectAll('path')
    .attr('transform', transform)
    .attr("stroke-width", 1 / transform.k);
  svg.selectAll("circle")
    .attr('transform', transform)
    .attr('r', 5 / transform.k)
    .attr("fill", d => colorScale(d.cat));

}

function resetZoom() {
  svg.transition()
    .duration(750)
    .call(zoom.transform, d3.zoomIdentity)
}

d3.select("#resetZoom").on("click", resetZoom);

svg.selectAll("path.land")
  .data(land.features)
  .join("path")
  .attr("class", "land")
  .attr("id", "land")
  .attr("fill", "white")
  .attr("stroke", "#808080")
  .attr("d", path);

//need to come before points

svg.append("g")
  .attr("class", "brush")
  .call(brush);

// draw points
svg.selectAll("circle.point")
  .data(data)
  .join("circle")
  .attr("class", "point")
  .attr("id", d => "id" + d.id)
  .attr("cx", d => projection([d.lon, d.lat])[0])
  .attr("cy", d => projection([d.lon, d.lat])[1])
  .attr("stroke-width", 1)
  .attr("r", 5)
  .attr("fill", d => colorScale(d.cat))
  .attr("cursor", "pointer")
  .on("mouseover", (e, d) => console.log(d.name))

字符串

quhf5bfb

quhf5bfb1#

我在画笔上也遇到了同样的问题,所以我建立了自己的画笔。你可以在x轴上缩放图表,在图表区域上画笔。

var data = [2,3,5,7];

    var width = 600;
    var height = 400;

    var margin = {
        left: 30,
        top: 30,
        bottom: 30,
        right: 15
    };

setTimeout(function() {v12()},200);



function v12() {

    var graph = {};

    graph.rectWidth = width - margin.left - margin.right;
    graph.rectHeight = height - margin.top - margin.bottom;

    graph.xScale = d3.scaleLinear()
        .domain([0,data.length-1])
        .range([0,graph.rectWidth]);

    graph.xAxis = d3.axisBottom(graph.xScale)
        //.scale(xScale)
        //.orient("bottom")
        .ticks( 5)
        .tickSize(-graph.rectHeight);

    graph.yScale = d3.scaleLinear()
        .domain([d3.min(data, function(d) { return d;}), d3.max(data,function(d) { return d})])
        .range([graph.rectHeight,0]);

    graph.yAxis = d3.axisLeft(graph.yScale)
        //.scale(yScale)
        //.orient("left")
        .ticks( 5)
        .tickSize(-graph.rectWidth);

    graph.lineFunction = d3.line()
        .x( function(d, i) { return graph.xScaleTemp(i);})
        .y( function(d) { return graph.yScaleTemp(d);});

    graph.zoomXY = d3.zoom()
    //.x(xScale)
    //.y(yScale)
        .scaleExtent([-10, 1000])
        .on("zoom", zoomed);

    graph.zoomX = d3.zoom()
    //.x(xScale)
    //.y(yScale)
        .scaleExtent([-10, 1000])
        .on("zoom", function() { zoomed("x")});

    graph.zoomY = d3.zoom()
    //.x(xScale)
    //.y(yScale)
        .scaleExtent([-10, 1000])
        .on("zoom", function() { zoomed("y")});

    graph.svg = d3.select("div")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .style("border", "1px solid black");

    graph.svg.append("text")
        .text("Graph: Axis Zooming and Focus")
        .attr("x", 10)
        .attr("y", 20);

    graph.svg.append("clipPath")
        .attr("id", "clip")
        .append("rect")
        .attr("x", 0)
        .attr("y", 0)
        .attr("width", graph.rectWidth)
        .attr("height", graph.rectHeight);

    graph.g = graph.svg
        .append("g")
        .attr("class", "main")
        .attr("transform", function() { return "translate(" + margin.left + "," + margin.top + ")";});

    graph.dataClip = graph.g
        .append("g")
        .attr("clip-path", "url(#clip)");   //append clip path to limit visibility

    //group element for data
    graph.dataGroup = graph.dataClip
        .append("g")
        .attr("class", "data-group")
        .attr("clip-path", "url(#clip)");

    graph.focus = graph.dataClip.append("g")                                // **********
        .style("display", "none");

    // append the circle at the intersection               // **********
    graph.focus.append("circle")                                 // **********
        .attr("class", "y")                                // **********
        .style("fill", "none")                             // **********
        .style("stroke", "steelblue")
        .attr("stroke-width", "3")// **********
        .attr("r", 10);

    graph.focus.append("line")
        .attr("stroke", "steelblue")
        .attr("stroke-width", "3")
        .attr("x0",0)
        .attr("x1", 0)
        .attr("y0",0)
        .attr("y1", graph.rectHeight);

    graph.xAxisElem = graph.g
        .append("g")
        .attr("class","axis x")
        .style("transform", function() {
            return "translate(0," + graph.rectHeight + "px)"; })
        .call(graph.xAxis);

    graph.yAxisElem = graph.g
        .append("g")
        .attr("class","axis y")
        .style("transform", "translate(0,0)")
        .call(graph.yAxis);

    graph.dataGroup.selectAll(".data-point")
        .data(data)
        .enter()
        .append("circle")
        .attr("class", "data-point")
        .attr("fill", "none")
        .attr("cx", 20)
        .attr("r", 5)
        .attr("stroke", "steelblue")
        .attr("stroke-width", 2);

    graph.dataGroup.append("path")
        .attr("class", "data-line")
        .style("stroke", "steelblue")
        .attr("stroke-width", "1");

   /* // append the rectangle to capture mouse               // **********
    graph.svg.append("rect")                                     // **********
        .attr("width", width)                              // **********
        .attr("height", height)                            // **********
        .style("fill", "none")                             // **********
        .style("pointer-events", "all")                    // **********
        .on("mouseover", function() { graph.focus.style("display", null); })
        .on("mouseout", function() { graph.focus.style("display", "none"); })
        .on("mousemove", mousemove);
*/

    graph.overlayX = graph.g//.select(".axis.x")
        .append("rect")
        .attr("fill", "rgba(255,0,0,0.5)")
        .attr("width", graph.rectWidth)
        .attr("height", 15)
        .attr("y", graph.rectHeight)
        .call(graph.zoomXY);

    graph.overlayY = graph.g//.select(".axis.y")
        .append("rect")
        .attr("fill", "rgba(255,0,0,0.5)")
        .attr("width", 150)
        .attr("height", graph.rectHeight)
        .attr("x", -150)
        //.call(graph.zoomXY)
        .on("mousedown.move", mousedowned);

    //append the rectangle to capture zoom
    graph.overlayRect = graph.g.append("rect")
        .attr("class", "overlay-rect")
        .attr("width", graph.rectWidth)
        .attr("height", graph.rectHeight)
        .style("fill", "none")                             // **********
        .style("pointer-events", "all")                    // **********
        .on("mouseover", function() { graph.focus.style("display", null); })
        .on("mouseout", function() { graph.focus.style("display", "none"); })
        .on("mousemove", mousemove)
        //.call(graph.zoomXY)
        .on("dblclick.zoom", function() {
            resetZoom();
        } )
        .on("mousedown.rectZoom", rectZoom);


    //console.log(graph.overlayRect.on("mousedown.zoom"));

    getZoomedScales();
    updateGraph();

    function updateGraph() {
        updateData();
        updateAxes();
        updateTooltip();
    }

    function getZoomedScales() {

        var transformX = d3.zoomTransform(graph.overlayX.node());
        var transformY = d3.zoomTransform(graph.overlayY.node());
        var transformXY = d3.zoomTransform(graph.overlayRect.node());

        graph.yScaleTemp = transformXY.rescaleY(transformY.rescaleY(graph.yScale));
        graph.xScaleTemp = transformXY.rescaleX(transformX.rescaleX(graph.xScale));

    }

    function updateData() {

        graph.dataGroup.select(".data-line")
            .attr("d", graph.lineFunction(data));

        graph.dataGroup.selectAll(".data-point")
            .data(data)
            .attr("cy", function( d, i) {
                return graph.yScaleTemp(d);
            })
            .attr("cx", function( d, i) {
                return graph.xScaleTemp(i);
            });
    }

    function updateAxes() {
        graph.xAxisElem.call(graph.xAxis.scale(graph.xScaleTemp));
        graph.yAxisElem.call(graph.yAxis.scale(graph.yScaleTemp));
    }

    function resetZoom() {
        graph.overlayRect
            .transition()
            .duration(750)
            .call(graph.zoomXY.transform, d3.zoomIdentity);

        graph.overlayX
            .transition()
            .duration(750)
            .call(graph.zoomX.transform, d3.zoomIdentity);

        graph.overlayY
            .transition()
            .duration(750)
            .call(graph.zoomY.transform, d3.zoomIdentity);

        setTimeout(function() {
            getZoomedScales();
            getTooltipPos();
            updateTooltip();
        },700);
    }

    function zoomed() {

        getZoomedScales();
        updateGraph();

    }

    function updateTooltip() {
        if(graph.pos !== undefined) {

            var i = graph.pos;

            if (i < 0) i = 0;
            else if (i >= data.length) i = data.length - 1;

            graph.focus.
                attr("transform", "translate(" + graph.xScaleTemp(i) + ",0)");

            graph.focus.select("circle.y")
                .attr("transform", "translate(0," + graph.yScaleTemp(data[i]) + ")");

        }

    }

    function getTooltipPos() {
        var x0 = graph.xScaleTemp.invert(graph.mousePos);
        graph.pos = Math.round(x0);
    }

    function mousemove() {
        graph.mousePos = d3.mouse(this)[0];

        getTooltipPos();
        updateTooltip();

    }

    function mousedowned() {
        var transformY = d3.zoomTransform(graph.overlayY.node());
        graph.yScaleTemp = (transformY.rescaleY(graph.yScale));
    }
    
    function rectZoom(d, i) {
                var element = this;
                var mouseOrigin = d3.mouse(element);
                var rect = graph.g.append("rect")
                    .attr("fill", "rgba(255, 255, 0, 0.8)")
                    .attr("stroke", "red");

                mouseOrigin[0] = Math.max(0, Math.min(graph.rectWidth, mouseOrigin[0]));
                mouseOrigin[1] = Math.max(0, Math.min(graph.rectHeight, mouseOrigin[1]));

                d3.select(window)
                    .on("mousemove.zoomRect", function() {
                        let m = d3.mouse(element);

                        m[0] = Math.max(0, Math.min(graph.rectWidth, m[0]));
                        m[1] = Math.max(0, Math.min(graph.rectHeight, m[1]));

                        rect.attr("x", Math.min(mouseOrigin[0], m[0]))
                            .attr("y", Math.min(mouseOrigin[1], m[1]))
                            .attr("width", Math.abs(m[0] - mouseOrigin[0]))
                            .attr("height", Math.abs(m[1] - mouseOrigin[1]));
                    })
                    .on("mouseup.zoomRect", function() {
                        d3.select(window).on("mousemove.zoomRect", null).on("mouseup.zoomRect", null);

                        var m = d3.mouse(element);

                        m[0] = Math.max(0, Math.min(graph.rectWidth, m[0]));
                        m[1] = Math.max(0, Math.min(graph.rectHeight, m[1]));

                        if (m[0] !== mouseOrigin[0] && m[1] !== mouseOrigin[1]) {
                            

                                var zoomRectWidth = Math.abs(m[0] - mouseOrigin[0]);
                            var scaleFactor = graph.rectWidth / zoomRectWidth;
                                
                            var e = graph.overlayX;
                                                        var t = d3.zoomTransform(e.node());
                            var k = t.k;
                            var x = t.x;
                            var translateX = Math.min(m[0], mouseOrigin[0]);
                            
                            e.transition()
                                .duration(750)
                                .call(graph.zoomXY.transform, d3.zoomIdentity
                                    .scale(scaleFactor)
                                    .translate(-translateX, 0)
                                    
                                    .translate(x, 0)
                                    .scale(k)
                                );
                                
                            var zoomRectHeight = Math.abs(m[1] - mouseOrigin[1]);
                            var scaleFactor = graph.rectHeight / zoomRectHeight;
                                
                            var e = graph.overlayY;
                            var t = d3.zoomTransform(e.node());
                            var k = t.k;
                            var y = t.y;
                            var translateY = Math.min(m[1], mouseOrigin[1]);
                            
                            e.transition()
                                .duration(750)
                                .call(graph.zoomXY.transform, d3.zoomIdentity
                                    .scale(scaleFactor)
                                    .translate(0, -translateY)
                                    
                                    .translate(0, y)
                                    .scale(k)
                                );

                        }
                        rect.remove();
                    }, true);

                d3.event.stopPropagation();
            };
}
.axis path {
        fill: none;
        stroke: steelblue;
        stroke-width: 0.3;
    }

    .axis line {
        fill: none;
        stroke: steelblue;
        stroke-width:1px;
        stroke-opacity:0.3;
    }

    .axis text {
        fill: steelblue;
    }

    .data-group path {
        stroke: red;
        fill: none;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.2/d3.min.js"></script>
<body>
<div>

</div>

</body>
</html>
x4shl7ld

x4shl7ld2#

实际上,我通过选择画笔并在缩放功能中对画笔应用变换来解决它,下面是工作示例:https://codepen.io/jhjanicki-the-scripter/pen/eYLWMGo
代码:

function zoomed(e) {

      const transform = e.transform; //k,x,y
    
      svg.selectAll('path')
        .attr('transform', transform)
        .attr("stroke-width", 1 / transform.k);
      svg.selectAll("circle")
        .attr('transform', transform)
        .attr('r', 5 / transform.k)
        .attr("fill", d => colorScale(d.cat));

      // HERE
      svg.select('.brush')
        .attr('transform', transform)
        .attr("stroke-width", 1 / transform.k);
    }

字符串

相关问题