Dynamically add a shape / mask to an inline svg - javascript

I have a site with a series of elements that, when clicked on, add inline svg code to that element. The svg essentially animates an "iris wipe" to tunr the element white. Code from a separate html document is the loaded into a series of divs. When all the images are done loading, I want to append a mask to the svg code to iris wipe it back to how it was.
I am using waitForImages.js to check when the images are done loading. This is working successfully. The mask is also being added to the svg correctly. However, the mask animates.
Here is the initial code for adding the svg:
$("#selProject").append('<svg id="circleCont" x="0px" y="0px" viewBox="0 0 360 360" enable-background="new 0 0 360 360"><circle class="circ" cx="50%" cy="50%" r="0.01" stroke="#FFFFFF" stroke-width="0.5" fill="none"><animate attributeName="r" from="0.01" to="100%" dur="0.2" begin="0s" fill="freeze"/><animate attributeName="stroke-width" from="0.5" to="100" dur="0.2" begin="0s" fill="freeze"/></circle><circle class="circ" cx="50%" cy="50%" r="0.01" stroke="#FFFFFF" stroke-width="0.5" fill="none"><animate attributeName="r" from="0.01" to="100%" dur="0.2" begin="0.1s" fill="freeze"/><animate attributeName="stroke-width" from="0.5" to="200" dur="0.2" begin="0.1s" fill="freeze"/></circle><circle class="circ" cx="50%" cy="50%" r="0.01" fill="#FFFFFF" mask="url(#mask1)"><animate attributeName="r" from="0.01" to="100%" dur="0.3" begin="0.2s" fill="freeze"/></circle></svg>')
This is probably not the cleanest way to do it, but is the way I knew how.
Later, after some other code / loading the other html document using ajax
$("#selProject").waitForImages(function() {
$("#projectPageInfo").waitForImages(function() {
$("svg").append('<mask id="mask1"><rect fill="white" width="100%" height="100%" /><circle id="circmask" class="circ" cx="50%" cy="50%" r="0.01" fill="#000000"><animate attributeName="r" from="0.01" to="100%" dur="0.3" begin="0s" fill="freeze"/></circle></mask>');
});
});
The mask worked correctly when it was apart of the initial svg code being added, and animated as it should have. However, I needed it not to happen until the images have loaded, and now, despite the mask being added to the svg successfully, it does not animate. What am I missing?

I suspect it's the typical jQuery problem. JQuery cannot be relied on to do the right thing with SVG elements. jQuery is designed to work with HTML, not SVG whose elements are in a different namespace.
The first append works because the browser knows what to do with the <svg> element, and does the right thing. However the second append fails because the <mask> element will be created in the default (ie. HTML) namespace rather than the SVG one.
If you look at the DOM properties of the appended <mask> element in your browser's dev tools, you will probably find that it has the wrong (ie. not SVG) namespace.
As a solution, I would try adding the <mask> back in to the original SVG and only set the mask attribute when you want the mask to be used. In other words, remove:
mask="url(#mask1)"
then when you want the mask to be applied:
document.getElementById("id-of-masked-circle").setAttribute("mask", "url(#mask1)");

Related

How can I cut text from a shape in SVG?

I have an SVG file that looks like below:
Is there a way to make the text transparent? That is, instead of a fill color I want to cut out the layers and show what's in the background (of the SVG i.e. whatever lies underneath the SVG). In other words, make the intersection of the path & text to be transparent?
Contents of the SVG file:
<svg width="36.087" height="34.314" viewBox="0 0 36.087 34.313999" x="1190.56" y="753.5780000000001">
<path fill="#63a95c" d="M36.087 13.107l-13.305-.66L18.047 0l-4.742 12.446L0 13.106l10.377 8.352L6.89 34.314l11.157-7.285 11.14 7.284-3.475-12.856" fill-rule="evenodd"/>
<text font-size="10px" x="10.498" y="23.484" fill="#ffffff" fill-opacity="1" font-family="OpenSans-Bold">8.5</text>
</svg>
I tried changing the transparency of the text element, but that only affects the text. The text inside the SVG is variable is populated dynamically so I can't "pre-process" the SVG file. Is there a way perhaps using evenodd fill or something similar to create an "exclusion" for intersection? Is it possible using one of the SVG JS libraries such as snap.svg or svg.js?
Edit:
The final SVG should look like this:
The SVG code posted above is for the star and the text. The final SVG should have the background color showing through the text while retaining the outer shape of the star.
Create a mask, put the text in it via a text element and then use the mask on the shape you want to clip a hole in. Something like this...
head, body {
width:100%;
height:100%;
}
<svg width="100%" height="100%" viewBox="0 0 200 200">
<defs>
<mask id="sample" maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white"/>
<text x="12" y="23" font-size="10" font-family="Impact">9.0</text>
</mask>
</defs>
<path fill="#63a95c" d="M36.087 13.107l-13.305-.66L18.047 0l-4.742 12.446L0 13.106l10.377 8.352L6.89 34.314l11.157-7.285 11.14 7.284-3.475-12.856" fill-rule="evenodd" mask="url(#sample)"/>

