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 + ")";
}
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 oftransform-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;
}
if i am not wrong, you want to scale the dots along their center, dots remain their current position and just gets bigger.
if this you want, then following code will help you
var bbox=elementNode.getBBox();
var cx=bbox.x+(bbox.width/2),
cy=bbox.y+(bbox.height/2); // finding center of element
var scalex=1.5, scaley=1.5; // your desired scale
var saclestr=scalex+','+scaley;
var tx=-cx*(scalex-1);
var ty=-cy*(scaley-1);
var translatestr=tx+','+ty;
elementNode.setAttribute('transform','translate('+translatestr+') scale('+saclestr+')');
So what i did, i first translate the dot and than scale it. i use following formula as described in Transforming Coordinate system
translate(-centerX*(factor-1), -centerY*(factor-1))
scale(factor)
An easier way to do this that does not involve a bunch of geometry is to put the item to be scaled and translated into a parent group ('g').
Then, you apply the translation to the parent group and the scale to the element itself.
var trasnstr = x + ',' + y;
var scalestr = scaleX + ',' + scaleY;
parentElement.setAttribute('transform', 'translate(' + trasnstr + ')');
element.setAttribute('transform', 'scale(' + scalestr + ')');
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.