Cách đặt điểm gốc biến đổi trong SVG


104

Tôi cần thay đổi kích thước và xoay các phần tử nhất định trong tài liệu SVG bằng javascript. Vấn đề là, theo mặc định, nó luôn áp dụng phép biến đổi xung quanh điểm gốc ở (0, 0)- trên cùng bên trái.

Làm cách nào để xác định lại điểm neo chuyển đổi này?

Tôi đã thử sử dụng transform-originthuộc tính, nhưng nó không ảnh hưởng gì cả.

Đây là cách tôi đã làm điều đó:

svg.getDocumentById('someId').setAttribute('transform-origin', '75 240');

Có vẻ như không đặt điểm mấu chốt thành điểm tôi đã chỉ định mặc dù tôi có thể thấy trong Firefox rằng thuộc tính được đặt chính xác. Tôi đã thử những thứ như center bottom50% 100%có và không có dấu ngoặc đơn. Không có gì hoạt động cho đến nay.

Có ai giúp được không?


FWIW, được cho là đã được sửa kể từ Firefox 19 beta 3, mặc dù tôi vẫn gặp sự cố trong Firefox 22. Danh sách bugzilla của Mozilla: bugzilla.mozilla.org/show_bug.cgi?id=828286
Toph

Câu trả lời:


147

Để xoay sử dụng transform="rotate(deg, cx, cy)", trong đó deg là độ bạn muốn xoay và (cx, cy) xác định tâm quay.

Để chia tỷ lệ / thay đổi kích thước, bạn phải dịch theo (-cx, -cy), sau đó chia tỷ lệ và sau đó dịch ngược lại thành (cx, cy). Bạn có thể làm điều này với một phép biến đổi ma trận :

transform="matrix(sx, 0, 0, sy, cx-sx*cx, cy-sy*cy)"

Trong đó sx là hệ số tỷ lệ trong trục x, sy trong trục y.


Cảm ơn bạn rất nhiều. Tôi nghĩ rằng xoay hoạt động hoàn hảo, nhưng việc thay đổi tỷ lệ / thay đổi kích thước luôn kết thúc bằng một số lượng ngẫu nhiên. Tôi đã thử sử dụng ma trận và thực hiện chúng một cách riêng biệt như "dịch (cx sx, cy sy) scale (sx, sy)". Kết quả tương tự.
CTheDark

6
Nó sẽ không phải là biến đổi = "matrix (sx, 0, 0, sy, cx-sx cx, cy-sy cy)"? Bởi vì bạn muốn chỉ dịch theo sự khác biệt. Tôi nghĩ logic này là đúng, nhưng nó vẫn mang lại cho tôi ra khỏi vị trí ...
CTheDark

Bây giờ nó hoạt động! Tôi chỉ đang sử dụng sai giá trị cx và cy! Cảm ơn rất nhiều!
CTheDark

1
@JayStevens Có, phép biến đổi ma trận sẽ hoạt động cho bất kỳ hệ thống nào, nó chỉ là phép toán. Sự khác biệt duy nhất có thể là ký hiệu. Theo như tôi có thể nói, phép biến đổi ma trận CSS sử dụng ký hiệu giống như đối với SVG, vì vậy sáu số giống nhau theo thứ tự đó sẽ hoạt động.
Peter Collingridge

1
Chỉ cần làm rõ, cx và cy cần phải lệch khỏi tâm của hộp mà bạn mở rộng quy mô của bạn. Điều này khiến tôi vấp phải một chút, khi tôi đang nghĩ trong các mô hình hộp CSS. @forresto ám chỉ điều này trong câu trả lời của anh ấy bên dưới.
Jason Farnsworth

22

Nếu bạn có thể sử dụng một giá trị cố định (không phải "center" hoặc "50%"), bạn có thể sử dụng CSS để thay thế:

-moz-transform-origin: 25px 25px;
-ms-transform-origin:  25px 25px;
-o-transform-origin: 25px 25px;
-webkit-transform-origin:  25px 25px;
transform-origin: 25px 25px;

Một số trình duyệt (như Firefox) sẽ không xử lý các giá trị tương đối một cách chính xác.


1
Chà. Sẽ +10 nếu tôi có thể. Điều này đã cứu ngày của tôi! Cảm ơn.
Cullub

Làm thế nào để làm điều này và chỉ điều này trong JavaScript?
Andrew S

Như element.setAttribute('transform-origin', '25px 25px')thế nào?
nikk wong

13

Nếu bạn giống tôi và muốn xoay và sau đó thu phóng với điểm gốc biến đổi, bạn sẽ cần nhiều hơn một chút.

// <g id="view"></g>
var view = document.getElementById("view");

var state = {
  x: 0,
  y: 0,
  scale: 1
};

// Origin of transform, set to mouse position or pinch center
var oX = window.innerWidth/2;
var oY = window.innerHeight/2;

var changeScale = function (scale) {
  // Limit the scale here if you want
  // Zoom and pan transform-origin equivalent
  var scaleD = scale / state.scale;
  var currentX = state.x;
  var currentY = state.y;
  // The magic
  var x = scaleD * (currentX - oX) + oX;
  var y = scaleD * (currentY - oY) + oY;

  state.scale = scale;
  state.x = x;
  state.y = y;

  var transform = "matrix("+scale+",0,0,"+scale+","+x+","+y+")";
  //var transform = "translate("+x+","+y+") scale("+scale+")"; //same
  view.setAttributeNS(null, "transform", transform);
};

