web-dev-qa-db-ja.com

場所を移動せずにSVGスケール

私がやろうとしていることは単純です。兄弟要素がVanillajsを使用してホバーされたときに、いくつかのSVGドットをscale(0)からscale(1)にスケーリングします。それらは デモ の赤いものです

これが基本的な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の変換の両方を使用しようとしましたが(おそらくCSSでアニメーション化できないため、setAttributeを遷移させることができませんでした)、どちらでも取得できませんでした。 Chromeでのみテストしています

赤い点を動かさずに拡大縮小する方法を知っている人はいますか?

これがデモです 見逃した場合は、もう一度

編集

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 + ")";
}
_
17
Zach Saucier

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;
}

デモはこちら

21
Paul LeBeau

私が間違っていない場合は、ドットを中心に沿って拡大縮小します。ドットは現在の位置のままで、大きくなります。

これが必要な場合は、次のコードが役立ちます

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

だから私がしたことは、最初にドットを平行移動し、それをスケーリングすることです。 座標系の変換 で説明されているように、次の式を使用します

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