SVG 缩放不移动位置

Posted

技术标签:

【中文标题】SVG 缩放不移动位置【英文标题】:SVG Scale without moving location 【发布时间】:2014-08-02 03:19:24 【问题描述】:

我要做的很简单:当兄弟元素使用 vanilla js 悬停时,将一些 SVG 点从 scale(0) 缩放到 scale(1)。他们是the demo中的红人

这是基本的 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>

问题是 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)";
        
    

我尝试同时使用 setAttribute 和 CSS 的转换(我无法让 setAttribute 进行转换,大概是因为它不能被 CSS 动画化)但两者都无法使用。我只在 Chrome 中测试过

有人知道如何在不移动的情况下缩放红点吗?

Here's the demo如果你错过了,请再次关注

编辑

我根据 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 + ")";

【问题讨论】:

不清楚你想要发生什么。你想让每个红点在鼠标悬停时变大吗? @BigBadaboom 是的,我想在鼠标悬停时缩放红点而不让它们从任何地方移入——只要变大就行了 【参考方案1】:

不涉及一堆几何图形的更简单方法是将要缩放和转换的项目放入父组 ('g')。

然后,将平移应用到父组,将缩放应用到元素本身。

var trasnstr = x + ',' + y;
var scalestr = scaleX + ',' + scaleY;

parentElement.setAttribute('transform', 'translate(' + trasnstr + ')');
element.setAttribute('transform', 'scale(' + scalestr + ')');

【讨论】:

不知道你为什么投了反对票,因为这种方法对我有用。但是,我正在使用 xml 代码并将路径包装到 ... 中,而不是将转换命令插入到路径本身中。搬走了。按预期缩放。谢谢!【参考方案2】:

更新后可与支持 transform-b​​ox 的现代浏览器一起使用 以前,这种方法仅适用于 Chrome。但是规范更改了 transform-origin 的工作方式,现在添加 transform-box 意味着它可以在更多浏览器(目前是 Chrome、FF 和 Opera)中使用。

其实不用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

【讨论】:

请注意,在 SVG(而不是 CSS)中使用时,transform-origin 在某些浏览器(尤其是移动设备)上似乎效果不佳。 Chrome 修复了一个“错误”,这意味着这不再起作用。我现在更新了这个例子,让它现在可以再次工作,并且也可以在其他浏览器中工作。 我现在正在做的一个项目也遇到了类似的问题 - 使用 transform-box: fill-box 让我做的事情变得更容易了! 太棒了,谢谢!不幸的是,它还没有被完全支持:w3cschool.cn/doc_css/css-transform-box.html 该页面上的信息已过期。我建议你使用 MDN 作为更可靠的资源:@​​987654323@【参考方案3】:

如果我没记错的话,你想沿着它们的中心缩放点,点保持它们当前的位置并且变得更大。

如果你愿意,下面的代码会帮助你

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+')');

所以我做了什么,我首先翻译点然后缩放它。我使用以下公式,如 Transforming Coordinate system

translate(-centerX*(factor-1), -centerY*(factor-1))
scale(factor)

【讨论】:

感谢您的帮助!使用该数学并使用 CSS 转换来保留转换。 Working demo(仅限 webkit)

以上是关于SVG 缩放不移动位置的主要内容,如果未能解决你的问题,请参考以下文章

【缩放】实现svg以鼠标为焦点缩放

如何通过在 svg 中保持固定位置来缩放元素

如何使用 SVG 进行缩放和平移

内联 svg 缩放 - 自动宽度

理解SVG的缩放 偏移的计算公式

前端如何实现div在固定角度缩放