Ở đây nó đang hoạt động: http://forresto.github.io/dataflow-prototyping/react/


Liên kết github của bạn 404s
NuclearPeon

Xin chào @NuclearPeon, điều này thật tuyệt vời, nhưng tôi dường như không thể triển khai nó trong mã của mình. Phần tử SVG mà tôi muốn nó áp dụng đã là một biến được gọi là "mainGrid". Tôi đã thử thay thế phiên bản của "view" bằng "mainGrid" mà không có hiệu lực. Tôi đang thiếu gì? Cảm ơn!
sakeferret

@sakeferret điều rõ ràng nhất cần kiểm tra là sự trùng idkhớp trong thuộc tính của getElementByIdvà của tài liệu id="...". Thật khó để biết chắc chắn nếu không nhìn thấy mã. Nếu bạn không muốn gửi nó như là một câu hỏi SO, bạn có thể thử tạo ví dụ đơn giản sử dụng tên thuộc tính của bạn cho đến khi nó hoạt động / bạn tìm thấy vấn đề này, sau đó thêm phía sau phần còn lại trong.
NuclearPeon

12

Để mở rộng quy mô mà không cần phải sử dụng matrixphép biến đổi:

transform="translate(cx, cy) scale(sx sy) translate(-cx, -cy)"

Và đây là trong CSS:

transform: translate(cxpx, cypx) scale(sx, sy) translate(-cxpx, -cypx)

0

Có vẻ như tất cả chúng ta đã bỏ lỡ một mẹo ở đây: transform-box: fill-box

việc sử dụng transform-box: fill-boxsẽ làm cho một phần tử trong SVG hoạt động như một phần tử HTML bình thường. Sau đó, bạn có thể áp dụng transform-origin: center(hoặc một cái gì đó khác) như bình thường

đúng vậy, transform-box: fill-boxkhông cần bất kỳ thứ ma trận phức tạp nào


-1

Tôi đã có một vấn đề tương tự. Nhưng tôi đã sử dụng D3 vị trí các yếu tố của tôi, và muốn chuyển đổichuyển tiếp để được xử lý bởi CSS. Đây là mã gốc của tôi, mà tôi đã làm việc trong Chrome 65:

//...
this.layerGroups.selectAll('.dot')
  .data(data)
  .enter()
  .append('circle')
  .attr('transform-origin', (d,i)=> `${valueScale(d.value) * Math.sin( sliceSize * i)} 
                                     ${valueScale(d.value) * Math.cos( sliceSize * i + Math.PI)}`)
//... etc (set the cx, cy and r below) ...

Điều này cho phép tôi để thiết lập cx, cytransform-origincác giá trị trong javascript sử dụng cùng dữ liệu.

NHƯNG điều này không hoạt động trong Firefox! Những gì tôi phải làm là bọc thẻ circletrong gthẻ và translatenó sử dụng cùng một công thức định vị từ phía trên. Sau đó, tôi đã thêm thẻ circlevào gthẻ và đặt cxcycác giá trị của nó thành 0. Từ đó, transform: scale(2)sẽ mở rộng quy mô từ trung tâm như mong đợi. Mã cuối cùng trông như thế này.

this.layerGroups.selectAll('.dot')
  .data(data)
  .enter()
  .append('g')
  .attrs({
    class: d => `dot ${d.metric}`,
    transform: (d,i) => `translate(${valueScale(d.value) * Math.sin( sliceSize * i)} ${valueScale(d.value) * Math.cos( sliceSize * i + Math.PI)})`
  })
  .append('circle')
  .attrs({
    r: this.opts.dotRadius,
    cx: 0,
    cy: 0,
  })

Sau khi thực hiện thay đổi này, tôi đã thay đổi CSS của mình để nhắm mục tiêu circlethay vì .dot, để thêm transform: scale(2). Tôi thậm chí không cần sử dụng transform-origin.

LƯU Ý:

  1. Tôi đang sử dụng d3-selection-multitrong ví dụ thứ hai. Điều này cho phép tôi chuyển một đối tượng đến .attrsthay vì lặp lại .attrcho mọi thuộc tính.

  2. Khi sử dụng theo nghĩa đen của mẫu chuỗi, hãy lưu ý các dấu ngắt dòng như được minh họa trong ví dụ đầu tiên. Điều này sẽ bao gồm một dòng mới trong đầu ra và có thể phá vỡ mã của bạn.


2
Có lẽ nếu bạn đặt chuyển đổi-box: fill-box; Firefox sẽ hoạt động theo cách bạn muốn.
Robert Longson

đã thử điều đó. dường như không hoạt động. Nó đã hoạt động sau khi tôi thay đổi svg:transform-origin.enabledpref trong about:configtrang của firefox . Nhưng ... đó không phải là một giải pháp thích hợp. Tôi cũng tìm thấy sự khác biệt giữa transform-origin: 50% 50%transform-origin: center center.
Jamie S

pref svg.transform-origin.enabled đã bị xóa nhiều phiên bản trước. Trừ khi bạn đang sử dụng phiên bản rất cũ, không có sự khác biệt giữa 50% và trung tâm. Vui lòng nêu ra lỗi bugzilla nếu có sự khác biệt trong Firefox 59 trở lên.
Robert Longson 21/03/18
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.