As shown in this fiddle it's possible to render a CSS sprite in SVG using the foreignObject element.
However this isn't supported in IE, so I was wondering if there was another way to do it.
I suspect the answer may be no, because I found two unresolved questions on this (1,2)
I'm using d3.js so any answer that spells out the d3 way to do this would be a bonus.
You could pick out parts of an image using a clipPath if necessary. Extend your jsfiddle like this to see what I mean...
<div class='source youtube'></div>
<svg width="100%" height="100%">
<foreignObject height=50 width=50>
<div class='source facebook'></div>
</foreignObject>
<defs>
<clipPath id="c">
<rect y="10" width="7" height="10"/>
</clipPath>
<clipPath id="c2">
<rect x="7" y="12" width="7" height="10"/>
</clipPath>
</defs>
<image transform="scale(4.5)" y="-5" width="40" height="20" xlink:href="https://s3.amazonaws.com/856/sprite.png" clip-path="url(#c)"/>
<g transform="translate(-30, 0)">
<image transform="scale(4.5)" x="0" y="0" width="40" height="20" xlink:href="https://s3.amazonaws.com/856/sprite.png" clip-path="url(#c2)"/>
</g>
<svg>
Related
I have this model
<svg id=cardModel viewBox="0 0 100 200">
<rect x="0" y="0" width="100" height="200" fill="transparent" stroke="black"></rect>
<text id="text" x="25" y="125" font-size=100>change me</text>
</svg>
and I want to change the text from
<use href="#cardModel"></use>
I tryed using getElementById but as expected the <use> have no child.
Here's a jsFiddle: https://jsfiddle.net/e6gh07fa/316/
(seems like we can't use namespace in SO scripts)
I know I could use Node.cloneNode() but I want to know if there is another way
I try to make SVG text (wrapped in <text> of course) place on SVG <rect>.
And I want to make it look like text centered in my rect.
But strange thing I got there, it looks not like I assumed.
It looks like this
When I assumed it gonna be look like that
Whats wrong with that?
First I tought that equal x and y in <text> and <rect> will work, but it was like in picture below
I tought that y="50%" in text will force text to ancor somewhere in the middle. But I can achieve in only at y="80%" as you can see.
Original markup is here
<svg height="500" width="500" class="ng-scope">
<svg height="50" width="393.703125" y="0">
<g>
<rect x="0" y="0" height="50" width="393.703125" style="fill: #A8A8A8">
</rect>
<text font-family="Airal" font-size="45" x="0" y="50%" inline-size="200">
TEST TEXT IN SVG
</text>
</g>
</svg>
</svg>
The y property is per default applied to the text bottom line - so there is a difference between the y-position of a text and the y-position of lines, rectangles, or other shapes.
You can use the alignment-baseline property e.g. with middle to achieve better results. Here's the w3c description with many more options.
<svg height="500" width="500" class="ng-scope">
<svg height="50" width="393.703125" y="0">
<g>
<rect x="0" y="0" height="50" width="393.703125" style="fill: #A8A8A8">
</rect>
<text font-family="Airal" font-size="45" x="0" y="50%" inline-size="200" alignment-baseline="middle">
TEST TEXT IN SVG
</text>
</g>
</svg>
</svg>
add viewBox. Again i am not expert at svg , have done just some work but this may work.
<svg height="500" width="500" class="ng-scope">
<svg height="50" width="393.703125" y="0" viewBox="0 0 90 90">
<g>
<rect x="0" y="0" height="50" width="393.703125" style="fill: #A8A8A8" >
</rect>
<text font-family="Airal" font-size="45" x="0" y="50%" inline-size="200">
TEST TEXT IN SVG
</text>
</g>
</svg>
</svg>
Here's a storyboard of the CSS/JS/SVG animation I'm trying to accomplish. Two triangle masks enter from either side, then intersect resulting in a negative mask:
The point where the triangles intersect is where it gets tricky. When I export the mask for panel 4 to SVG, it looks like this:
<svg width="416px" height="289px" viewBox="0 0 416 289" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path d="M211.503681,65.6626347 L507.009604,-138.787586 L507.009604,425.787586 L211.507182,221.339788 L-84,425.792431 L-84,-138.787586 L211.503681,65.6626347 Z M211.503681,65.6626347 L99,143.5 L211.507182,221.339788 L324.01001,143.502422 L211.503681,65.6626347 Z" id="path-1"></path>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle-1-Copy-3" fill="#F6A623" x="0" y="0" width="416" height="289"></rect>
<mask id="mask-2" fill="white">
<use xlink:href="#path-1"></use>
</mask>
<use id="Combined-Shape" fill="#000000" xlink:href="#path-1"></use>
<rect id="Rectangle-1-Copy-2" fill="#4990E2" mask="url(#mask-2)" x="0" y="0" width="416" height="289"></rect>
</g>
</svg>
It looks like it's basically drawing two shapes, the negative-space diamond mask in the center and the remainder of the outer triangles.
So the static mask itself appears to be possible with SVG, but I don't know how to animate it. Is there a library that can simplify this kind of SVG transition/tweening, or a fancy math equation that can calculate the paths dynamically?
Or am I looking at this the wrong way entirely and there's a much easier way to do it altogether?
So the solution was to make it both more simple AND more complicated.
Instead of two layers on top of each other with one mask, I needed three layers. One on the bottom to show behind the first mask, the second to be masked by the incoming triangles, and a third layer above that, duplicate to the first, where a diamond-shaped mask is applied.
<svg width="500" height="300" viewbox="0 0 500 300">
<defs>
<clipPath id="triangles">
<path id="left" d="M-250,-150 L250,150 L-250,450 Z" fill="black" />
<path id="right" d="M250,150 L750,-150 L750,450 Z" fill="black" />
</clipPath>
<clipPath id="diamond">
<path id="diamond-path" d="M250,0 L500,150 L250,300 L0,150 Z" fill="black" />
</clipPath>
</defs>
<!-- bottom -->
<g id="bottom">
<rect fill="darkorange" x="0" y="0" width="500" height="300" />
<text x="50%" y="65%" text-anchor="middle" class="text">Text</text>
</g>
<!-- middle/triangles -->
<g id="middle" clip-path="url(#triangles)">
<rect fill="dodgerblue" x="0" y="0" width="500" height="300" />
<text x="50%" y="65%" text-anchor="middle" class="text">Text</text>
</g>
<!-- top/diamond -->
<g id="top" clip-path="url(#diamond)">
<rect fill="darkorange" x="0" y="0" width="500" height="300" />
<text x="50%" y="65%" text-anchor="middle" class="text">Text</text>
</g>
</svg>
The top layer with the diamond path starts out scaled to 0, making it invisible. The two triangle clip paths are animated in towards each other, showing the bottom layer underneath. When the two triangle points meet, the diamond clip path on the top layer is scaled up, revealing the top layer which is a duplicate of the bottom.
I also switched to clip paths instead of masks because they're a) better supported and b) allow for multiple paths.
Here's a Codepen using CSS for the animations (only works in WebKit for the moment).
UPDATE: Here's a Codepen using GSAP that works in all browsers: http://s.codepen.io/kgrote/debug/mPxzZY
The below selector will not find <clipPath> elements inside <defs> on Chrome (38):
d3.selectAll('defs clipPath')
(This is D3.js code but I suspect underlying querySelectorAll issue)
It works fine on Firefox. Is there a different selector syntax to use that will work on both browsers?
In the example below on Firefox you will see the whole text because the clip path is removed. But on Chrome it will be cut off after 85 pixels because the clip path is not removed.
d3.selectAll('defs clipPath').remove();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg height="200" width="400">
<defs>
<clipPath id="clip1">
<rect id='tt' x="0" y="0" width="85" height="15"></rect>
</clipPath>
</defs>
<text clip-path="url(#clip1)" x="0" y="15">This text should all be visible once we remove the clip-path</text>
</svg>
As Lars pointed out, this was a webkit bug, and now in Blink it still exists as Issue 237435: querySelectorAll unable to find SVG camelCase elements in HTML
So until it's fixed, using a class selector is probably the best workaround.
d3.selectAll('defs .clippy').remove();
d3.selectAll('defs .clippy').remove();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg height="200" width="400">
<defs>
<clipPath id="clip1" class='clippy'>
<rect id='tt' x="0" y="0" width="85" height="15"></rect>
</clipPath>
</defs>
<text clip-path="url(#clip1)" x="0" y="15">This text should all be visible once we remove the clip-path</text>
</svg>
Imagine this situation:
<svg height="0px">
<defs>
<g id="img">
<circle id="cir1" cx="10" cy="10" r="5"/>
<circle id="cir2" cx="30" cy="10" r="5" />
<rect id="rect1" x="50" y="5" width="20" height="10" />
</g>
</defs>
</svg>
<div id="div1">
<div id="div2" style="width:100px; height: 50px; border: 1px solid black">
hey
</div>
</div>
<svg height="20px"><use id="img1" xlink:href="#img"/></svg>
<svg height="20px"><use id="img2" xlink:href="#img"/></svg>
<svg height="20px"><use id="img3" xlink:href="#img"/></svg>
<svg height="20px">
<circle id="cir5" cx="10" cy="10" r="5"/>
</svg>
It's possible to change the color ONLY of the first circle of the image with id=img1 using jQuery/javascript?
Something like
$("#img1 #cir1").css("fill","red");
I tried but it doesn't work.
If can be helpful here's the fiddle: http://jsfiddle.net/MaxMarkson/q6Wep/
That's not possible. Think of the <use> as being paint the referenced item here. If you alter the referenced item then all the <use> instances will change.
The authors of the SVG specification have noticed that people often want to do this sort of thing and there is a SVG parameterisation proposal which may become part of SVG 2. The examples in the parameterisation proposal are created using javascript so you could do it that way right now if you copy the mechanism there.