I am new to SVG. I have a svg image, i have applied a transform matrix for a <g> tag. Now I want to calculate <g> tag width and height. on page load i can calculate width and height using getBBox() or getBoundingClientRec() functions. But when i am resizing the window i am changing matrix values, Before updating matrix values how can i calculate <g> tag width and height.
-- getBBox() and getBoundingClientRec() returns width and height based on matrix values.
example:
On page load:
<g id="xyz" transform="matrix(1,0,0,1,10,10)"></g>
When resizing the window i am calling a method which calculates matrix values.
Note: Matrix values are calculated not yet updated
I need to center the image to the browser window on resizing. I am subtracting window width and image width divided by 2 and i am applying that value for matrix.e i., "x" value.
example: new matrix values are [1.2,0,0,1.2,x,10]
For centering map i need to know width and height of <g> by using calculated matrix values i.e., [1.2,0,0,1.2,x,10]
Wrap the transformed group with another <g> and call getBBox() on that.
However why not describe the original problem you are trying to achieve? Since you are new to SVG, there is a chance you are approaching your problem the wrong way. Maybe we can suggest a better solution than trying to manipulate matrixes yourself.
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
My image changes size and I want to know how to keep a label in the same spot on the image no matter the size. I want to keep the label inside of a speech bubble, is there a way to put constraints on it to do this, or is there another way? I'm working in Xcode.
Instead of giving constant position values, derive the required position using height and width of the image using mathematical calculation.
Example:
Let's say the image size is 400x400 and you want a label at bottom right
You can derive it as
X=width-width*0.75
Y=height-height*0.8
Now even if the height and width varies, the label will be in same location
I want to show another image on hover and due to the layout of the homepage I need to use sprite to do it. I've got it working fine, but I'm using fixed width and height for the container.
You can see my JSFiddle here:
http://jsfiddle.net/mckeene/fhk0byqt/4/
The problem arises when I want to make it responsive. I can use max-width: 100%, but what about the height?
OK, this solution is based on the known fact that some properties like padding-top and margin-top, when given values in percentage, are calculated based on the width of an element rather than its height.
Making an element “as high” as required by the known dimensions of a responsive image can be done by using padding-top – I used a value of 66.54% here based on your image’s dimensions (half the image height divided by the width, times 100) to span the container element up to the required height.
Now normally to display the upper half of an image first, and then the second half on hover, I would use absolute positioning – but we can’t use top here, since a value in percentage for that property would be based on the height. But luckily, as already mentioned, margin-top is one of those properties where percentage is calculated based on the width – so we can use margin-top: -66.54% here to “pull” the image up over its container’s padding first to show its upper half, and then double that (margin-top: -133.1%) to pull it up even further on hover, to show its lower half.
Is there any way of labeling plots with images. For example, when I use the following:
plot(Y(:,1),Y(:,2),'o','LineWidth',2);
gname(names)
I can label each dot in a plot with a name. Is there any way to insert images instead of names?
It is possible, but not as convenient as gname by far. You can use the low-level version of image to insert images in your plot at arbitrary positions. Here's a simple example which puts the "Mandrill" image that comes with Matlab with its upper left corner pixel at the position (pi/2, 0):
% example plot
x = linspace(0, 2*pi, 100);
plot(x, cos(x))
% insert image
load mandrill
colormap(map)
image('CData', X, 'XData', [pi/2, pi/2 + 0.5], 'YData', [0, -0.3])
The result looks like this:
Problems with this approach:
There is no interactive point-and-click facility, you have to explicitly insert and position the image labels programmatically, or program such a point-and-click facility yourself. ginput might help doing so.
A figure window can only have one associated color map. That means if you have different images, they either all have to use the same colormap or have to be truecolor images.
Not just the position, but also the display size of the image has to be specified in the call to image, and both are by default specified with respect to the plot's coordinate system. This makes it hard to achieve the correct aspect ratio. You can switch (temporarily) to absolute units using the axes property 'Units' , but then you have to figure out the correct position in e.g. absolute millimeters or inches. Moreover, images are usually indexed with vertical coordinates increasing from top to bottom, while plots usually have vertical coordinates increasing from bottom to top. This is the reason for the negative value -0.3 in the 'YData' property above.
Alternatively, you can insert images each in their own little axes sitting on top of the plot's axes, which makes it easy to get the right orientation and aspect ratio using axis image. You'll still have the problem though to figure out the correct position for the axes.
What happens with the code below is that image width is scaled to 100% as expected and the height also scales as expected keeping the aspect ratio correct. Issue is that there is a margin at the bottom and that seems to be the height of the original contentHeight of the image. How can I get rid of that?
I am using percentages so that it scales when device orientation changes.
backdrop.source = "http://cf2.imgobject.com/t/p/" + "w342" + data.backdrop;
backdrop.scaleMode = "letterbox";
backdrop.horizontalAlign = "left";
backdrop.verticalAlign = "top";
backdrop.smooth = true;
backdrop.percentWidth = 100;
The answer to your question is don't use the letterbox setting. That is going to preserve the aspect ratio and make the black area, hence the name letterbox :)
Try setting scaleMode to zoom instead. As the documentation states, zoom will result in one axis being clipped. This should scale the image, preserve the aspect ratio, but clip some edges of the image to avoid having the black area.
Other solutions to this problem are:
modify the original image outside of Flash
use a mask to achieve similar results that the zoom setting will provide. In this approach you make the image bigger, but then apply a square mask to the image. The mask reveals only the square portion ... clipping what is outside the mask.
(undesirable in most cases) use the scaleMode setting of strectch (and specify both width/height) so that the area is filled, this will not preserve the aspect ratio
PS: There is no way to avoid the black area if the image's aspect ratio is not square. Even with HTML/CSS. This is just math/geometry. The same thing happens in HTML -- the image is either stretched, clipped, or will not fill both dimensions.
[Edit]
PPS: One other idea, if you know the original aspect ratio of the image, is to calculate a new width that will be closest to the desired width, but naturally preserves the width to height aspect ratio.
For example, the width:height ratio is 4:3. Your desired width is 500 pixels. Using cross products you get this:
4 500
- = -
3 x
Using cross products you get the equation:
4x = 3*500
Now solve for x:
x = 3*500/4 = 375
Therefore, if the original aspect ratio is 4:3, you can set a width of 500 and a height of 375 to scale the image and not have any black areas. You can even write code that dynamically calculates the aspect ratio, and applies this logic to scale something nicely. The point is that you have to the respect aspect ratio when scaling the image to avoid the "black" areas.