auto colour fill when Generate Font from svg figma icon using icomoon - svg

I want to convert an svg as an icon font element in my HTML/CSS using icomoon app
<svg width="325" height="350" viewBox="0 0 325 350" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M205.43 59.38C205.43 26.5853 232.015 0 264.81 0C297.605 0 324.19 26.5853 324.19 59.38C324.19 92.1747 297.605 118.76 264.81 118.76C246.525 118.76 230.17 110.495 219.277 97.4975L116.536 157.274C117.985 162.41 118.76 167.83 118.76 173.43C118.76 178.348 118.162 183.127 117.035 187.697L222.002 248.758C232.806 237.522 247.991 230.53 264.81 230.53C297.605 230.53 324.19 257.115 324.19 289.91C324.19 322.705 297.605 349.29 264.81 349.29C232.015 349.29 205.43 322.705 205.43 289.91C205.43 280.791 207.486 272.152 211.159 264.431L109.497 205.292C98.9566 221.836 80.4498 232.81 59.38 232.81C26.5853 232.81 0 206.225 0 173.43C0 140.635 26.5853 114.05 59.38 114.05C79.7735 114.05 97.7659 124.331 108.457 139.992L209.556 81.1716C206.893 74.4248 205.43 67.0733 205.43 59.38ZM264.81 19C242.509 19 224.43 37.0787 224.43 59.38C224.43 81.6813 242.509 99.76 264.81 99.76C287.111 99.76 305.19 81.6813 305.19 59.38C305.19 37.0787 287.111 19 264.81 19ZM59.38 133.05C37.0787 133.05 19 151.129 19 173.43C19 195.731 37.0787 213.81 59.38 213.81C81.6813 213.81 99.76 195.731 99.76 173.43C99.76 151.129 81.6813 133.05 59.38 133.05ZM224.43 289.91C224.43 267.609 242.509 249.53 264.81 249.53C287.111 249.53 305.19 267.609 305.19 289.91C305.19 312.211 287.111 330.29 264.81 330.29C242.509 330.29 224.43 312.211 224.43 289.91Z" fill="#0D0F13"/>
</svg>
But every time I generate the icon font file one of the three circles is filled black in icomoon preview and when used in HTML:
<span class="icon icon-share">