SVG Hover State with Multiple Elements

Good afternoon everyone,
I'm defining an SVG on my page with the following defs.
<svg width="0" height="0">
<defs>
<g id="stroke-hexagon">
<polygon fill="#002663" stroke="#FFFFFF" stroke-width="6" stroke-miterlimit="12" points="57.8,185 5.8,95 57.8,5 161.8,5 213.8,95 161.8,185 "/>
</g>
<g id="hexagon">
<polygon fill="#006890" points="52,180 0,90 52,0 156,0 208,90 156,180 "/>
</g>
</defs>
</svg>
...and implementing it later in the HTML using this:
<svg width="208px" height="180px" viewBox="0 0 208 180" >
<use xlink:href="#hexagon"></use>
<text class="faicon" x="50%" y="70px" fill="white" font-size="80px" text-anchor="middle">&#xf040</text>
<text text-anchor="middle" x="50%" y="70%" fill="white">Logo Here</text>
</svg>
Works totally fine. I am also able to style the polygon's fill with simple CSS. Looks like this:
#hexagon:hover polygon {
fill:#990000;
}
The hover effect fails, however, whenever the mouse leaves the polygon and instead hovers over either of the 'text' elements within the svg. Is there a way to define a CSS rule that prevents this behavior. Or, would it be better (easier) to change the attribute using JS / jQuery?
Thanks!
Your texts are rendered on top of your polygon and are therefore intercepting mouse events. You should set up a css rule like
text {
pointer-events: none;
}
This will prevent the text from becoming a target of mouse events which should give you the desired hover effect for the polygon.

Stop/Start SVG-Rotation via Javascript

I need a little bit of help.
In the following code, I want to control the rotation of the star via onmouse-event.
If you move the mouse over the star, it is supposed to rotate.
I thought about changing the transform in attributeName to something different when the mouse is not over the star via roationon()/off() functions so that the rotation doesn't work but I have no idea how to do that.
I appreciate every help I can get.
Thanks
<!DOCTYPE html>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<html>
<body>
<script>
function rotationon() {}
function rotationoff() {}
</script>
<svg height="2000" width="2000">
<polygon id="stern1" points="100,10 40,180 190,60 10,60 160,180" fill="yellow" transform="translate(100, 100)" onmouseover="rotationon()" onmouseout="rotationoff()" >
<animateTransform
attributeName="transform"
begin="0s"
dur="5s"
type="rotate"
from="0 100 100"
to="360 100 100"
repeatCount="indefinite"
/>
</polygon>
</svg>
</body>
</html>
There's a few different ways to approach this, depending on what its going to integrate with, and how well it will play with other browsers.
My gut instinct would be to say ultimately, its worth using one of the SVG libs out there, like Raphael, snap.svg, Pablo.js etc. They will help with some of the issues likely to be faced.
You can also just use pure SVG like I mentioned http://jsfiddle.net/xaM6q/
However, to use the method you are trying, you may want to use something like beginElement() and endElement, so the code could look something like the following...fiddle at http://jsfiddle.net/xaM6q/2/
<script>
function rotationon(evt){
document.getElementById('myanim').beginElement();
}
function rotationoff(){
document.getElementById('myanim').endElement();
}
</script>
<svg height="2000" width="2000">
<g transform="translate(100,100)">
<polygon id="stern1" points="100,10 40,180 190,60 10,60 160,180" fill="yellow" onmouseover="rotationon()" onmouseout="rotationoff()" >
<animateTransform
id="myanim"
attributeName="transform"
begin="indefinite"
dur="5s"
type="rotate"
from="0 100 100"
to="360 100 100"
fill="freeze"
repeatCount="indefinite"
/>
</polygon>
</g>
Couple of things worth noting. I've added a g element to help keep the transformation in place, as you probably want that (without it, you may find it moves away). Also the animation may be a bit erratic depending how you want it to stop (I've added 'fill=freeze'), and what happens with events mid animation.
Its worth knowing all of this to get to know SVG animations, but as mentioned, I would probably still look at using a 3rd party lib and control the rotation manually, rather than using the animate tag, so you can halt/restart a rotation at any angle easily.

