I have a problem with SVG coordinates. I'm sure it's not a bug and it's probably explained somewhere in the documentation and therefore I don't question it. But for my needs I can't find other way to resolve the problem. Now to the point.
TL;DR
In SVG, this
<mask id="myMask" x="0" y="0">
...
</mask>
<use xlink:href="..." x="100" y="100" mask="#myMask">
changes the <use> x (or y) position together with the <mask> x (or y) position.
I want to keep the <mask> in its place.
Longer explanation
Background
In my web application I have a SVG path. I use it in multiple places so I decided to use the <use> element. Now, in some places I wanted to hide part of the path so I use <mask> element with a rectangle in it. There's also a situation where there is a stack of the same path placed one below another. To achieve this I used the y attribute for one of the <use> elements.
The question
Unfortunately, when I change the y coordinate of the <use> the <mask> attached to it also changes its y coordinate. The situation doesn't occur on <path> element nor other elements.
Below is an example (for simplicity I used <rect> elements).
Run the snippet to see four squares placed next to each other (two red and two yellow). The grey overlay represents the mask's boundaries. Squares will rather look like rectangles because they're cut in the half of their height by the <mask>. As you can see in the code, red squares are imported by <use> element and yellow ones are placed directly with <rect> element. Also the second and the fourth square are both moved 500 units downwards. My problem is clearly represented by the second square. It should be cut exactly like the fourth square but I need to do it the <use> way.
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="100px" viewBox="0 0 4000 1000">
<defs>
<rect id="svgRect" width="1000" height="1000"></rect>
<mask id="svgIconMask10">
<rect x="0" y="0" width="4000" fill="#ffffff" height="500"></rect>
</mask>
</defs>
<!-- Rectangle 1 -->
<use xlink:href="#svgRect" y="0" fill="#E5584C" mask="url(#svgIconMask10)"></use>
<!-- Rectangle 2 -->
<use xlink:href="#svgRect" x="1000" y="300" fill="#E5584C" style="mask: url(#svgIconMask10);"></use>
<!-- Rectangle 3 -->
<rect x="2000" y="0" width="1000" height="1000" fill="#E5D24C" style="mask: url(#svgIconMask10);"></rect>
<!-- Rectangle 4 -->
<rect x="3000" y="300" width="1000" height="1000" fill="#E5D24C" style="mask: url(#svgIconMask10);"></rect>
<!-- Mask area boundaries -->
<rect fill="transparent" stroke="#000" stroke-width="6px" x="0" y="0" width="4000" height="500" />
</svg>
You can apply the mask to a <g /> element and have the <use /> positioned independently:
<g style="mask: url(#svgIconMask10);">
<use xlink:href="#svgRect" x="1000" y="300" fill="#E5584C"></use>
</g>
http://jsfiddle.net/yb1q8dwh/
Related
i have this hexagon svg:
<svg width="100%" height="125">
<g transform="translate(-33 -35)">
<g transform="translate(33 35)">
<polygon points="50,0 100,28.87 100,86.61 50,115.48 0,86.61 0,28.87" fill="#28282B"/>
</g>
<g transform="translate(83.5 68.305)">
<polygon points="0,0 -50,28.87 0,57.74 50,28.87" fill="#28282B" />
</g>
</g>
</svg>
I want to cut this in half vertically so that it looks like this
How can i achieve this?
The number pairs in the polygon's points attribute are x/y coordinates.
You could just lop off the last two points to get the right half of the hexagon.
In the snippet below the first path is the original hex. The second is the right half--same as the first but with the last two coordinates removed.
(I've also removed the two transform groups that canceled each other out, and the other poly that didn't appear to be contributing anything useful.)
<svg width="100%" height="125">
<!-- original hexagon -->
<polygon points="50,0 100,28.87 100,86.61 50,115.48 0,86.61 0,28.87" fill="#28282B"/>
<!-- right half -->
<polygon points="50,0 100,28.87 100,86.61 50,115.48" fill="#9999ff"/>
</svg>
Update: If you want the halves as separate SVGs make two copies and delete the relevant points from each:
<!-- left half -->
<svg viewBox="0 0 50 116" width="50" height="116">
<polygon points="50,0 50,115.48 0,86.61 0,28.87" fill="skyblue"/>
</svg>
<!-- right half -->
<svg viewBox="50 0 50 116" width="50" height="116">
<polygon points="50,0 100,28.87 100,86.61 50,115.48" fill="steelblue"/>
</svg>
I also tightened up the canvas with the viewBox attribute on these so they’d be easier to line up next to each other, without all the dead space where the other half used to be.
I have an SVG image that is being created and enhanced programatically. After creating it, it is drawn on a Canvas. However, the lines do not seem to have the same width, despite having the same value for stroke-width and the attribute shape-rendering set to crispEdges.
The coordinates are calculated in JavaScript (hence the weird numbers). However, some lines seem to be twice as thick as others (see example below). I don't understand why this happens or how I can fix it.
My best guess is that the calculations are not precise enough and the angle is not actually a perfect 45°, resulting in a thicker line. But when I calculate the slope by hand, it's 45°.
Setting shape-rendering to auto theoretically works, but the circumstances require the lines to be not smooth.
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="80" shape-rendering="crispEdges" stroke-linecap="square" stroke="rgb(0,0,0)" stroke-width="1">
<rect id="background" x="0" y="0" width="3201" height="1677" fill="rgb(255,255,255)" stroke-width="0"/>
<line x1="0.5" y1="71.5" x2="71.2106781186546" y2="0.7893218813452"/>
<line x1="71.2106781186546" y1="0.7893218813452" x2="141.9213562373093" y2="71.5"/>
<line x1="141.9213562373093" y1="71.5" x2="212.632034355964" y2="0.7893218813452"/>
<line x1="212.632034355964" y1="0.7893218813452" x2="283.3427124746186" y2="71.5"/>
</svg>
The purpose of the crispEdges attribute is to accentuate the contrast between edges in your picture, not to ensure that strokes are drawn with the same width.
You probably want to use geometricPrecision instead. However, if it's important to use crisp edges for some reason, try drawing your lines with the same gradients and with their start/end points aligned to the pixel grid (ideally, offset by 0.5 pixels).
Here's your SVG, with minor modifications to ensure the stroke width appears consistent:
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="80" shape-rendering="crispEdges" stroke-linecap="square" stroke="rgb(0,0,0)" stroke-width="1">
<rect id="background" x="0" y="0" width="300" height="80" fill="rgb(255,255,255)" stroke-width="0"/>
<line x1="0.5" y1="71.5" x2="71.5" y2="0.5"/>
<line x1="71.5" y1="0.5" x2="142.5" y2="71.5"/>
<line x1="142.5" y1="71.5" x2="213.5" y2="0.5"/>
<line x1="213.5" y1="0.5" x2="284.5" y2="71.5"/>
</svg>
I have a <text> and a <rect> element nested in a svg element. I want the <rect> element to begin right after the <text> ends. But it looks like I have to position them using x and y.
Is there any way to put them next to each other like regular HTML elements ?
<svg>
<text>1200</text>
<rect width='120' height='12'></rect>
</svg>
I only seem to be able to get it with this:
<svg height="30" width="100">
<text x="0" y="15" fill="red">SVG TEXT!</text>
</svg>
<svg height="30" width="100">
<rect width='120' height='12'></rect>
</svg>
See the Pen 'SVG Text and Box Test' by James Almeida here: http://codepen.io/jimmyplaysdrums/pen/ZYdEyw/
In HTML5 Canvas you can draw an image as a whole, or draw only a piece of it, or draw only a piece of it to an arbitrary rectangle (which may scale it).
Here's an example of all three:
Here's the code used to draw those three examples:
ctx.drawImage(img, 0, 0);
ctx.drawImage(img,
// source rect
50, 50, 70, 70,
// destination rect
0, 200, 70, 70
);
ctx.drawImage(img,
// source rect
50, 50, 70, 70,
// destination rect
0, 270, 30, 30
);
This is also relatively easy to do in CSS.
My question is, for a given image, how can you achieve the same effects using SVG <image> elements?
How do I, for instance, make an Image that occupies 50x50 pixels, that shows a portion of the referenced href, as in the first crop?
One could use a clipping path to crop part of the image, but then you (seemingly) cannot use a clipping path of a larger image while defining the width and height of the <image> element to be small.
Here's a fiddle with the above code plus a sample SVG element:
http://jsfiddle.net/wcjVd/
You don't need a clipPath at all, you can use the viewBox to do what you want
<svg width="70px" height="70px" viewBox="50 50 70 70">
<image x="0" y="0" width="200" height="200"
xlink:href="http://placekitten.com/200/200" clip-path="url(#myClip)">
</image>
</svg>
The value of the viewBox attribute is a list of four numbers <min-x>, <min-y>, <width> and <height>, separated by whitespace and/or a comma, which specify a rectangle in user space which should be mapped to the bounds of the viewport established by the given element, taking into account attribute preserveAspectRatio.
Demo Here
Glad I can help you here because you helped me with a Canvas project a few months ago!
Edit
You said that you needed to transform them also, so after an hour I came up with these couple options:
If you can, transform the original image and do the same effect. Demo here If you are wanting the cropped image at the origin (0,0) then the code would look like
<defs>
<clipPath id="myClip">
<rect x="0" y="0" width="70" height="70"/>
</clipPath>
</defs>
<image x="-50" y="-50" width="200" height="200" clip-path="url(#myClip)"
xlink:href="http://placekitten.com/200/200"></image>
OR, more satisfactorily, you could do it using a use
<defs> <!-- Your original clip -->
<clipPath id="myClip">
<rect x="50" y="50" width="70" height="70"/>
</clipPath>
</defs>
<image id="myImage" x="0" y="0" width="200" height="200"
xlink:href="http://placekitten.com/200/200" clip-path="url(#myClip)" />
<!-- Hide the original (I made a layer of white) -->
<!-- This is the part that you could probably find a better way of doing -->
<rect x="0" y="0" width="200" height="200" style="fill:white" />
<!-- Create what you want how you want where you want it -->
<use x="-50" y="-50" width="200" height="200" xlink:href="#myImage" transform="scale(1.3)"/>
</svg>
Demo for that approach here
In W3C's spec, it says:
The value of the ‘transform’ attribute is a <transform-list>, which is defined as a list of transform definitions, which are applied in the order provided.
...
If a list of transforms is provided, then the net effect is as if each transform had been specified separately in the order provided
When I tried out the follow markups in firefox, chrome and IE10, all three rendered by doing translate first, then following by rotate! See the codepen snippet. What have I missed here? Or the implementations from the 3 browsers are not W3C compliant?
<svg width="180" height="200"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- This is the element before translation and rotation are applied -->
<rect x="0" y="0" height="100" width="100" style="stroke:#000; fill: #0086B2" fill-opacity=0.2 stroke-opacity=0.2></rect>
<!-- Now we add a text element and apply rotate and translate to both -->
<rect x="0" y="0" height="100" width="100" style="stroke:#000; fill: #0086B2" transform="rotate(45 100 50) translate(50)"></rect>
</svg>
The specification is pretty clear about the order which is that the rightmost transform is applied first.
If a list of transforms is provided, then the net effect is as if each transform had been specified separately in the order provided.
I.e,
<g transform="translate(-10,-20) scale(2) rotate(45) translate(5,10)">
<!-- graphics elements go here -->
</g>
is functionally equivalent to:
<g transform="translate(-10,-20)">
<g transform="scale(2)">
<g transform="rotate(45)">
<g transform="translate(5,10)">
<!-- graphics elements go here -->
</g>
</g>
</g>
</g>
If you want to sequentially change these transformations
you have to try this one
<g transform="translate(-10,-20) onmouseover=changeTransform(evt)">
function changeTransfrom(evt)
{
var id=evt.target;
id.setAttribute('transform',scale(0.5));
id.setAttribute('transform',rotate(30));
id.setAttribute('transform',skewY(45));
id.setAttribute('transform',matrix(2,2));
}