Raphael SVG VML 实现多枢轴点旋转

发布于 2024-10-11 19:17:00 字数 1090 浏览 6 评论 0原文

在过去的两天里,我已经有效地弄清楚了如何不旋转拉斐尔元素。

基本上我试图在元素上实现多个枢轴点以通过鼠标旋转它。

当用户进入旋转模式时,将创建 5 个枢轴。一个位于边界框的每个角,一个位于框的中心。

当鼠标按下并移动时,使用 Raphael elements.rotate( Degrees, x, y) 并根据鼠标位置和 atan2 到枢轴点计算角度。

当我旋转元素、bbox 和其他枢轴后,问题就出现了。 x,y 位置相同,只是视口不同。

在启用 SVG 的浏览器中,我可以基于 matrixTransformation 和 getCTM。然而,在创建第一组新枢轴后,由于舍入误差,枢轴之后的每次旋转都会远离变换后的 bbox。

rotateerrors

上述内容在 IE 中甚至不是一个选项,因为它是基于 VML 的,无法解释转换。

是唯一有效的实施方式 元素旋转是通过使用旋转 绝对或绕中心旋转 边界框?

是否有可能创建多个 对象的枢轴点和更新 鼠标松开后它们仍保留在 变换后的角和中心 框?

更新: 我尝试使用 jQuery 偏移量来查找旋转后的枢轴,并使用该偏移位置作为枢轴点。

演示网站... http://weather.speedfetishperformance.com/dev/raphael/rotation.html

Over the last two days I've effectively figured out how NOT to rotate Raphael Elements.

Basically I am trying to implement a multiple pivot points on element to rotate it by mouse.

When a user enters rotation mode 5 pivots are created. One for each corner of the bounding box and one in the center of the box.

When the mouse is down and moving it is simple enough to rotate around the pivot using Raphael elements.rotate(degrees, x, y) and calculating the degrees based on the mouse positions and atan2 to the pivot point.

The problem arises after I've rotated the element, bbox, and the other pivots. There x,y position in the same only there viewport is different.

In an SVG enabled browser I can create new pivot points based on matrixTransformation and getCTM. However after creating the first set of new pivots, every rotation after the pivots get further away from the transformed bbox due to rounding errors.

rotate errors

The above is not even an option in IE since in is VML based and cannot account for transformation.

Is the only effective way to implement
element rotation is by using rotate
absolute or rotating around the center
of the bounding box?

Is it possible at all the create multi
pivot points for an object and update
them after mouseup to remain in the
corners and center of the transformed
bbox?

UPDATE:
I've attempted to use jQuery offset to find the pivot after it's been rotated, and to use that offset location as the pivot point.

Demo site ...
http://weather.speedfetishperformance.com/dev/raphael/rotation.html

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

无所的.畏惧 2024-10-18 19:17:00

我能想到的最好的跨浏览器方法是自己实现旋转,而不是让 SVG 来做。旋转 x,y 坐标相当简单,每当我需要进行 2D 旋转时,我一直在使用这个(tcl)代码: 画布旋转

这样做的好处是您可以最大程度地控制旋转,因为您是手动进行的。这解决了您在旋转后尝试猜测最终坐标的问题。此外,这应该是跨浏览器兼容的。

缺点是你必须使用路径。因此,没有矩形(尽管将它们转换为路径应该很容易)或省略号(转换为路径有点困难,但可行)。另外,由于您是手动执行此操作,因此它应该比让 SVG 为您执行此操作慢。

下面是该 Tcl 代码在 javascript 中的部分实现:

首先我们需要一个正则表达式来标记 SVG 路径:

var svg_path_regexp = (function(){
  var number = '-?[0-9.]+';
  var comma = '\s*[, \t]\s*';
  var space = '\s+';
  var xy = number + comma + number;
  var standard_paths = '[mlcsqt]';
  var horiz_vert = '[hv]\s*' + number;
  var arc = 'a\s*' + xy + space + number + space + xy + space + xy;
  var OR = '\s*|';

  return new RegExp(
    standard_paths +OR+
    xy +OR+
    horiz_vert +OR+
    arc,

    'ig'
  );
})();

