一个svg元素在另一个中居中时比例之间的关系

时间:2019-01-15 06:41:50

标签: 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。

2 个答案:

答案 0 :(得分:2)

实际上,您需要将xy乘以scaleFactor,因为scaleFactorDecimal Fraction

return translate(-x * scaleFactor, -y * scaleFactor);
  

如果实际比例为0.5,则需要除以2,但如果为1,则需要除以1

这意味着:

x / 2 <=> x * 0.5
x / 1 <=> x * 1

因此x * scaleFactory * scaleFactor可以与所有scaleFactor小数部分的值一起正常工作。

以下是一些测试x, y值的示例:

const centerElementInOtherDiv = (x, y, scaleFactor = 1) => {
  return {
    x: -x / scaleFactor,
    y: -y / scaleFactor
  };
};

const centerElementInOtherMul = (x, y, scaleFactor = 1) => {
  return {
    x: -x * scaleFactor,
    y: -y * scaleFactor
  };
};


const svgCoords = [
  { x: 100, y: 100 },
  { x: 200, y: 200 },
  { x: 100, y: 300 }
];

for(svgCoord of svgCoords) {
  let mulXY = centerElementInOtherMul(svgCoord.x, svgCoord.y);
  let divXY = centerElementInOtherDiv(svgCoord.x, svgCoord.y);
  console.log(`scaleFactor = 1   -> mul(x: ${mulXY.x}, y: ${mulXY.y}), div(x: ${divXY.x}, y: ${divXY.y})`)

  mulXY = centerElementInOtherMul(svgCoord.x, svgCoord.y, 0.5);
  divXY = centerElementInOtherDiv(svgCoord.x, svgCoord.y, 0.5);
  console.log(`scaleFactor = 0.5 -> mul(x: ${mulXY.x}, y: ${mulXY.y}), div(x: ${divXY.x}, y: ${divXY.y})`)
}

,结果输出将是:

scaleFactor = 1   -> mul(x: -100, y: -100), div(x: -100, y: -100)
scaleFactor = 0.5 -> mul(x: -50, y: -50), div(x: -200, y: -200)
scaleFactor = 1   -> mul(x: -200, y: -200), div(x: -200, y: -200)
scaleFactor = 0.5 -> mul(x: -100, y: -100), div(x: -400, y: -400)
scaleFactor = 1   -> mul(x: -100, y: -300), div(x: -100, y: -300)
scaleFactor = 0.5 -> mul(x: -50, y: -150), div(x: -200, y: -600)

如您所见,乘法可以按预期工作,因为您正在处理小数部分。

答案 1 :(得分:2)

我很想看看SVG代码。

将svg元素居中放置在另一个已转换元素中的一种方法是将两个元素都包裹在<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>

如果只希望缩小矩形而不希望缩小圆,则可以将圆包裹在一组<g>中,请使用该组并将圆按适当的比例放大:

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>