I'm doing an online customization tool on a website which directly changes an SVG element made on Adobe Illustrator by a graphist.
I have a problem with the transform-origin: center
CSS property which is not understood when opening the svg on Illusrator.
However, for exemple, the transform: rotate(180)
is understood and read.
How can I do to make it works another way (or this way but different ?) please ? Can't find anything on the Internet so far..
CodePudding user response:
A possible workaround might be to convert all transform
properties to matrix transformations.
This way we can reset all transform-orgin
attributes to the default "0 0".
Example: convert all transformations to matrix
// getTransformToElement polyfill
SVGElement.prototype.getTransformToElement =
SVGElement.prototype.getTransformToElement ||
function(toElement) {
return toElement.getScreenCTM().inverse().multiply(this.getScreenCTM());
};
//let svgOrigMarkup = svgOrig.value;
svgPreview.innerHTML = svgOrig.value;
let svg = document.querySelector("svg");
optimizeForAi(svg);
svgOrig.addEventListener('change', function(e) {
svgPreview.innerHTML = e.currentTarget.value;
svg = document.querySelector("svg");
optimizeForAi(svg);
})
function optimizeForAi(svg) {
//add namespace
let xmlns = svg.getAttribute('xmlns');
if (!xmlns) {
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
}
//add viewBox and dimensions
addViewBoxAndWidth(svg);
let els = svg.querySelectorAll("*");
//convert units and transformations
els.forEach(function(el) {
transFormToMatrix(el);
percentageToAbs(svg, el)
});
//update markup
svgMarkup.value = svg.outerHTML;
}
function addViewBoxAndWidth(svg) {
let vB = svg.getAttribute("viewBox");
let [width, height] = [svg.getAttribute("width"), svg.getAttribute("height")];
let [svgW, svgH] = vB
?
[vB.split(" ")[2], vB.split(" ")[3]] : [width, height];
if (!svgW || !svgH) {
let bb = svg.getBBox();
[svgW, svgH] = [bb.width, bb.height];
}
//add viewBox
if (!vB) {
svg.setAttribute("viewBox", [0, 0, svgW, svgH].join(" "));
}
//add width and height
if (!width || !height) {
svg.setAttribute("width", svgW "px");
svg.setAttribute("height", svgH "px");
}
}
function percentageToAbs(svg, el) {
let vB = svg.getAttribute("viewBox");
let [svgW, svgH] = [vB.split(" ")[2], vB.split(" ")[3]];
// find percentage values
let atts = [...el.attributes];
atts.forEach(function(att, i) {
let attName = att.nodeName;
let attVal = att.value;
if (attVal.indexOf("%") !== -1) {
let rel = attName.indexOf("y") !== -1 ? svgH : svgW;
//convert to absolute value
let attValAbs = (rel / 100) * parseFloat(attVal);
el.setAttribute(attName, attValAbs);
}
});
}
function transFormToMatrix(el) {
let type = el.nodeName.toLowerCase();
let matrixString = "";
let types = [
"path",
"polygon",
"polyline",
"rect",
"ellipse",
"circle",
"line",
"text",
"g",
"svg"
];
if (types.indexOf(type) !== -1) {
// get el matrix
let matrix = el.getTransformToElement(el.parentNode);
let [a, b, c, d, e, f] = [
matrix.a,
matrix.b,
matrix.c,
matrix.d,
matrix.e,
matrix.f
];
matrixString = [a, b, c, d, e, f].join(" ");
let matrixStringRound = [a, b, c, d, e, f]
.map((val) => {
return val.toFixed(3);
})
.join(" ");
//exclude non transformed elements
if (matrixStringRound != "1 0 0 1 0 0") {
el.setAttribute("transform", `matrix(${matrixString})`);
el.removeAttribute("transform-origin");
el.style.setProperty('transform-origin', '0px 0px', 'important');
}
}
return matrixString;
}
svg{
max-width:75%;
height:auto;
}
<div id="svgPreview"></div>
<h3>Paste in your svg markup</h3>
<textarea id="svgOrig" style="width:100%; min-height:20em">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 400" >
<style>
.g2{
transform-origin: center;
}
</style>
<rect width="1000" height="400" fill="orange"></rect>
<g transform="translate(-200 50) rotate(-45)" transform-origin="500 200">
<g transform="translate(200 -50)" style="color:green">
<text x="50%" y="50%" transform="rotate(45)" transform-origin="center" text-anchor="middle" font-size="50">Text</text>
</g>
</g>
</svg>
</textarea>
<h3>Optimized</h3>
<textarea id="svgMarkup" style="width:100%; min-height:20em"></textarea>
The transFormToMatrix(el)
method does the matrix calculation and depends on the deprecated getTransformToElement()
method (polyfill included).
Unfortunately transform-orgin
is not be the only property AI or other graphic applications might struggle with (it also depends on you version).
Recommendations:
- Prefer svg style attributes like
transform="rotate(45 100 100)"
Especially svg's rotate() tends to be more robust, since it also allows to define pivot point coordinates - include
xmlns
,viewBox
,xmlns:xlink
,width
,height
attributes - otherwise AI might calculate wrong artboard dimensions. - relative (%) values tend to make troubles – better use userUnits (so unitless values like x="100")
- if your svg contains use elements add legacy xlink:href references like
<use id="sym1" href="#symbol1" xlink:href="#symbol1" />