This rendering issue is caused by non ideal path directions.
You icon is a compound path – it contains multiple subpaths (the cut out "holes").
Usually, you define which shapes should be interpreted as solid or cut-out by switching path directions:
E.g. if the outer shape's path commands have a clockwise path direction – the inner ones should use a counter-clockwise direction.
It doesn't matter if you're using clockwise vs. counter-clockwise or the other way around. Inner (cut out) paths just have to use the opposite direction.
Also explained in this article: The Winding Order of the Fill Rule
Svg: fill-rule="evenodd
The original svg file renders fine since it has a fill-rule="evenodd" applied to the path element.
Apparently, icomoon font generator expects your icons to have the aforementioned alternating directions.
In other words it can't use fill-rules when converting svg to font files.
Fix path directions
Here is a helper script to fix inner paths.
const svg = document.querySelector('svg');
const path = svg.querySelector('path');
function fixPath(path) {
// get pathData array
let pathData = path.getPathData({
normalize: true
});
// split sub paths
let pathDataSubArr = splitSubpaths(pathData);
let fixedPathData = fixInnerPathDirections(path, pathDataSubArr);
path.setPathData(fixedPathData);
}
/**
* helpers
*/
function fixInnerPathDirections(path, pathDataSubArr) {
let svg = path.closest('svg');
let fixedPathData = [];
let subPathEls = [];
let bbO = path.getBBox();
let [xO, yO, wO, hO, rO, bO] = [bbO.x, bbO.y, bbO.width, bbO.height, (bbO.x + bbO.width), (bbO.y + bbO
.height)];
let outerPathData = pathDataSubArr[0];
let outerPathTmp = document.createElementNS('http://www.w3.org/2000/svg', 'path');
outerPathTmp.setPathData(outerPathData);
outerPathTmp.classList.add('outer');
let outerClockwise = isClockwise(outerPathTmp)
pathDataSubArr.forEach(function(pathDataSub, i) {
// create temporary subpath elements for checking positions
let subPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
subPath.setPathData(pathDataSub);
subPath.setAttribute('stroke', 'red');
subPath.setAttribute('stroke-width', '1');
subPath.setAttribute('fill', 'none');
svg.appendChild(subPath);
subPathEls.push(subPath);
let bb = subPath.getBBox();
let [x, y, w, h, r, b] = [bb.x, bb.y, bb.width, bb.height, (bb.x + bb.width), (bb.y + bb
.height)];
// remove temporary subpaths
subPath.remove();
if (i > 0) {
// is subpath within outer path
if (x > xO && y > yO && r < rO && b < bO) {
let isClockwiseInner = isClockwise(subPath);
// if subpath has same direction as outer path: reverse direction
if (isClockwiseInner == outerClockwise) {
pathDataSub = reversePathData(pathDataSub);
}
}
}
fixedPathData = fixedPathData.concat(pathDataSub);
})
return fixedPathData;
}
function reversePathData(pathData) {
let M = pathData[0];
let newPathData = [M];
// split subpaths
let subPathDataArr = splitSubpaths(pathData);
subPathDataArr.forEach(function(subPathData, s) {
let subPathDataL = subPathData.length;
let closed = subPathData[subPathDataL - 1]['type'] == 'Z' ? true : false;
let subM = subPathData[0]['values'];
// insert Lineto if last path segment has created by z
let lastCom = closed ? subPathData[subPathDataL - 2] : subPathData[subPathDataL - 1];
let lastComL = lastCom['values'].length;
let lastXY = [lastCom['values'][lastComL - 2], lastCom['values'][lastComL - 1]];
let diff = Math.abs(subM[0] - lastXY[0]);
if (diff > 1 && closed) {
subPathData.pop();
subPathData.push({
'type': 'L',
'values': [subM[0], subM[1]]
});
subPathData.push({
'type': 'Z',
'values': []
});
}
subPathData.forEach(function(com, i) {
// reverse index
let subpathDataL = subPathData.length;
let indexR = subpathDataL - 1 - i;
let comR = subPathData[indexR];
let comF = subPathData[i];
let [typeR, valuesR] = [comR['type'], comR['values']];
let [typeF, valuesF] = [comF['type'], comF['values']];
if (typeF == 'M' && s > 0) {
newPathData.push(comF);
} else if (typeR != 'M' && typeR != 'Z') {
indexR--;
let prevCom = i > 0 ? subPathData[indexR] : subPathData[subpathDataL - 1 - i];
let prevVals = prevCom ? (prevCom['values'] ? prevCom['values'] : [0, 0]) : [];
prevVals = prevCom['values'];
let prevValsL = prevVals.length;
let newCoords = [];
if (typeR == 'C') {
newCoords = [
valuesR[2], valuesR[3],
valuesR[0], valuesR[1],
prevVals[prevValsL - 2], prevVals[prevValsL - 1]
];
if (!closed) {
let nextVals = i < subpathDataL - 1 ? subPathData[i + 1]['values'] : lastXY;
let lastCX = (i < subpathDataL - 2) ? nextVals[prevValsL - 2] : subM[0];
let lastCY = (i < subpathDataL - 2) ? nextVals[prevValsL - 1] : subM[1];
newCoords[4] = lastCX;
newCoords[5] = lastCY;
}
} else {
newCoords = [prevVals[prevValsL - 2], prevVals[prevValsL - 1]];
}
newPathData.push({
'type': typeR,
'values': newCoords
});
}
})
//use last coordinates as M values if path isn't closed
if (!closed) {
newPathData[0]['values'] = lastXY;
}
if (closed) {
newPathData.push({
'type': 'Z',
'values': []
});
}
});
return newPathData;
}
/**
* check path direction of polygon
* based on answer:
* #mpen: https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order/1180256#answer-46613578
*/
function isClockwise(path, divisions = 50) {
var length = path.getTotalLength();
let p1 = path.getPointAtLength(0);
let A = [p1.x, p1.y];
let p2 = path.getPointAtLength(length * 0.25);
let B = [p2.x, p2.y];
let p3 = path.getPointAtLength(length * 0.75);
let C = [p3.x, p3.y];
let poly = [A, B, C];
let end = poly.length - 1;
let sum = poly[end][0] * poly[0][1] - poly[0][0] * poly[end][1];
for (let i = 0; i < end; ++i) {
const n = i + 1;
sum += poly[i][0] * poly[n][1] - poly[n][0] * poly[i][1];
}
let cw = sum > 0 ? true : false;
return cw;
}
function splitSubpaths(pathData) {
let pathDataL = pathData.length;
let subPathArr = [];
let subPathMindex = [];
pathData.forEach(function(com, i) {
let [type, values] = [com['type'], com['values']];
if (type == 'M') {
subPathMindex.push(i);
}
});
//split segments after M command
subPathMindex.forEach(function(index, i) {
let n = subPathMindex[i + 1];
let thisSeg = pathData.slice(index, n);
subPathArr.push(thisSeg)
})
return subPathArr;
}
<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill#1.0.3/path-data-polyfill.min.js"></script>
<p><button type="button" onclick="fixPath(path)">Fix path directions</button></p>
<svg width="325" height="350" viewBox="0 0 325 350" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="
M205.43 59.38C205.43 26.5853 232.015 0 264.81 0C297.605 0 324.19 26.5853 324.19 59.38C324.19 92.1747 297.605 118.76 264.81 118.76C246.525 118.76 230.17 110.495 219.277 97.4975L116.536 157.274C117.985 162.41 118.76 167.83 118.76 173.43C118.76 178.348 118.162 183.127 117.035 187.697L222.002 248.758C232.806 237.522 247.991 230.53 264.81 230.53C297.605 230.53 324.19 257.115 324.19 289.91C324.19 322.705 297.605 349.29 264.81 349.29C232.015 349.29 205.43 322.705 205.43 289.91C205.43 280.791 207.486 272.152 211.159 264.431L109.497 205.292C98.9566 221.836 80.4498 232.81 59.38 232.81C26.5853 232.81 0 206.225 0 173.43C0 140.635 26.5853 114.05 59.38 114.05C79.7735 114.05 97.7659 124.331 108.457 139.992L209.556 81.1716C206.893 74.4248 205.43 67.0733 205.43 59.38Z
M264.81 19C242.509 19 224.43 37.0787 224.43 59.38C224.43 81.6813 242.509 99.76 264.81 99.76C287.111 99.76 305.19 81.6813 305.19 59.38C305.19 37.0787 287.111 19 264.81 19Z
M59.38 133.05C37.0787 133.05 19 151.129 19 173.43C19 195.731 37.0787 213.81 59.38 213.81C81.6813 213.81 99.76 195.731 99.76 173.43C99.76 151.129 81.6813 133.05 59.38 133.05Z
M224.43 289.91C224.43 267.609 242.509 249.53 264.81 249.53C287.111 249.53 305.19 267.609 305.19 289.91C305.19 312.211 287.111 330.29 264.81 330.29C242.509 330.29 224.43 312.211 224.43 289.91Z
" fill="#0D0F13" />
</svg>
(Inspect the element in your dev tools to get the new path)
How it works
path commands are parsed (using path-data-polyfill by Jarek Foksa)
1.2 the path data is retrieved in a normalized way (path.getPathData({normalize: true}):
all commands will be converted to a reduced set using only absolute M, L, C, Z commands.
split up path data into sub paths (each sub path starts with a new M command)
check if sub paths are intersecting with the outer shape
check directions between outer and inner sub paths (using the isClockwise(path) helper)
reverse sub path direction if needed (reversePathData(pathdata))
concatenate sub paths and overwrite the original d property
Codepen example
You might also use this codepen helper: Fix svg compound path direction
(still pretty buggy, I'm working on it ...)
HTML/CSS example
#font-face {
font-family: 'icomoon';
src: url('data:font/woff2;charset=utf-8;base64,d09GMgABAAAAAAKsAA0AAAAABpgAAAJWAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCXhEICoIwgicLDgABNgIkAxgEIAWDGwdFG5gFyJ4FdpMzjFBuZhHDYhE2Lyuvefjx8LVWvt89OwuoLkDsUKWigS2BIwnAKmwRyqeiD1z8VXT0WrS2WSR7w9M/YpFmHip+uESqS2jQkv/fzbMtejpWgbRBCusLh5mNJZxQLwq8+J+4Gbcs0ayoyySyNm4KAw7HxADTzsMwkLHPpyWQZQUKD0Zv7Xy3GOQpg6RIkmGRRZTNaBEChl5xBP7+oo23DrQwAJIQKkKBhJyGYq1CepJu3KLi+GdF1DGd6Klcj0blY/2GbQYFAcSpCPnkiQjsIBSYSGQ1aKhJkjB1VP8HxD9n+u8YsSw0AIRMlEQGIiEBAIICrg7RJwElaKGKPeAyoAAEQihyzqva7VbQSyfP/JuzIacHG9Ph+enDBacfK4dHbovJUxU8A6u/WJQ7Xqm9+nF6qDkbiclwo2VkBgb1ANjxPsxY2H3AZ4bRsplS0Zw5z18lt1pWnMqpZVxycbNKnuXD1oYHbk6mZGvfmfmdMLq4AgJQAH3+SAIPQNz+esn3KSVZ/Z/wwqyM0of8OfegJoEw3uUoQP4sfyGUBSC2AvhN/opAaTcRSAAA6HgjQKjqIZBUzUUgq1qPQKFqBwIVTacQKFXdF6BhvJcsirrQIQIJGQtkZClQIBuxigFprNRxvmfDSncH5TTVNTU14OCqn9JsAJswRX01GR14mZYGrQlL5G2Yjq6yW8/JxgFnVkarTN86GzQ19OgVMQ1MR0YPk6eXNaYXISRh2uCeHNrBzTrX/NNhDoTEti+JJEWWoyj3Hxm3Sk58/FjZQZy3Ai5jAAAA') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
.icon{
font-family: 'icomoon';
font-size:5vw
}
.icon-share:before {
content: "\e900";
}
<span class="icon icon-share">

Related

SVG mouse position relative to transformed element

I have some SVG elements and I would like to add the ability to resize using mouse.
It works great when element doesn't have any transforms (matrix, rotate, scale etc).
If I remove transform (transform="matrix(1.1,1.1,1,0,0,0)") from the rect element - it works great.
My understanding is that I should use matrix.inverse() function and transform it so I get mouse coordinates in the element space, but everything I tried didn't work for me.
I need to correctly calculate the x, y difference between start drag point and current point, but it doesn't work.
var rect = document.getElementById('rect');
var grip = document.getElementById('grip');
function moveGrip() {
var baseMatrix = rect.getCTM(),
x = rect.width.baseVal.value + rect.x.baseVal.value,
y = rect.height.baseVal.value + rect.y.baseVal.value;
grip.cx.baseVal.value = x * baseMatrix.a + y * baseMatrix.c + baseMatrix.e;
grip.cy.baseVal.value = x * baseMatrix.b + y * baseMatrix.d + baseMatrix.f;
}
grip.addEventListener('mousedown', onGripMouseDown);
document.addEventListener('mousemove', onDocMouseMove);
var startDragPoint, startDragSize;
function onGripMouseDown(evt) {
startDragSize = {w: rect.width.baseVal.value, h: rect.height.baseVal.value};
startDragPoint = {x: evt.clientX, y: evt.clientY};
}
function onDocMouseMove(evt) {
if (evt.buttons & 1) {
// dragging
rect.width.baseVal.value = startDragSize.w + evt.clientX - startDragPoint.x;
rect.height.baseVal.value = startDragSize.h + evt.clientY - startDragPoint.y;
moveGrip();
}
}
moveGrip();
<svg width="500" height="400">
<rect id="rect" x="20" y="20" width="200" height="100"
transform="matrix(1.1,1.1,1,0,0,0)"
style="fill:none;stroke: #3a2dd0; stroke-width: 2px;"></rect>
<g>
<circle id="grip" r="3" stroke-width="1" stroke="green"></circle>
</g>
</svg>
Probably, you'd need to apply the inverse matrix to delta-x,y as follows;
function onDocMouseMove(evt) {
if (evt.buttons & 1) {
// dragging
var invMatrix = rect.getCTM().inverse(),
x = evt.clientX - startDragPoint.x,
y = evt.clientY - startDragPoint.y;
rect.width.baseVal.value = startDragSize.w + x * invMatrix.a + y * invMatrix.c;
rect.height.baseVal.value = startDragSize.h + x * invMatrix.b + y * invMatrix.d;
moveGrip();
}
}

I have one path(heart) , scaled to reduce size(width and height) with added transform value. I need difference width of added and scaled path

Below different transform values of same path
Path with first transform value.
<path id="cropMarkMask" class="bleedPath" d="M418.8267682588199,227.4779418368866C418.19489615365677,228.3214910972795,417.6135738169066,229.21559012608546,417.08911996962104,230.1633982838303C384.0200933459036,280.60890779953655,338.2093657215696,328.4195106367121,279.6569370966192,373.5983661558829C276.601835468155,375.8067591634284,273.5467338396908,378.0720206604385,270.4947915717524,380.38783192586175C254.17353509538623,391.86894807667755,239.22028172669843,397.3441198679169,225.63187210516324,396.81650666010563C225.42335431045936,396.81650666010563,225.26222692364274,396.81650666010563,225.1579680262908,396.81650666010563C225.1579680262908,396.81650666010563,224.8420319737092,396.81650666010563,224.8420319737092,396.81650666010563C224.63351417900532,396.81650666010563,224.4723867921887,396.81650666010563,224.36812789483676,396.81650666010563C210.78287763382738,397.3441198679169,195.82962426513959,391.86894807667755,179.5052084282476,380.38783192586175C176.45326616030923,378.0720206604385,173.39816453184503,375.8067591634284,170.34306290338083,373.5983661558829C111.79063427843042,328.4195106367121,65.97990665409648,280.60890779953655,32.91088003037898,230.1633982838303C32.383266822567684,229.21559012608546,31.805103846343336,228.3214910972795,31.173231741180103,227.4779418368866C7.689704952788771,188.09335352206264,-2.527666987700611,149.91564092810046,0.5274346407635936,112.95112277605168C3.4782773718758646,76.09086352135482,19.221370872017665,47.02474668384638,47.75987450171482,25.752772263526335C93.25466607346718,-8.156644260058247,144.3825974627497,-8.57999857051761,201.14682803008822,24.48902805319989C209.1494882419805,29.22806884192409,216.88992153023005,31.439621209995384,224.36812789483682,31.123685157413775C224.57980505006645,31.123685157413775,224.78832284477033,31.123685157413775,225.00000000000003,31.123685157413775C225.2085177947039,31.123685157413775,225.42019494993357,31.123685157413775,225.63187210516327,31.123685157413775C233.1100784697701,31.335362312643458,240.85051175801962,29.123809944572166,248.85317196991187,24.48902805319989C305.61740253725037,-8.579998570517605,356.7453339265329,-8.156644260058242,402.2401254982853,25.752772263526335C430.78178848850814,47.02474668384638,446.52488198865,76.09402288188062,449.4725653592364,112.95112277605168C452.5276669877007,149.91564092810046,442.31029504721124,188.09335352206264,418.8267682588199,227.4779418368866C418.8267682588199,227.4779418368866,418.8267682588199,227.4779418368866,418.8267682588199,227.4779418368866" width="357.21000000000004" height="315.02055178728943" aWidth="0" aHeight="0" x="71.39499999999998" y="92.48972410635528" aX="0" aY="0" fill="none" stroke="#ff0000" style="pointer-events: none; display: block;" display="none" transform="translate(107.11599999999999,128.2063399552543) scale(0.63504,0.6138000000000001)"></path>
Path with second transform value.
<path id="safeMarkMask" class="bleedPath" d="M418.8267682588199,227.4779418368866C418.19489615365677,228.3214910972795,417.6135738169066,229.21559012608546,417.08911996962104,230.1633982838303C384.0200933459036,280.60890779953655,338.2093657215696,328.4195106367121,279.6569370966192,373.5983661558829C276.601835468155,375.8067591634284,273.5467338396908,378.0720206604385,270.4947915717524,380.38783192586175C254.17353509538623,391.86894807667755,239.22028172669843,397.3441198679169,225.63187210516324,396.81650666010563C225.42335431045936,396.81650666010563,225.26222692364274,396.81650666010563,225.1579680262908,396.81650666010563C225.1579680262908,396.81650666010563,224.8420319737092,396.81650666010563,224.8420319737092,396.81650666010563C224.63351417900532,396.81650666010563,224.4723867921887,396.81650666010563,224.36812789483676,396.81650666010563C210.78287763382738,397.3441198679169,195.82962426513959,391.86894807667755,179.5052084282476,380.38783192586175C176.45326616030923,378.0720206604385,173.39816453184503,375.8067591634284,170.34306290338083,373.5983661558829C111.79063427843042,328.4195106367121,65.97990665409648,280.60890779953655,32.91088003037898,230.1633982838303C32.383266822567684,229.21559012608546,31.805103846343336,228.3214910972795,31.173231741180103,227.4779418368866C7.689704952788771,188.09335352206264,-2.527666987700611,149.91564092810046,0.5274346407635936,112.95112277605168C3.4782773718758646,76.09086352135482,19.221370872017665,47.02474668384638,47.75987450171482,25.752772263526335C93.25466607346718,-8.156644260058247,144.3825974627497,-8.57999857051761,201.14682803008822,24.48902805319989C209.1494882419805,29.22806884192409,216.88992153023005,31.439621209995384,224.36812789483682,31.123685157413775C224.57980505006645,31.123685157413775,224.78832284477033,31.123685157413775,225.00000000000003,31.123685157413775C225.2085177947039,31.123685157413775,225.42019494993357,31.123685157413775,225.63187210516327,31.123685157413775C233.1100784697701,31.335362312643458,240.85051175801962,29.123809944572166,248.85317196991187,24.48902805319989C305.61740253725037,-8.579998570517605,356.7453339265329,-8.156644260058242,402.2401254982853,25.752772263526335C430.78178848850814,47.02474668384638,446.52488198865,76.09402288188062,449.4725653592364,112.95112277605168C452.5276669877007,149.91564092810046,442.31029504721124,188.09335352206264,418.8267682588199,227.4779418368866C418.8267682588199,227.4779418368866,418.8267682588199,227.4779418368866,418.8267682588199,227.4779418368866" width="357.21000000000004" height="315.02055178728943" aWidth="0" aHeight="0" x="71.39499999999998" y="92.48972410635528" aX="0" aY="0" fill="none" stroke="#2B8F49" stroke-width="2" stroke-dasharray="5,5" style="pointer-events: none; display: block;" display="none" transform="translate(142.837,163.92295580415333) scale(0.47628000000000004,0.43380000000000013)"></path>
This is not exactly answering your question but I think you'll find it useful. I draw 2 rectangles, one for each heart: the transformed bounding box. The code is giving you (please open the console) the size and the position of those boxes so that you can calculate the difference you need:
const SVG_NS = 'http://www.w3.org/2000/svg';
const SVG_XLINK = "http://www.w3.org/1999/xlink"
const svg = document.querySelector("svg")
let heart_bbox = heart.getBBox();
// untransformed heart
let W0 = heart_bbox.width;
let H0 = heart_bbox.height;
let x0 = heart_bbox.x;
let y0 = heart_bbox.y;
// red heart
let bbox1 = {
width: W0 * 0.635,
height: H0 * 0.614,
x : x0 + 107.116,
y : y0 + 128.206
}
console.log("red heart",bbox1);
// green heart
let bbox2 = {
width: W0 * 0.476,
height: H0 * 0.434,
x : x0 + 142.837,
y : y0 + 163.923
}
console.log("green heart",bbox2);
drawRect(bbox1, svg)
drawRect(bbox2, svg)
function drawRect(o, parent) {
let rect = document.createElementNS(SVG_NS, 'rect');
for (let name in o) {
if (o.hasOwnProperty(name)) {
rect.setAttributeNS(null, name, o[name]);
}
}
parent.appendChild(rect);
return rect;
}
rect{stroke:#d9d9d9; fill:none;}
<svg viewBox = "-10 -10 500 500">
<defs>
<path id="heart" d="M418.8267682588199,227.4779418368866C418.19489615365677,228.3214910972795,417.6135738169066,229.21559012608546,417.08911996962104,230.1633982838303C384.0200933459036,280.60890779953655,338.2093657215696,328.4195106367121,279.6569370966192,373.5983661558829C276.601835468155,375.8067591634284,273.5467338396908,378.0720206604385,270.4947915717524,380.38783192586175C254.17353509538623,391.86894807667755,239.22028172669843,397.3441198679169,225.63187210516324,396.81650666010563C225.42335431045936,396.81650666010563,225.26222692364274,396.81650666010563,225.1579680262908,396.81650666010563C225.1579680262908,396.81650666010563,224.8420319737092,396.81650666010563,224.8420319737092,396.81650666010563C224.63351417900532,396.81650666010563,224.4723867921887,396.81650666010563,224.36812789483676,396.81650666010563C210.78287763382738,397.3441198679169,195.82962426513959,391.86894807667755,179.5052084282476,380.38783192586175C176.45326616030923,378.0720206604385,173.39816453184503,375.8067591634284,170.34306290338083,373.5983661558829C111.79063427843042,328.4195106367121,65.97990665409648,280.60890779953655,32.91088003037898,230.1633982838303C32.383266822567684,229.21559012608546,31.805103846343336,228.3214910972795,31.173231741180103,227.4779418368866C7.689704952788771,188.09335352206264,-2.527666987700611,149.91564092810046,0.5274346407635936,112.95112277605168C3.4782773718758646,76.09086352135482,19.221370872017665,47.02474668384638,47.75987450171482,25.752772263526335C93.25466607346718,-8.156644260058247,144.3825974627497,-8.57999857051761,201.14682803008822,24.48902805319989C209.1494882419805,29.22806884192409,216.88992153023005,31.439621209995384,224.36812789483682,31.123685157413775C224.57980505006645,31.123685157413775,224.78832284477033,31.123685157413775,225.00000000000003,31.123685157413775C225.2085177947039,31.123685157413775,225.42019494993357,31.123685157413775,225.63187210516327,31.123685157413775C233.1100784697701,31.335362312643458,240.85051175801962,29.123809944572166,248.85317196991187,24.48902805319989C305.61740253725037,-8.579998570517605,356.7453339265329,-8.156644260058242,402.2401254982853,25.752772263526335C430.78178848850814,47.02474668384638,446.52488198865,76.09402288188062,449.4725653592364,112.95112277605168C452.5276669877007,149.91564092810046,442.31029504721124,188.09335352206264,418.8267682588199,227.4779418368866C418.8267682588199,227.4779418368866,418.8267682588199,227.4779418368866,418.8267682588199,227.4779418368866" />
</defs>
<use xlink:href ="#heart" fill="none" stroke="#ff0000" transform="translate(107.11599999999999,128.2063399552543) scale(0.63504,0.6138000000000001)"></use>
<use xlink:href ="#heart" fill="none" stroke="#2B8F49" vector-effect="non-scaling-stroke" stroke-dasharray="5,5" transform="translate(142.837,163.92295580415333) scale(0.47628000000000004,0.43380000000000013)"></path>
</svg>

Evaluating SVG Filter Performance (Alphamap)

Is there a known tool to evaluate SVG Filter Performance? I am generating an alphamap using the following SVG node, but am struggling a little with the performance.
<feColorMatrix result="alphaOut" in="SourceGraphic" type="matrix" values="0 0 0 0 1, 0 0 0 0 1, 0 0 0 0 1, 0 0 0 1 0" />
Is there a way to quantify rendering times for SVG filters? Alternativly, does anybody know a better way to extract alpha information using SVG filters?
Idea for measuring performance taken from https://developers.google.com/web/updates/2012/07/How-to-measure-browser-graphics-performance
function draw(x) {
var g = document.createElementNS("http://www.w3.org/2000/svg", "g")
g.id = "canvas"
canvas.parentNode.replaceChild(g, canvas)
for (var i = 1; i < x; i++) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect")
rect.setAttribute("width", "100")
rect.setAttribute("height", "100")
rect.setAttribute("filter", "url(#" + filter + ")")
rect.setAttribute("fill", "rgba(128,128,128,0.8)")
rect.setAttribute("x", Math.random() * 300)
rect.setAttribute("y", Math.random() * 300)
g.appendChild(rect)
}
}
var filter = "f1"
var t, previousTime;
var drawLoad = 1;
var slowCount = 0;
var maxSlow = 10;
// Note, you might need to polyfill performance.now and requestAnimationFrame
t = previousTime = performance.now();
var tick = function() {
var maximumFrameTime = 1000 / 30; // 30 FPS
t = performance.now();
var elapsed = t - previousTime;
previousTime = t;
if (elapsed < maximumFrameTime || slowCount < maxSlow) {
if (elapsed < maximumFrameTime) {
drawLoad += 10;
} else {
slowCount++;
}
draw(drawLoad);
requestAnimationFrame(tick);
} else {
// found maximum sustainable load at 30 FPS
document.getElementById('res').innerHTML = ("could draw " + (drawLoad) + " in " +
maximumFrameTime + " ms");
}
};
requestAnimationFrame(tick);
svg {
background: beige
}
<button onclick="filter='f1';drawLoad=1;slowCount=0;requestAnimationFrame(tick)">test filter 1</button>
<button onclick="filter='f2';drawLoad=1;slowCount=0;requestAnimationFrame(tick)">test filter 2</button>
<div id="res"></div>
<svg width="400" height="400">
<filter id="f1">
<feColorMatrix result="alphaOut" in="SourceGraphic" type="matrix" values="0 0 0 0 1
0 0 0 0 1
0 0 0 0 1
0 0 0 1 0" />
</filter>
<filter id="f2">
<feMerge>
<feMergeNode dx="1" dy="1" in="SourceAlpha" />
</feMerge>
</filter>
<g id="canvas">
</g>
</svg>

svg arc path getBBox result is wrong when the coordnate is very big in Chrome

There is a path element of "M 1050000 80 A 40 40 0 1 0 1050000 40", getBBox() result width and height are: 0.0000711679458618164, 0.00007629334868397564
If move the path 10000 pixels towards left, become "M 1040000 80 A 40 40 0 1 0 1040000 40", then getBBox() result width and height are: 74.625, 79.99937438964844
The element's width and height are not changed but getBBox() give different results.
What's the reason and how to avoid this issue ? getBoundingClientRect() ?
Testing snippets:
function $$(id){return document.getElementById(id);}
var b1 = $$("p1").getBBox();
$$("r1").innerHTML=('p1: width:'+b1.width +', height:'+ b1.height);
//0.0000711679458618164, 0.00007629334868397564
var b2 = $$("p2").getBBox();
$$("r2").innerHTML=('p2: width:'+b2.width +', height:'+ b2.height);
//74.625, 79.99937438964844
<svg width="110" height="110" viewBox="1050000 0 110 110">
<path id="p1" d="M 1050000 80 A 40 40 0 1 0 1050000 40" stroke="#880000" stroke-width="1" fill="none"></path>
</svg>
<svg width="110" height="110" viewBox="1040000 0 110 110">
<path id="p2" d="M 1040000 80 A 40 40 0 1 0 1040000 40" stroke="#880000" stroke-width="1" fill="none"></path>
</svg>
<p id="r1"></p>
<p id="r2"></p>
Has confirmed this is a bug of chromium: issue 678162. It's originally caused by the code bug of Skia engine. The fixing is committed.
Only some versions of Chrome has this bug. As my testing, Chrome 52 is ok, Chrome 55 has bug.
On my case, I just need to multiple the wrong bbox by 0x100000.
0.0000711679458618164 * 0x100000 == 74.625
0.00007629334868397564 * 0x100000 == 79.99937438964844
Finally I use a custom function to calculate bbox for chrome.
This function cannot handle the nested svg(s) or nested transforms.
function bbox (element) {
var svg = element.farthestViewportElement,
mtr = element.getScreenCTM().inverse(),
bcr = element.getBoundingClientRect(),
x1 = bcr.left, x2 = bcr.right,
y1 = bcr.top, y2 = bcr.bottom,
pts = [[x1,y1],[x1,y2],[x2,y1],[x2,y2]],
pt = svg.createSVGPoint(), pt2,
bb = {}, u;
if(x1===0 && x2===0 && y1===0 && y2===0){
return {x:0,y:0,width:0,height:0};
}
for(var i=0; i < 4; i++){
pt.x = pts[i][0];
pt.y = pts[i][1];
pt2 = pt.matrixTransform(mtr);
if(bb.xmin === u){
bb.xmin = bb.xmax = pt2.x;
bb.ymin = bb.ymax = pt2.y;
}else{
if(bb.xmin > pt2.x){
bb.xmin = pt2.x;
}else if(bb.xmax < pt2.x){
bb.xmax = pt2.x;
}
if(bb.ymin > pt2.y){
bb.ymin = pt2.y;
}else if(bb.ymax < pt2.y){
bb.ymax = pt2.y;
}
}
}
return {
x: bb.xmin,
y: bb.ymin,
width: bb.xmax - bb.xmin,
height: bb.ymax - bb.ymin
};
}

SVG Coordinate translation

Why doesn't this SVG inline code produce a box 60 by 40 with a dot in the middle?
I would like to plot items on a regular Cartesian coordinate map using SVG. The data I would have would be the size of the displayable map and center point on the Cartesian main map.
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="40" viewBox="0 0 60 40">
<g transform="translate(-470,480) scale(1,-1)">
<circle title="Center" cx="500" cy="500" r="3" fill="red"/>
</g>
</svg>
Your code is creating an 60 by 40 pixel SVG, then:
Draws a circle centred on (500, 500)
The scale transform moves the circle to (500, -500)
The translate transform moves it to (30, -20)
If you change the transform to transform="translate(-470,520) scale(1,-1)" you should get what I think you want.
I came up with this code snippet that will create a map of with Cartesian coordinates and plot them in an SVG window using those coordinates. Hope this will help somebody.
The function takes the center of the map as $x, $y and draws the map around that coordinate.
public static function xyMap( $x, $y, $width = 0, $height = 0, $show = array('X')) {
$minx = $x - ($width / 2);
$maxx = $x + ($width / 2);
if ($minx < 0) {
$minx = 0;
$maxx = $width;
} elseif ($maxx > Yii::app()->params['maxMapX']) {
$maxx = Yii::app()->params['maxMapX'];
$minx = Yii::app()->params['maxMapX'] - $width;
}
$miny = $y - ($height / 2);
$maxy = $y + ($height / 2);
if ($miny < 0) {
$miny = 0;
$maxy = $height;
} elseif ($maxy > Yii::app()->params['maxMapY']) {
$maxy = Yii::app()->params['maxMapY'];
$miny = Yii::app()->params['maxMapY'] - $height;
}
$x_xform = -1 * $minx;
$y_xform = $maxy;
$x_scale = 1;
$y_scale = -1;
echo "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"$width\" height=\"$height\" viewBox=\"0 0 $width $height\">\n";
echo "<g transform=\"translate($x_xform, $y_xform) scale($x_scale,$y_scale)\">\n";
echo "<rect x=\"$minx\" y=\"$miny\" width=\"$width\" height=\"$height\" stroke=\"black\" stroke-width=\"2\" fill=\"white\" />\n";
echo "<circle title=\"Center\" cx=\"$x\" cy=\"$y\" r=\"3\" fill=\"red\"/>\n";
echo "</g>\n</svg>\n";
}

Resources