I am trying to make a play and stop button. I don't know how to morph the triangle shape (it is a path) into the square shape (it is a path) when it has been clicked. Only showing one shape at a time. Can anyone help?
<svg class="playStop" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 971 530" style="enable-background:new 0 0 971 530;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;
stroke:#000000;
stroke-width:4;
stroke-miterlimit:10;}
</style>
<path id="playTriangle" class="st0" d="M432,290.7V187.8c0-11.4,9.2-20.7,20.6-20.8c3.2,0,6.3,0.7,9.2,2.2l86.9,43.3l16.2,8.1c10.2,5,14.5,17.5,9.4,27.7c-2,4.1-5.3,7.5-9.4,9.5l-13.4,6.7l-89.8,44.8c-10.2,5-22.6,0.8-27.6-9.5C432.7,297,432,293.9,432,290.7z"/>
<path id="stopSquare" class="st0" d="M458.6,167h91.3c14.7,0,26.6,11.9,26.6,26.6v91.3c0,14.7-11.9,26.6-26.6,26.6h-91.3c-14.7,0-26.6-11.9-26.6-26.6v-91.3C432,178.9,443.9,167,458.6,167z"/>
</svg>
I think one way is to define your two paths in defs and then use a use xlink:href="#shapeName" with an onclick handler that toggles that attribute or the corresponding DOM property, if supported..
A use element object with fully implemented SVG DOM has a href property with a baseVal property that can be read and set, so inside browsers as far as I have tested (with Firefox, Chrome, IE and Edge on Window) we can simply toggle that property, see https://jsfiddle.net/4x0gnkob/ for an online sample.
.st0{fill:none;
stroke:#000000;
stroke-width:4;
stroke-miterlimit:10;}
<svg class="playStop" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 971 530" style="enable-background:new 0 0 971 530;" xml:space="preserve">
<defs>
<path id="playTriangle" class="st0" d="M432,290.7V187.8c0-11.4,9.2-20.7,20.6-20.8c3.2,0,6.3,0.7,9.2,2.2l86.9,43.3l16.2,8.1c10.2,5,14.5,17.5,9.4,27.7c-2,4.1-5.3,7.5-9.4,9.5l-13.4,6.7l-89.8,44.8c-10.2,5-22.6,0.8-27.6-9.5C432.7,297,432,293.9,432,290.7z"/>
<path id="stopSquare" class="st0" d="M458.6,167h91.3c14.7,0,26.6,11.9,26.6,26.6v91.3c0,14.7-11.9,26.6-26.6,26.6h-91.3c-14.7,0-26.6-11.9-26.6-26.6v-91.3C432,178.9,443.9,167,458.6,167z"/>
</defs>
<use xlink:href="#playTriangle" pointer-events="all" onclick="this.href.baseVal = this.href.baseVal == '#playTriangle' ? '#stopSquare' : '#playTriangle';"></use>
</svg>
An alternative is to toggle the DOM attribute, it seems a bit complicated in an HTML5 environment as I thought I could solve it with setAttributeNS and getAttributeNS in one line, after some testing it seems that within HTML5 getAttribute('xlink:href') works better, so the full code tries to test which function returns a value.
function toggleLink(element, value1, value2) {
var xlinkNS = 'http://www.w3.org/1999/xlink';
var linkName = 'xlink:href';
var oldValue = element.getAttributeNS(xlinkNS, linkName) || element.getAttribute(linkName);
if (element.hasAttributeNS(xlinkNS, 'href')) {
element.setAttributeNS(xlinkNS, linkName, oldValue == value1 ? value2 : value1)
}
else {
element.setAttribute(linkName, oldValue == value1 ? value2 : value1);
}
}
.st0{fill:none;
stroke:#000000;
stroke-width:4;
stroke-miterlimit:10;}
<svg class="playStop" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 971 530" style="enable-background:new 0 0 971 530;" xml:space="preserve">
<defs>
<path id="playTriangle" class="st0" d="M432,290.7V187.8c0-11.4,9.2-20.7,20.6-20.8c3.2,0,6.3,0.7,9.2,2.2l86.9,43.3l16.2,8.1c10.2,5,14.5,17.5,9.4,27.7c-2,4.1-5.3,7.5-9.4,9.5l-13.4,6.7l-89.8,44.8c-10.2,5-22.6,0.8-27.6-9.5C432.7,297,432,293.9,432,290.7z"/>
<path id="stopSquare" class="st0" d="M458.6,167h91.3c14.7,0,26.6,11.9,26.6,26.6v91.3c0,14.7-11.9,26.6-26.6,26.6h-91.3c-14.7,0-26.6-11.9-26.6-26.6v-91.3C432,178.9,443.9,167,458.6,167z"/>
</defs>
<use xlink:href="#playTriangle" pointer-events="all" onclick="toggleLink(this, '#stopSquare', '#playTriangle')"></use>
</svg>
Online at https://jsfiddle.net/w36k21uz/1/.
You cannot do all the things just like that, you can either use SMIL, which is to become deprecated, or use a dedicated animation engine. I developed KUTE.js with a SVG Plugin that does most of the things you probably need for SVG.
A quick demo (should work in Firefox only due to some Stackoverflow XSS issue):
<div style="width: 220px">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 600">
<path id="rectangle" fill="indigo" d="M38.01,5.653h526.531c17.905,0,32.422,14.516,32.422,32.422v526.531
c0,17.905-14.517,32.422-32.422,32.422H38.01c-17.906,0-32.422-14.517-32.422-32.422V38.075C5.588,20.169,20.104,5.653,38.01,5.653z"></path>
<path id="star" style="visibility:hidden" d="M301.113,12.011l99.25,179.996l201.864,38.778L461.706,380.808
l25.508,203.958l-186.101-87.287L115.01,584.766l25.507-203.958L0,230.785l201.86-38.778L301.113,12.011"></path>
</svg>
</div>
<script id="core" src="https://cdn.jsdelivr.net/kute.js/1.5.5/kute.min.js"></script>
<script id="svg" src="https://cdn.jsdelivr.net/kute.js/1.5.5/kute-svg.min.js"></script>
<script>
var tween = KUTE.to('#rectangle', { path: '#star' }, {duration: 1500, yoyo: true, repeat: 1}).start();
document.addEventListener('click', function(){
!tween.playing && tween.start();
}, false);
</script>
I have a map with SVG and I am going to create an Area Map with that. It works well when I embed that SVG file in my HTML page, but not when I include it by
This is my SVG:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="595.28px" height="841.89px" viewBox="0 0 595.28 841.89" enable-background="new 0 0 595.28 841.89" xml:space="preserve">
<g id="Layer_1">
<rect x="106.429" y="86.429" fill="#D32A2A" stroke="#000000" stroke-miterlimit="10" width="311.429" height="194.286"/>
</g>
<g id="Layer_2">
<rect x="127.144" y="261.143" fill="#1F5785" stroke="#000000" stroke-miterlimit="10" width="311.428" height="194.287"/>
</g>
</svg>
And this is my javascript:
$(function(){
$('#Layer_2').click(function() {
alert('Thanks');
});
});
This works well and when I click on blue square, an alert shows. DEMO
But I need to include it like this:
<object data="map.svg" type="image/svg+xml" id="map-id" width="100%" height="100%"></object>
and when I include it, I don't have access to that. DEMO
You right this don't work...
You need for use "live" bind, because when you include it directly it
load in not same time as your script:
$('#Layer_2').live('click', function() {
alert('Thanks');
});
I have found this solutions first for read, second for read
To access svg.contentDocument it must be loaded from the same domain, and it will be null otherwise.
There working DEMO.
for simlify your use svg as inside you must start your code after loading all svg objects:
<object id="mysvg1" data="./test.svg" type="image/svg+xml" width="100%" height="100%"></object>
<object id="mysvg2" data="./test.svg" type="image/svg+xml" width="100%" height="100%"></object>
...
var my_start_function = function(){
// start your code here
}
...
var svg_objects = ['mysvg1','mysvg2'], load_counter = 0;
for (var i in svg_objects) {
document.getElementById(svg_objects[i]).addEventListener("load",function() {
load_counter++;
if (load_counter == svg_objects.length) my_start_function();
}, false);
}
and access to svg elements throught a little function:
var svg_inside = function(svg_id,elem_id) {
return $($(document.getElementById(svg_id.substr(1)).contentDocument).find(elem_id)[0]);
}
...
svg_inside('#mysvg1','#Layer_2').click(function() {
alert('Thanks');
});
I have an svg image that has several different paths within it. How would I go about using snap to 'loop' the svg image and create an array with the ID of each element as a variable name and the path attribute of each element and the value of that array. (Eventually I would like to animate the path morphs.)
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="612px"
height="792px" viewBox="0 0 612 792" enable-background="new 0 0 612 792" xml:space="preserve">
<g id="start">
<path id="shape" d="M197.063,173.937c-34.624,8.446-44.945,28.214-37.234,56.383c-10.217,26.923-0.263,47.865,37.234,60.639
c-27.426,18.955-33.862,37.69-36.17,56.383c55.893-14.307,98.408-8.579,131.915,10.638c31.761-17.963,45.708-39.883,45.745-64.894
c-45.952,8.926-61.438-0.612-63.83-18.085c40.011-15.036,53.504-32.917,44.681-53.191
C261.068,217.78,215.96,204.763,197.063,173.937z"/>
</g>
<g id="middle">
<path id="shape_1" fill="#590E0E" d="M265.148,215.426c-34.624,8.446-113.03-13.275-105.319,14.894
c-10.217,26.923,39.099,4.248,76.596,17.021c-27.426,18.955-73.224,81.307-75.532,100c55.893-14.307,98.408-8.579,131.915,10.638
c31.761-17.963,45.708-39.883,45.745-64.894c-45.952,8.926-61.438-0.612-63.83-18.085c40.011-15.036,53.504-32.917,44.681-53.191
C324.589,80.138,328.73,149.424,265.148,215.426z"/>
</g>
<g id="final">
<path id="shape_2" fill="#146734" d="M31.106,44.149c-34.624,8.446,121.012,158.002,128.723,186.17
c-10.217,26.923,39.099,4.248,76.596,17.021c-27.426,18.955-73.224,81.307-75.532,100c55.893-14.307,98.408-8.579,131.915,10.638
c31.761-17.963,45.708-39.883,45.745-64.894c-45.952,8.926-61.438-0.612-63.83-18.085c40.011-15.036,53.504-32.917,44.681-53.191
C202.381,169.062,47.715,12.327,31.106,44.149z"/>
</g>
</svg>
If you use the selectAll method, it will bring to back an Array like Snap object (sounds like you need this rather than an array). So something like this maybe (untested).
var someObj = {};
var gs = s.selectAll('g');
gs.forEach(function(el) {
var p = el.select('path');
someObj[ el.attr('id') ] = p.attr('d');
});
fiddle
Overall, I have a rectangular SVG image separated into two halves which I would like to add an image to on click, however, when I import the SVG it creates only one PathGroup rather than two- Does anybody know how I can achieve two distinct PathGroups? I believe this is the first step in achieving my overall goal.
Here's how I'm loading the SVG:
fabric.loadSVGFromURL('./img/rect_w_two_halves.svg', function(objects, options){
loadedObject = fabric.util.groupSVGElements(objects, options);
loadedObject.set({
left:0,
top:0,
scaleY:.50,
scaleX:.50
});
canvas.add(loadedObject);
canvas.renderAll();
});
Here's how the image looks like:
Here's the SVG:
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="320px"
height="568px" viewBox="0 0 320 568" enable-background="new 0 0 320 568" xml:space="preserve">
<g id="Layer_2">
<line fill="none" stroke="#FFFFFF" stroke-width="15" stroke-miterlimit="10" x1="253.978" y1="284" x2="66.022" y2="284"/>
<path fill="none" stroke="#FFFFFF" stroke-width="15" stroke-miterlimit="10" d="M253.04,448.055
c0,10.338-8.381,18.719-18.719,18.719H85.68c-10.338,0-18.72-8.381-18.72-18.719V119.945c0-10.338,8.382-18.719,18.72-18.719
h148.642c10.338,0,18.719,8.381,18.719,18.719V448.055L253.04,448.055z"/>
</g></svg>
Thanks in advance as I've been really stuck on this issue.
You can add objects from SVG without grouping by passing every element into canvas.add method:
var canvas = new fabric.Canvas('mycanvas');
fabric.loadSVGFromURL(url);
function callback(objects, options) {
canvas.add.apply(canvas, objects);
canvas.renderAll();
}
There's a demo on Fabric.js website, you can find that part at Load SVG tab, "load without grouping" button
I am trying to change the attribute of a circle in an SVG via javascript. When I have the SVG directly embeded in the HTML file, it works: 'kreis1' is changing color upon clicking a button. But when I have the SVG in an extra file (which I would like to have ultimatly when the SVG will have more than two circles), 'kreis1' doesn't react.
The is my SVG file, two circles:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="200px" height="200px" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<circle id="kreis1" fill="#8CC63F" stroke="#000000" cx="53.318" cy="55.5" r="50"/>
<circle id="kreis2" fill="#C44741" stroke="red" cx="138.786" cy="130" r="30" stroke-width="0"
onmouseover="evt.target.setAttribute('stroke-width','10');"
onmouseout="evt.target.setAttribute('stroke-width','0');"/>
</svg>
... and here is my HTML:
<!DOCTYPE html>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="200px" height="200px" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<circle id="kreis1" fill="#8CC63F" stroke="#000000" cx="53.318" cy="55.5" r="50"/>
<circle id="kreis2" fill="#C44741" stroke="red" cx="138.786" cy="130" r="30" stroke-width="0"
onmouseover="evt.target.setAttribute('stroke-width','10');"
onmouseout="evt.target.setAttribute('stroke-width','0');"/>
</svg>
<button onclick="getElementById('kreis1').innerHTML=changeColor()">Change!</button>
<object type="image/svg+xml" data="test.svg">
<html>
<head>
<script>
var htmlDocument = document;
function changeColor()
{
document.getElementById("kreis1").setAttribute("fill", "blue");
}
</script>
</head>
</html>
test.svg is a different document to the html document. Fortunately you can get the test.svg document via the contentDocument field on the <object> tag.
<!DOCTYPE html>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="200px" height="200px" viewBox="0 0 200 200" enable-background="new 0 0 200 200" xml:space="preserve">
<circle id="kreis1" fill="#8CC63F" stroke="#000000" cx="53.318" cy="55.5" r="50"/>
<circle id="kreis2" fill="#C44741" stroke="red" cx="138.786" cy="130" r="30" stroke-width="0"
onmouseover="evt.target.setAttribute('stroke-width','10');"
onmouseout="evt.target.setAttribute('stroke-width','0');"/>
</svg>
<button onclick="getElementById('kreis1').innerHTML=changeColor()">Change!</button>
<object id="object1" type="image/svg+xml" data="test.svg">
<html>
<head>
<script>
var htmlDocument = document;
function changeColor()
{
document.getElementById("object1").contentDocument.getElementById("kreis1").setAttribute("fill", "blue");
}
</script>
</head>
</html>
It's a limitation of browser layout engines. External SVGs can't be styled by CSS. They have to be embedded in the HTML directly. JavaScript does permit interaction via the object tag per Robert's answer.
If you'd like to go down the embedding route (in my opinion it is more flexible) I've developed a script to automate this embedding, I've got a couple of hours so I'll bundle it up into something reusable this morning and update the question when it's done.
EDIT: Here's the embedding plugin, with docs: http://sakurasvg.appsondemand.com.au/