首页 > 解决方案 > 将一个 svg 元素居中于另一个元素时的比例关系

问题描述

我编写了这些函数以将一个 svg 元素居中于另一个元素中:

import { Matrix, translate } from 'transformation-matrix';

export const getElementCenter = (element: SVGGraphicsElement) => {
  const bBox = element.getBBox();

  return { x: bBox.x + bBox.width / 2, y: bBox.y + bBox.height / 2 };
};

export const centerElementInOther = (
  element: SVGGraphicsElement,
  other: SVGGraphicsElement,
  scaleFactor: number = 1
): Matrix => {
  const elementCentre = getElementCenter(element);
  const otherCentre = getElementCenter(other);

  const x = elementCentre.x - otherCentre.x;
  const y = elementCentre.y - otherCentre.y;

  // how can I work out the scaleFactor?  If it the actual scale is 0.5 then I need to divide by 2 but if it is 1 then I need to divide by 1

  return translate(-x / scaleFactor, -y / scaleFactor);
};

除非元素被缩放,否则一切正常,然后我需要应用一些数学,但我不明白这个比例。

一切正常,直到我将元素的比例更改为 0.5,然后我不得不将中心 x 和中心 y 除以 2。

标签: svg

解决方案


我很想看到 SVG 代码。

将 svg 元素置于另一个已转换元素中的一种方法是将两个元素包装在 a 中<g>并将转换应用于组,如下所示:

const bBox1 = theRect1.getBBox();

c1.setAttributeNS(null,"cx",bBox1.x + bBox1.width / 2)
c1.setAttributeNS(null,"cy",bBox1.y + bBox1.height / 2);
svg{border:1px solid;}
<svg viewBox="0 0 200 100">
  <g transform="scale(.5,.5) translate(150,50) rotate(30,30,10)">
  <rect id="theRect1" x="30" y="10" width="80" height="40" />
  <circle id="c1" r="2" fill="red" />
  </g>
</svg>

如果无法将元素包装在一个组中,则可以对要居中的元素应用相同的转换

const bBox2 = theRect2.getBBox();
// get the transformation applied to the rect
let theTransformation = theRect2.getAttribute("transform")

c2.setAttributeNS(null,"cx",bBox2.x + bBox2.width / 2)
c2.setAttributeNS(null,"cy",bBox2.y + bBox2.height / 2);
//apply the same transformation to the circle
c2.setAttributeNS(null,"transform",theTransformation) 
svg{border:1px solid;}
<svg viewBox="0 0 200 100">
  <rect id="theRect2" x="30" y="10" width="80" height="40" transform="scale(.5,.5) translate(150,50) rotate(30,30,10)" />
  <circle id="c2" r="2" fill="red" />
</svg>

如果您只想缩小 rect 而不是圆圈,您可以将圆圈包裹在 group<g>中,使用 group 并将圆圈放大适当的数量:

const bBox3 = theRect3.getBBox();
// get the transformation applied to the rect
let theTransformation = theRect3.getAttribute("transform");
// get the scale applied
let scaleRy = theTransformation
  .split("scale(")[1]
  .split(")")[0]
  .split(","); //[".5", ".5"]
//calculate the circle's scale
let circleScale = `scale(${1 / Number(scaleRy[0])}, ${1 / Number(scaleRy[1])})`;

theUse.setAttributeNS(null, "x", bBox3.x + bBox3.width / 2);
theUse.setAttributeNS(null, "y", bBox3.y + bBox3.height / 2);
//apply the same transformation to the circle
theUse.setAttributeNS(null, "transform", theTransformation);
//scale the circle
c3.setAttributeNS(null, "transform", circleScale);
svg{border:1px solid;}
<svg viewBox="0 0 200 100">
  <defs>
  <g id="cWrap">
  <circle id="c3" r="2" fill="red" transform="scale(2,2)" ></circle>
  </g>
  </defs>
  <rect id="theRect3" x="30" y="10" width="80" height="40" transform="scale(.5,.5) translate(150,50) rotate(30,30,10)" />
  <use id="theUse" xlink:href="#cWrap" />
</svg>


推荐阅读