user1636946
user1636946

Reputation: 145

SVG transition to points when changed by javascript

I have a simple SVG that is just a square. I have attempted to modify it through CSS but I can't seem to get that to work. I'm successful with "fill:" changing the color but I can't seem to get "points:" to work. So I successfully modified it with javascript setAttribute but it'd be nice to have some kind of transition. I've tried "transition: .1s ease-out;" but that only works with the fill. Thanks.

	$("svg").click(function(){
		document.getElementById('pointz').setAttribute("points", "31.8,40.3 11.8,66.7 89.3,86.7 69.3,20.3");
	});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>Click the square below</p>
  <svg viewBox="0 0 194.6 185.1">
	<polygon id="pointz" fill="#000" points="21.8,30.3 21.8,76.7 79.3,76.7 79.3,30.3"/>
    </polygon>
  </svg>

Upvotes: 0

Views: 779

Answers (1)

Andrew Willems
Andrew Willems

Reputation: 12458

Here I'm showing a hack that allows you to individually animate each point of an svg polygon using the jQuery animate function. The principle would be essentially identical to animate the d attribute of an svg path (and thus curves can also be animated).

var anim = function() {
  $("svg").animate({top: "+=0"}, { // do a dummy animation on the svg root element
    duration: 1000,
    progress: function(xxx, p) { // 'xxx' not needed, 'p' proceeds from 0 to 1
      $("#shp").attr("points", oldPts.map(function(oldPt, i) { // update the shape
        return +oldPt + (newPts[i] - oldPt) * p; // calculate the changing coordinates
      })); // note coercions: oldPt: string --> number; return value: array --> string
    }
  });
};

var oldPts = $("#shp").attr("points").split(/, *| +/); // get original coords, split string into array
var newPts = [31.8, 40.3, 11.8, 66.7, 89.3, 86.7, 69.3, 20.3]; // the final desired coordinate values

$("button").click(function() {
  (this.firstClk = !this.firstClk) ? anim() : $("#shp").attr("points", oldPts);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div><button>Click to animate, then to reset, then repeat</button></div>
<svg><polygon id="shp" points="21.8,30.3 21.8,76.7 79.3,76.7 79.3,30.3"></polygon></svg>

I call it a hack because jQuery isn't really set up to do this. The work-around is that the jQuery animate function can call a progress function once during every animation cycle, and you can use that function to, well, do anything. Like morph an svg shape.

One of the reasons jQuery can't typically do this the "normal" way is that jQuery animation is typically meant to increment or decrement numerical values, like a left margin distance, or values that can be easily converted to numerical values, like a color. Svg shape coordinates are stored in complex strings (e.g. points="0,0 0,50 90,60 ...") that can't easily be incremented or decremented. The hack here simply leaves it up to you to take the string apart, change the values, and then re-assemble it. Complex, yes, but actually not that complex it turns out.

It is true that svg animation could be accomplished other ways as well. So why use jQuery? Two reasons. First, jQuery animation is just convenient. Second, and perhaps more importantly, this way you can coordinate the animation of svg element attributes, including their shapes, with other "legitimate" jQuery animations. Thus, you can now slide your new div down the top of the screen, change the color of your h1 heading, and make your svg frowny-face start to smile, all with one jQuery animation.

If you do coordinate the shape animation with another jQuery animation (e.g. color or opacity, etc.), fine. If you don't, however, this hack still requires that you "legitimately" animate something. If you only want to manipulate the shape of an svg element, the solution is to "animate" some other unimportant but animatable parameter from its original starting value to...its original starting value. In this example, the "legitimate" property that I animate is {top: "+=0"} of the svg root element. This basically says "animate the top value from its current value to its current value plus zero". Thus you don't really change it, but you do use this as the "hook" on which to hang the rest of the animation.

There's an additional bonus here. Another reason that jQuery isn't often used for animating svg images is that, while jQuery can animate css properties of svg elements, like color or opacity, it can't animate attributes of svg elements (emphasize: css properties vs svg attributes). Thus, while you can animate the left value of an HTML div, you can't animate the x attribute of an SVG rect, even though they are both referring to the distance from the left edge of the thing to some reference point. The type of hack I show here could allow any such svg attributes to be animated, not just css properties.

Note that the target of the "legitimate" jQuery animation and the shape you really want to animate don't have to be the same thing. In this example, I'm "legitimately" animating the svg root element (i.e. the actual <svg ...> thing), while I'm morphing the shape of an svg polygon embedded within that svg root element. Why did I choose the svg root element for the "legitimate" animation? It is the one element that is always guaranteed to be there when you want to implement this type of hack, so it just seemed reasonable to make this a target that could consistently be used. You can use whatever you want to though.

Enjoy.

Upvotes: 1

Related Questions