javascript - Place one SVG element on top of another when using a non proportional viewbox -
i have svg:
<svg id="root" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="347.27mm" viewbox="0 0 409.32783 1230.3321" width="115.48mm" version="1.1"> <circle id="b1" cx="0" cy="0" r="15" class="bottom" /> <circle id="b2" cx="120" cy="100" r="15" class="bottom" /> <g transform="translate(17,43)"> <circle id="b3" cx="240" cy="200" r="15" class="bottom" /> <circle id="b4" cx="360" cy="300" r="15" class="bottom" /> </g> <!-- <g transform="translate(10,20)"> --> <circle id="t1" cx="80" cy="30" r="15" class="top" /> <circle id="t2" cx="130" cy="30" r="15" class="top" /> <!-- <g transform="translate(10,20)"> --> <circle id="t3" cx="180" cy="30" r="15" class="top" /> <circle id="t4" cx="230" cy="30" r="15" class="top" /> <!-- </g> --> <!-- </g> --> </svg> after red balls coordinates changed code follows. renders as:
i want center red balls on top of green ones. have solution works if remove viewbox attribute in svg tag. unfortunately can't that. however, works if viewbox isn't there:
var svg = document.getelementbyid("root"); (var index = 1; index <= 4; index++) { var bottom = svg.getelementbyid("b" + index); var top = svg.getelementbyid("t" + index); var targetx = parsefloat(top.getattribute("cx")); var targety = parsefloat(top.getattribute("cy")); var center = getelementcenter(svg, bottom); top.setattribute("cx", parsefloat(center.x)); top.setattribute("cy", parsefloat(center.y)); } function gettransformedcoords(x, y, matrix) { var transformedx = x * matrix.a + y * matrix.c + matrix.e; var transformedy = x * matrix.b + y * matrix.d + matrix.f; return { x: transformedx, y: transformedy }; } function getelementcenter(svg, element) { var x, y, width = 0, height = 0; var boundingbox = element.getbbox(); var centerx = boundingbox.x + (boundingbox.width / 2); var centery = boundingbox.y + (boundingbox.height / 2); var out = gettransformedcoords(centerx, centery, element.getctm()); // getctm affected viewport centerx = out.x; centery = out.y; return { x: centerx, y: centery }; } my working theory because viewbox hasn't got same ratio viewport. don't know how fix that.
i want use plain javascript this. if nice use suitable library.
questions:
- how can make work while keeping viewbox?
- is possible make account transformations in green circles well? commented out in svg, nice have solid works when they're in there well. number 1 enough me accept answer.
jsfiddle if want try code out: https://jsfiddle.net/kentl/gpqcdecx/
ps. here's css if want style it:
.top {fill:red;animation:fall 4s ease-in-out infinite;} .bottom{fill:green;} @keyframes fall {0%{opacity:0.0;}50%{opacity:1.0;}100%{opacity:0.0;}}
just use gettransformtoelement. chrome you'll need polyfill though it's no longer natively implemented there.
// goal place red circles on top of green ones // working theory: // viewbox hasn't got same ratio viewport, affects returned transformation, returning // matrix larger numbers 1 scaling x , y. matrix used convert // local coordinate space in bottom element, global 1 used top element, coordinates // scaled slightly.. when coordinates set in top element, they'll affected again // same up-scaling. making them become more , more off further 0,0 come. // question: // 1. how can fixed? // 2. if it's not hard: uncomment <g>'s in svg. how compensate affected top ctm well? console.clear(); var svg = document.getelementbyid("root"); (var index = 1; index <= 4; index++) { var bottom = document.getelementbyid("b" + index); var top1 = document.getelementbyid("t" + index); var targetx = parsefloat(top1.getattribute("cx")); var targety = parsefloat(top1.getattribute("cy")); var center = getelementcenter(svg, bottom); t = bottom.gettransformtoelement(top1); top1.setattribute("cx", parsefloat(center.x) + t.e); top1.setattribute("cy", parsefloat(center.y) + t.f); } function getelementcenter(svg, element) { var x, y, width = 0, height = 0; var boundingbox = element.getbbox(); var centerx = boundingbox.x + (boundingbox.width / 2); var centery = boundingbox.y + (boundingbox.height / 2); return { x: centerx, y: centery }; } .top { fill: red; animation: fall 4s ease-in-out infinite; } .bottom { fill: green; } @keyframes fall { 0% { opacity: 0.0; } 50% { opacity: 1.0; } 100% { opacity: 0.0; } } <svg id="root" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="347.27mm" viewbox="0 0 409.32783 1230.3321" width="115.48mm" version="1.1"> <circle id="b1" cx="0" cy="0" r="15" class="bottom" /> <circle id="b2" cx="120" cy="100" r="15" class="bottom" /> <g transform="translate(17,43)"> <circle id="b3" cx="240" cy="200" r="15" class="bottom" /> <circle id="b4" cx="360" cy="300" r="15" class="bottom" /> </g> <!-- <g transform="translate(10,20)"> --> <circle id="t1" cx="80" cy="30" r="15" class="top" /> <circle id="t2" cx="130" cy="30" r="15" class="top" /> <!-- <g transform="translate(10,20)"> --> <circle id="t3" cx="180" cy="30" r="15" class="top" /> <circle id="t4" cx="230" cy="30" r="15" class="top" /> <!-- </g> --> <!-- </g> --> </svg> 
Comments
Post a Comment