Thursday, May 23, 2024
 Popular · Latest · Hot · Upcoming
32
rated 0 times [  38] [ 6]  / answers: 1 / hits: 17818  / 10 Years ago, wed, june 11, 2014, 12:00:00

What I'm trying to do is simple: scale some SVG dots from scale(0) to scale(1) when a sibling element is hovered using vanilla js. They are the red ones in the demo



Here's the basic SVG setup



<?xml version=1.0 encoding=utf-8 ?>
<!DOCTYPE svg PUBLIC -//W3C//DTD SVG 1.1//EN http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd>
<svg version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink
x=0px y=0px viewBox=0 0 720 576 style=enable-background:new 0 0 720 576; xml:space=preserve>
<style type=text/css>
.st3 {
fill:red;
}
* {
-webkit-transition:.3s;
transition:.3s;
}
</style>
<g id=Layer_4>
<!-- Shield -->
<path class=st8 d=M601,304.7c-32.4-15.4-68.6-24-106.8-24c-40.4,0-78.5,9.6-112.3,26.6c4.9,79.7,41.9,146.7,109.5,187.6
C559.8,454.1,597,385.6,601,304.7z />
<path class=st9 d=M420.1,328.7c2.1-4.7,32.5-23.9,72.5-23.9c39.9,0,73.1,20,75.5,24.3c2.4,4.3,5.7,40-12.7,74.6
c-19.7,36.9-53.5,50.1-61.8,50.4c-6.4,0.2-41.8-14.3-62.5-51.6C411.5,367.4,418,333.4,420.1,328.7z />
<circle class=st10 cx=494.9 cy=373.3 r=35.5 />
</g>
<g id=Layer_8>
<!-- Dots on shield -->
<circle class=st3 cx=578.8 cy=316.2 r=4.6 />
<circle class=st3 cx=543.4 cy=346.2 r=4.6 />
<circle class=st3 cx=505 cy=375.5 r=4.6 />
</g>
</svg>


The issue is that SVG scales based on the origin location, not the current location, thus when a transform is applied it moves the element in addition to scaling it. I am attempting to fix this situation by translating by the BBox() offset, scaling, then translating back but that only seemed to help and not entirely fix the issue.



var shield = document.getElementById(Layer_4),
dots = document.querySelectorAll(#Layer_8 .st3);

toggleTransform(false);

shield.onmouseover = function () { toggleTransform(true); }
shield.onmouseout = function () { toggleTransform(false); }

function toggleTransform(bool) {
if (!bool) {
for (var i = 0; i < dots.length; i++) {
var box = dots[i].getBBox(),
cx = box.x + box.width / 10,
cy = box.y + box.height / 10;
//dots[i].setAttribute(transform, translate( + cx + + cy + ) scale(0) translate( + cx + + cy + ));
dots[i].style.WebkitTransform = translate( + cx + px, + cy + px) scale(0) translate( + -cx + px, + -cy + px);
}
} else {
for (var i = 0; i < dots.length; i++) {
var box = dots[i].getBBox(),
cx = box.x + box.width / 2,
cy = box.y + box.height / 2;
//dots[i].setAttribute(transform, translate(0 0) scale(1) translate(0 0));
dots[i].style.WebkitTransform = translate(0, 0) scale(1) translate(0, 0);
}
}
}


I tried using both setAttribute and CSS's transform (I couldn't get setAttribute to transition, presumably because it's not animatable by CSS) but couldn't get it with either. I've only been testing in Chrome



Anyone have an idea how I can scale, while not moving, red dots?



Here's the demo again if you missed it



Edit



I made a function based on RashFlash's answer to make it quite simple to use and also takes into account offsets and different transform origins



function scaleMe(elem, scaleX, scaleY, newOffsetX, newOffsetY, originX, originY) {
newOffsetX = null ? 0 : newOffsetX;
newOffsetY = null ? 0 : newOffsetY;
originX = null ? center : originX;
originY = null ? center : originY;

var bbox = elem.getBBox(),
cx = bbox.x + (bbox.width / 2),
cy = bbox.y + (bbox.height / 2),
tx = -cx * (scaleX - 1) + newOffsetX,
ty = -cy * (scaleY - 1) + newOffsetY;

if(originX === left || originX === right) {
tx = newOffsetX;
}
if(originY === top || originY === bottom) {
ty = newOffsetY;
}

var scalestr = scaleX + ',' + scaleY,
translatestr = tx + 'px,' + ty + 'px';

elem.style.WebkitTransformOrigin = originX + + originY;
elem.style.MozTransformOrigin = originX + + originY;
elem.style.msTransformOrigin = originX + + originY;
elem.style.transformOrigin = originX + + originY;

elem.style.WebkitTransform = translate( + translatestr + ) scale( + scalestr + );
elem.style.MozTransform = translate( + translatestr + ) scale( + scalestr + );
elem.style.msTransform = translate( + translatestr + ) scale( + scalestr + );
elem.style.transform = translate( + translatestr + ) scale( + scalestr + );
}

More From » svg

 Answers
41

Updated to work with modern browsers that support transform-box
Previously, this approach worked only in Chrome. But spec changes to how transform-origin works, and the addition of transform-box now means that this works in more browsers (currently Chrome, FF, and Opera).




You can actually achieve this effect without JS.



.st3 {
fill: red;
-webkit-transform: scale(1);
-webkit-transform-origin: 50% 50%;
-webkit-transition:.3s;
transform: scale(1);
transform-origin: 50% 50%;
transition:.3s;
transform-box: fill-box;
}

#Layer_4:hover + g .st3 {
-webkit-transform: scale(2);
-webkit-transform-origin: 50% 50%;
-webkit-transition:.3s;
transform: scale(2);
transform-origin: 50% 50%;
transition:.3s;
}


Demo here


[#70609] Tuesday, June 10, 2014, 10 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
nadiatristinl

Total Points: 151
Total Questions: 116
Total Answers: 108

Location: Japan
Member since Tue, Jul 26, 2022
2 Years ago
;