javascript SVG 缩放而不移动位置
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24173560/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
SVG Scale without moving location
提问by Zach Saucier
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
我想要做的很简单:当使用 vanilla js 悬停同级元素时,将一些 SVG 点从scale(0)
到缩放scale(1)
。他们是演示中的红色
Here's the basic SVG setup
这是基本的 SVG 设置
<?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 issueis 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.
问题在于 SVG 根据原点位置而不是当前位置进行缩放,因此当应用变换时,除了缩放元素之外,它还会移动元素。我试图通过按BBox()
偏移量进行转换,缩放,然后向后转换来解决这种情况,但这似乎只是有所帮助,并不能完全解决问题。
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
我尝试同时使用setAttribute
和 CSS 的转换(我无法setAttribute
进行转换,大概是因为它无法通过 CSS 设置动画),但两者都无法实现。我只在 Chrome 中测试过
Anyone have an idea how I can scale, while not moving, red dots?
任何人都知道如何在不移动的情况下缩放红点?
Here's the demoagain 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
我根据 RashFlash 的答案制作了一个函数,使其使用起来非常简单,并且还考虑了偏移量和不同的变换原点
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 + ")";
}
回答by Paul LeBeau
Updated to work with modern browsers that support transform-boxPreviously, 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).
更新后可与支持 transform-box 的现代浏览器一起使用以前,此方法仅适用于 Chrome。但是规范的工作方式发生了变化
transform-origin
,添加transform-box
now 意味着这可以在更多浏览器(目前是 Chrome、FF 和 Opera)中使用。
You can actually achieve this effect without JS.
其实不用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;
}
回答by RashFlash
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)
回答by David Johnson
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').
一种不涉及一堆几何体的更简单的方法是将要缩放的项目放入父组 ('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 + ')');