I am drawing an SVG group with a circle and a piece of text. As part of an animation, I scale the group up and down. When the group is scaled relatively small, everything looks fine. However, once it gets very small, you start getting some nasty artifacts.
An example is below. Notice the circles on the right are fine, but when they get really small, you get the text artifacts on the left.
Below is some exemplary SVG code that causes the artifacts. This only seems to happen in firefox.
<g transform="translate(77 256) scale(0.00469784)">
<circle stroke="rgb(180, 0, 100)" r="30" cx="0" cy="0"/>
<text text-anchor="middle" dominant-baseline="middle" x="0" y="0" font-size="15">Foo Bar</text>
</g>
Related
I have patterns that each have a single image in them. I need the images to scale to the full width or height of their containers, which are paths, while retaining their proportions. Essentially, they need to behave like an html image might if you set min-width:100%; min-height:100%;
I have not used svgs much before and do not know which attributes to change to get this type of behaviour. I've been trying all sorts of combinations of viewBox, preserveAspectRatio, patternUnits and more, but I cannot seem to get what I want.
To get this to work, you need to understand how objectBoundingBox units work in SVG, and also how preserveAspectRatio works.
Object Bounding Box Units
The size and content of gradients, patterns and a number of other SVG features can be specified in terms of the size of the object (path, rect, circle) which is being painted by specifying objectBoundingBox as the unit. The opposite is always userSpaceOnUse, which uses the coordinate system that the shape is drawn in.
Object bounding box units are usually the default for declaring the size and position of the graphical fill element; you change this by setting the patternUnits property on the <pattern> element. However, user space units are usually the default for any units used in the content graphics; to change this you set the patternContentUnits property.
So first step: To create a pattern that completely fills the shape, you need to:
Declare the height and width of the pattern as 100% (or 1); these will by default be interpreted relative to the bounding box).
Declare patternContentUnits="objectBoundingBox".
Size the content (your image) so that it has a height and width of 1.
You cannot use 100% as a synonym for 1 object bounding box unit within the pattern content itself (i.e., the image dimensions); percentages are interpreted relative to the SVG size, not the objectBoundingBox.*
I should mention, since you say that your shapes are <path> elements, that the object bounding box is the smallest rectangle that is perpendicular to the coordinate system in which the path is drawn and contains all the path's points. It doesn't include stroke. For example a straight horizontal line has a zero-height bounding box; an angled line has a bounding box rectangle such that the line is the diagonal of the box. If your paths are awkwardly shaped and/or not very well aligned with the coordinate system, the bounding box can be much larger than the path.
Preserving Aspect Ratio
The preserveAspectRatio property applies to images and to any element that can have a viewBox property: the parent <svg>, nested <svg>, <symbol>, <marker> and <pattern>. For images, the aspect ratio is calculated from the image's inherent width:height ratio, for all the others it is calculated from the width:height numbers in the viewBox attribute.
For either type of element, if you declare a height or width for the element that doesn't match the aspect ratio, the preserveAspectRatio property determines whether the content will be stretched to fit (none), sized to fit one dimension and cropped in the other (slice) or shrunk to fit both dimensions with extra space (meet); for meet and slice options you also specify how to align the content in the space.
However, it is important to note that the aspect ratio of the space available is calculated in the current coordinate system, not in screen pixels. So if a higher-level viewBox or transformation has altered the aspect ratio, things can still be distorted even with a preserveAspectRatio property set on the current element.
The other thing to know is that the default value is usually not none. For both <image> and <pattern> elements, the default is xMidYMid meet -- i.e., shrink to fit and center. Of course, this default only has an impact on pattern elements if the pattern element has a viewBox property (otherwise, it's assumed to have no aspect ratio to preserve).
What value you want to use for preserveAspectRatio will depend on the image and design:
Should the image be stretched to fit the shape preserveAspectRatio="none"?
Should the image aspect ratio be maintained, but sized to completely fit in or cover the shape?
In the first case (stretch), you don't need to do anything to the <pattern> element (no viewBox means no aspect ratio control), but you do need to specifically turn off aspect ratio control on the image.
In contrast, if you want to avoid distortion of the image you will need to:
Set viewBox and preserveAspectRatio properties on the <pattern> element;
Set the preserveAspectRatio property on the <image> if you want something different than the default.
Working Example
This fiddle shows three ways of getting a pattern image to fill a shape.
The top row has aspect control turned off.
<!-- pattern1 - no aspect ratio control -->
<pattern id="pattern1" height="100%" width="100%"
patternContentUnits="objectBoundingBox">
<image height="1" width="1" preserveAspectRatio="none"
xlink:href="/*url*/" />
</pattern>
The middle row has aspect ratio control on the <image> element so the picture is cropped to fit the pattern, but the picture is still distorted when the pattern is drawn in the rectangle because the objectBoundingBox units that define the coordinate system are different for height versus width. (The image in the circle isn't distorted because the circle's bounding box is a square.)
<!-- pattern2 - aspect ratio control on the image only -->
<pattern id="pattern2" height="100%" width="100%"
patternContentUnits="objectBoundingBox">
<image height="1" width="1" preserveAspectRatio="xMidYMid slice"
xlink:href="/*url*/" />
</pattern>
The bottom row has preserveAspectRatio set on both the image and the pattern (and also a viewBox set on the pattern). The image gets cropped but not stretched.
<!-- pattern3 - aspect ratio control on both image and pattern -->
<pattern id="pattern3" height="100%" width="100%"
patternContentUnits="objectBoundingBox"
viewBox="0 0 1 1" preserveAspectRatio="xMidYMid slice">
<image height="1" width="1" preserveAspectRatio="xMidYMid slice"
xlink:href="/*url*/" />
</pattern>
Source image by Stefan Krause, from Wikimedia Commons. The original aspect ratio is 4:6 portrait mode.
* Correction on 2015-04-03
I am trying to draw an irregular, petal shape for a D3 animation, but I cannot find any understandable documentation for odd shapes in SVG-format. How might I go about creating such a shape?
It's something like this:
<svg>
<path fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" d="M21,34.328C21,15.693,32.477,0.515,50.124,0.515
C67.77,0.515,79,16.928,79,34.328c0,17.399-29,65.157-29,65.157S21,52.962,21,34.328z"/>
</svg>
And use as definition (defs) for future reference. Look here
and here: Is it possible to import svg shapes in d3.js?
If you want do it by hand, here's the Paths Tutorial: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
Here the Codepen
I am working with SVG in HTML to define specific shapes using the polyline tool. I am looking to animate the appearance of a specific shape into a different shape at the touch of a button and over a few seconds.
I have been looking at using the animate tool to change the polylines points attribute, but have so far been unable to find a solution or something that works perfectly.
Is it possible to do this? If not, is there a viable alternative?
You can supply polylines (and even paths with bezier curves etc) to tween, as long as they have the same number of points, because SVG just moves each (control) point independently. If the shapes don't have the same number of control points, you could just coincide some, but I guess graphical editors will "correct" this.
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="5cm" height="5cm" viewBox="0 0 1000 1000"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<polyline stroke="red" stroke-width="3" fill="none">
<animate attributeName="points" dur="5s" repeatCount="indefinite"
from="100,100 900,100 900,900 100,900 100,100"
to="200,200 800,500 800,500 200,800 200,200"
/>
</polyline>
</svg>
I am aware of SVG's animation capabilities, but is there a way to easily combine pictures into animations, like in animated GIFs? For example, cycling between a circle,
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red"/>
</svg>
and a rectangle,
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect width="300" height="100"
style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,0)"/>
</svg>
Well you could possibly fade the alpha values in and out if that's the effect you are after. i.e fade out the circle and fade in the rect at the same time and vice-versa.
Having the actual shape transform from one to another would probably need to be done by you using JavaScript.
If your SVG will be viewed in a web browser, you can animate it by manipulating the nodes with JavaScript. My "web site" is an example of this.
If you would use path elements instead of the basic rectangles and circle elements it would be possible to use path morphing to morph one shape into another using SVG animations.
There is pretty good example of this at carto.net.
Depending on your use case there might be a few drawbacks with this solution. The "pictures" that you put into the animation not only needs to be path elements, there are also some more constraints such as that the path elements need to have the same number of vertices and they need to be of the same type.
When I use a rotate transform, the coordinate of the current element will be changed. See this example.
Is there someone that knows how to control the coordinates or give me some suggestions about making a rotated element animate along a path (some browsers don't support the animateMotion tag using javascript).
I find it easiest to rotate elements around the origin, and then use groups to add additional transformations.
For example, this rotates something 45 degrees, moves to 50 pixels down and left, then animates it from (50, 50) to (100, 150) over 10 seconds:
<g>
<animateTransform attributeName="transform"
attributeType="XML"
type="translate"
from="50 50" to="100 150"
dur="10s" fill="freeze"/>
<g transform="translate(50,50) rotate(45)">
Your elements here
</g>
</g>
You can build up quite complex animations this way:
http://www.petercollingridge.co.uk/blog/svg-seedling-animation
If you have a more specific description of what you want I might be able to help.