How to trace out letters like a laser

Looking for ideas on how to animate what looks like a laser drawing out a word in a cursive font using SVG. The animation can be done with SMIL or JavaScript I don't care - though I think it would be easier with SMIL.
I am pretty sure if I could just get the letters represented as a path I could figure out how to animate a line from a fixed point to the word path - even if the path is non-continuous.
Any ideas?
EDIT
My demo was very basic, essentially I wrote animate functions for each letter and arranged their timing. Here is the letter X for example:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="none">
<rect x="0" y="0" width="100%" height="100%" fill="black"/>
<path id="word" stroke="red" d="M10 10 L40 40 M40 10 L10 40" />
<line x1="10" y1="10" x2="25" y2="50" stroke="blue" stroke-width="0.5">
<animate attributeName="x1" begin="0s" dur="1s" values="10; 40;" />
<animate attributeName="y1" begin="0s" dur="1s" values="10; 40;" />
<animate attributeName="x1" begin="1s" dur="1s" values="40; 10;" />
<animate attributeName="y1" begin="1s" dur="1s" values="10; 40;" />
<set attributeName="visibility" to="hidden" begin="2s" />
</line>
</svg>
I am sure we can all agree that this is not an ideal long term solution... I thought it would be relatively easy to animate one end of a LINE along a path but I am having problems just getting the path...
Extract the paths from the glyphs in question, then apply a dash-array animation as seen in this example on each of the paths.
From a high level perspective, I would think you would want to do something like render the font to a canvas, then use the pixel information to generate the animation sequence. A simple algorithm could just trace from left to right, it would be a good deal harder to figure out a single stroke path, but that is doable as well.
You don't mention any idea of what platform or any time constraints, so its hard to get much closer than that.
One possibility... SVG Fonts are, I understand, stored as a sequence of SVG commands used to draw individual characters. The vector-based nature of drawing in SVG would seem like it would be amenable to 'tracing out' characters in realtime; you might be able to make a conversion utility to pre-convert SVG fonts to simple paths.

How to create SVG animation tag using javascript

I have this SVG circle with an animation.
<circle cx="30" cy="50" r="15" fill="blue" stroke="black" stroke-width="1">
<animateMotion path="M 0 0 H 300 Z" dur="8s" repeatCount="indefinite" />
</circle>
It works fine. I want to make the exact same thing using javascript so if I click a button, it would create the same circle with the same animation.
here is my try:
var animateMotion = document.createElementNS("http://www.w3.org/2000/svg", "animateMotion");
animateMotion.setAttribute("dur","8s");
animateMotion.setAttribute("d", "M 0 0 H 300 Z");
animateMotion.setAttribute("repeatCount","indefinite");
//assuming that I already created my circle
myCircle.appendChild(animateMotion);
var animateMotion = document.createElementNS('http://www.w3.org/2000/svg','animateMotion');
animateMotion.setAttribute("dur","3s");
animateMotion.setAttribute("repeatCount","1");
var mpath = document.createElementNS('http://www.w3.org/2000/svg','mpath');
mpath.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#YourPath");
animateMotion.appendChild(mpath);
Looks like animateMotion elements are bugged in Webkit, when dinamically created (see SVG elements will not Animate when added dynamically), and unsupported in IE. I think you're better off using pure javascript animation using a library like d3.
Maybe this is your problem:
In XML:
<animateMotion path= ... />
In JS:
animateMotion.setAttribute("d", ... ); //Should this be "path"?
Hope it helps,
regards m93a.
If just the click is important, it can be solved by using the begin attribute in your animateMotion:
<svg height="500" width="500" id="foo">
<circle cx="30" cy="50" r="15" fill="blue" stroke="black" stroke-width="1">
<animateMotion path="M 0 0 H 300 Z" dur="8s" repeatCount="indefinite" begin="foo.click"/>
</circle>
</svg>
Should work with a button as well, if the button has an identifier, e.g. id="startbutton", then it should be begin="startbutton.click
Another alternative with inline javascript can be found here: http://svg-whiz.com/svg/PausePlay.svg
cheers
if you want the animate as soon as <animateMotion> element append page,
set <animateMotion> 'begin' to 'indefinite' and begin the animate call its .beginElement();
var animateMotion = document.createElementNS(SVG_NS, 'animateMotion');
animateMotion.setAttribute('begin', 'indefinite');
//append animateMotion to the page
animateMotion.beginElement();
Here you can get number of examples regarding svg using javascript.
You can get help from there.

Categories

Resources