transform.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { attr } from './attr';
  2. import { isSVGGraphicsElement } from './elem';
  3. import { createSVGTransform, parseTransformString, transformStringToMatrix, matrixToTransformString, createSVGMatrix, } from './matrix';
  4. export function transform(elem, matrix, options = {}) {
  5. if (matrix == null) {
  6. return transformStringToMatrix(attr(elem, 'transform'));
  7. }
  8. if (options.absolute) {
  9. elem.setAttribute('transform', matrixToTransformString(matrix));
  10. return;
  11. }
  12. const transformList = elem.transform;
  13. const svgTransform = createSVGTransform(matrix);
  14. transformList.baseVal.appendItem(svgTransform);
  15. }
  16. export function translate(elem, tx, ty = 0, options = {}) {
  17. let transformAttr = attr(elem, 'transform');
  18. const transform = parseTransformString(transformAttr);
  19. if (tx == null) {
  20. return transform.translation;
  21. }
  22. transformAttr = transform.raw;
  23. transformAttr = transformAttr.replace(/translate\([^)]*\)/g, '').trim();
  24. const newTx = options.absolute ? tx : transform.translation.tx + tx;
  25. const newTy = options.absolute ? ty : transform.translation.ty + ty;
  26. const newTranslate = `translate(${newTx},${newTy})`;
  27. // Note that `translate()` is always the first transformation. This is
  28. // usually the desired case.
  29. elem.setAttribute('transform', `${newTranslate} ${transformAttr}`.trim());
  30. }
  31. export function rotate(elem, angle, cx, cy, options = {}) {
  32. let transformAttr = attr(elem, 'transform');
  33. const transform = parseTransformString(transformAttr);
  34. if (angle == null) {
  35. return transform.rotation;
  36. }
  37. transformAttr = transform.raw;
  38. transformAttr = transformAttr.replace(/rotate\([^)]*\)/g, '').trim();
  39. angle %= 360; // eslint-disable-line
  40. const newAngle = options.absolute ? angle : transform.rotation.angle + angle;
  41. const newOrigin = cx != null && cy != null ? `,${cx},${cy}` : '';
  42. const newRotate = `rotate(${newAngle}${newOrigin})`;
  43. elem.setAttribute('transform', `${transformAttr} ${newRotate}`.trim());
  44. }
  45. export function scale(elem, sx, sy) {
  46. let transformAttr = attr(elem, 'transform');
  47. const transform = parseTransformString(transformAttr);
  48. if (sx == null) {
  49. return transform.scale;
  50. }
  51. sy = sy == null ? sx : sy; // eslint-disable-line
  52. transformAttr = transform.raw;
  53. transformAttr = transformAttr.replace(/scale\([^)]*\)/g, '').trim();
  54. const newScale = `scale(${sx},${sy})`;
  55. elem.setAttribute('transform', `${transformAttr} ${newScale}`.trim());
  56. }
  57. /**
  58. * Returns an DOMMatrix that specifies the transformation necessary
  59. * to convert `elem` coordinate system into `target` coordinate system.
  60. */
  61. export function getTransformToElement(elem, target) {
  62. if (isSVGGraphicsElement(target) && isSVGGraphicsElement(elem)) {
  63. const targetCTM = target.getScreenCTM();
  64. const nodeCTM = elem.getScreenCTM();
  65. if (targetCTM && nodeCTM) {
  66. return targetCTM.inverse().multiply(nodeCTM);
  67. }
  68. }
  69. // Could not get actual transformation matrix
  70. return createSVGMatrix();
  71. }
  72. /**
  73. * Returns an DOMMatrix that specifies the transformation necessary
  74. * to convert `elem` coordinate system into `target` coordinate system.
  75. * Unlike getTransformToElement, elem is child of target,Because of the reduction in DOM API calls,
  76. * there is a significant performance improvement.
  77. */
  78. export function getTransformToParentElement(elem, target) {
  79. let matrix = createSVGMatrix();
  80. if (isSVGGraphicsElement(target) && isSVGGraphicsElement(elem)) {
  81. let node = elem;
  82. const matrixList = [];
  83. while (node && node !== target) {
  84. const transform = node.getAttribute('transform') || null;
  85. const nodeMatrix = transformStringToMatrix(transform);
  86. matrixList.push(nodeMatrix);
  87. node = node.parentNode;
  88. }
  89. matrixList.reverse().forEach((m) => {
  90. matrix = matrix.multiply(m);
  91. });
  92. }
  93. return matrix;
  94. }
  95. /**
  96. * Converts a global point with coordinates `x` and `y` into the
  97. * coordinate space of the element.
  98. */
  99. export function toLocalPoint(elem, x, y) {
  100. const svg = elem instanceof SVGSVGElement
  101. ? elem
  102. : elem.ownerSVGElement;
  103. const p = svg.createSVGPoint();
  104. p.x = x;
  105. p.y = y;
  106. try {
  107. const ctm = svg.getScreenCTM();
  108. const globalPoint = p.matrixTransform(ctm.inverse());
  109. const globalToLocalMatrix = getTransformToElement(elem, svg).inverse();
  110. return globalPoint.matrixTransform(globalToLocalMatrix);
  111. }
  112. catch (e) {
  113. return p;
  114. }
  115. }
  116. //# sourceMappingURL=transform.js.map