现在我们可以实现旋转函数:

function rotate_SVG_path (path, Ox, Oy, angle) {
  angle = angle * Math.atan(1) * 4 / 180.0; // degrees to radians

  var tokens = path.match(svg_path_regexp);

  for (var i=0; i<tokens.length; i++) {
    var token = tokens[i].replace(/^\s+|\s+$/g,''); // trim string

    if (token.match(/\d/)) { // assume it's a coordinate
      var xy = token.split(/[, \t]+/);
      var x = parseFloat(xy[0]);
      var y = parseFloat(xy[1]);
      x = x - Ox;  // Shift to origin
      y = y - Oy;
      var xx = x * Math.cos(angle) - y * Math.sin(angle); // Rotate
      var yy = x * Math.sin(angle) + y * Math.cos(angle);
      x = xx + Ox; // Shift back
      y = yy + Oy;

      token = x + ',' + y;
    }
    else if (token.match(/^[hv]/)) {
      // handle horizontal/vertical line here
    }
    else if (token.match(/^a/)) {
      // handle arcs here
    }

    tokens[i] = token;
  }
  return tokens.join('');
}

上面的旋转函数实现了除水平/垂直线(您需要跟踪以前的 xy 值)和弧。两者都不应该太难实施。

The best cross-browser way I can think of to do what you want is to implement the rotation yourself rather than let SVG do it. Rotating x,y coordinates is fairly simple and I've been using this (tcl) code whenever I need to do 2D rotation: Canvas Rotation.

The upside to this is you have maximum control of the rotation since you're doing it manually. This solves the problems you're having trying to guess the final coordinates after rotation. Also, this should be cross browser compatible.

The downside is you have to use paths. So no rects (though it should be easy to convert them to paths) or ellipses (a little bit harder to convert to path but doable). Also, since you're doing it manually, it should be slower than letting SVG do it for you.

Here's a partial implementation of that Tcl code in javascript:

first we need a regexp to tokenize SVG paths:

var svg_path_regexp = (function(){
  var number = '-?[0-9.]+';
  var comma = '\s*[, \t]\s*';
  var space = '\s+';
  var xy = number + comma + number;
  var standard_paths = '[mlcsqt]';
  var horiz_vert = '[hv]\s*' + number;
  var arc = 'a\s*' + xy + space + number + space + xy + space + xy;
  var OR = '\s*|';

  return new RegExp(
    standard_paths +OR+
    xy +OR+
    horiz_vert +OR+
    arc,

    'ig'
  );
})();

now we can implement the rotate function:

function rotate_SVG_path (path, Ox, Oy, angle) {
  angle = angle * Math.atan(1) * 4 / 180.0; // degrees to radians

  var tokens = path.match(svg_path_regexp);

  for (var i=0; i<tokens.length; i++) {
    var token = tokens[i].replace(/^\s+|\s+$/g,''); // trim string

    if (token.match(/\d/)) { // assume it's a coordinate
      var xy = token.split(/[, \t]+/);
      var x = parseFloat(xy[0]);
      var y = parseFloat(xy[1]);
      x = x - Ox;  // Shift to origin
      y = y - Oy;
      var xx = x * Math.cos(angle) - y * Math.sin(angle); // Rotate
      var yy = x * Math.sin(angle) + y * Math.cos(angle);
      x = xx + Ox; // Shift back
      y = yy + Oy;

      token = x + ',' + y;
    }
    else if (token.match(/^[hv]/)) {
      // handle horizontal/vertical line here
    }
    else if (token.match(/^a/)) {
      // handle arcs here
    }

    tokens[i] = token;
  }
  return tokens.join('');
}

The above rotate function implements everything except horizontal/vertical lines (you need to keep track of previous xy value) and arcs. Neither should be too hard to implement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文