简体   繁体   中英

rotate and zoom svg with d3 javascript

I want to rotate and zoom graphic around its center with D3.js. When I zoom graphic I want to zoom it with current aspect ratio and vice versa when I rotate graphic I want to zoom it to the current point that my mouse points. For zooming I use wheel of the mouse and for rotation I use the button of the mouse.

var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    transform = d3.zoomIdentity;

var points = d3.range(2000).map(phyllotaxis(10));

var g = svg.append("g");

g.append("line")
    .attr("x1", "20")
    .attr("y1", "20")
    .attr("x2", "60")
    .attr("y2", "60")
    .attr("stroke", "black")
    .attr("stroke-width", "10");

  svg.call(d3.drag()
      .on("drag",onDrag)
  )
  // ##########################
  var boxCenter = [100, 100];
  // #############################
  function onDrag(){

    var x = d3.event.sourceEvent.pageX,
    y = d3.event.sourceEvent.pageY;

    var angle = Math.atan2(x - boxCenter[0],
        - (y - boxCenter[1]) )*(180/Math.PI);

    g.attr("transform", "rotate("+angle+")");
  }

svg.call(d3.zoom()
    .scaleExtent([1 / 2, 8])
    .on("zoom", zoomed));

function zoomed() {
      g.attr("transform", d3.event.transform);
  }


function phyllotaxis(radius) {
  var theta = Math.PI * (3 - Math.sqrt(5));
  return function(i) {
    var r = radius * Math.sqrt(i), a = theta * i;
    return {
      x: width / 2 + r * Math.cos(a),
      y: height / 2 + r * Math.sin(a)
    };
  };
}

Here is my example: https://jsfiddle.net/6Lyjz35L/

For the rotation around center to be correct at the initial zoom you need to add a 'transform-origin' attribute to 'g'.

g.attr("transform-origin", "50% 50%");

The other problems you're having stem from assigning the 'transform' attribute in two separate places. An element ('g') can only have one 'transform' attribute applied at a time, so you're overwriting one or the other each time you rotate or zoom. To fix this you can create a helper method which will append both of the transforms you want in a single string.

var currentAngle = 0;
var currentZoom = '';

function getTransform(p_angle, p_zoom) {
  return `${p_zoom} rotate(${p_angle})`;
  // return p_zoom + " rotate(" + p_angle + ")";
}

// In the rotate:
currentAngle = angle;
g.attr("transform", getTransform(currentAngle, currentZoom));

// In the zoom:
currentZoom = d3.event.transform;
g.attr("transform", getTransform(currentAngle, currentZoom));

There is one more issue which is introduced by the zoom, and that is that you'll have to calculate a new transform-origin at different zoom levels. The issue I said was introduced by the zoom was actually the result of applying the operations in the incorrect order. Originally I applied the rotation and THEN then translation. It actually needs to be reversed, translation and THEN rotation. This will keep the correct transform-origin.

Here's a fiddle with those changes: https://jsfiddle.net/scmxcszz/1/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM