Reputation: 95
I am a total newbie trying to make an interactive SVG - preferably without any external scripting. The effect that I am aiming for is to have one SVG element act as an interactive toggle to make another element appear and disappear.
Please find below a simple version where the text "Toggle" acts as the toggle. On click, this will animate the opacity attribute of the rectangle from 0 to 1 making it appear.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
width="47.652294mm"
height="10.096307mm"
viewBox="0 0 47.652294 10.096307"
version="1.1"
id="svg8">
<style
id="style861"></style>
<defs
id="defs2" />
<g
id="layer1"
transform="translate(-29.085516,-61.315985)">
<rect
fill="#ff0000"
opacity="0"
id="rect"
width="9.8317242"
height="9.8317242"
x="66.773796"
y="61.448277">
<animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext.click" />
</rect>
<text
xml:space="preserve"
style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
x="29.085516"
y="68.002762"
id="toggletext"><tspan
id="tspan857"
x="29.085516"
y="68.002762"
style="stroke-width:0.264583">Toggle</tspan></text>
</g>
</svg>
Any subsequent click will now just simply repeat that same animation. But I want any second (fourth, sixth, etc.) click to reverse the animation (i.e. make the rectangle disappear). In other words to truly act as a toggle.
Any advice on how to achieve this effect with as little code and/or invisible elements as possible would be greatly appreciated. Thanks!
Upvotes: 2
Views: 651
Reputation: 21193
Although I like the No-JavaScript pointer-events
method; this is how I would do it:
When OP says: preferably without any external scripting.
I presume he means no 3rd party libraries.
So I would use a native JavaScript Web Component (JSWC) <svg-toggle>
(supported in all modern browsers)
that creates the SVG for any number of toggles you want
To toggle animation:
from
and to
parameters on every click<style>
svg {
display: inline-block; width: 30%; vertical-align: top;
cursor: pointer; background: teal; color: white;
}
</style>
<svg-toggle></svg-toggle>
<svg-toggle color="yellow"></svg-toggle>
<svg-toggle color="blue" label="Blue" duration=".5"></svg-toggle>
<script>
customElements.define('svg-toggle', class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18">
<text x="2" y="12" font-size="10px" fill="currentColor">
${this.getAttribute("label")||'Toggle'}</text>
<rect fill="${this.getAttribute("color")||'red'}" x='33' y="3" width="12" height="12">
<animate attributeName="opacity" dur="${this.getAttribute("duration")||2}s" from="0" to="1" fill="freeze" begin="indefinite"/>
</rect></svg>`;
this.animate = this.querySelector("animate");
this.onclick = (evt) => this.toggle();
}
toggle( // method, so can be called from Javascript
from = this.animate.getAttribute("from"), // optional from/to parameters
to = this.animate.getAttribute("to"),
) {
this.animate.setAttribute( "from", to );
this.animate.setAttribute( "to" , from );
this.animate.beginElement();
}
});
</script>
Then stick the JavaScript on every SVG:
<svg
onclick="{
let a = this.querySelector('animate');
let from = a.getAttribute('from');
a.setAttribute('from',a.getAttribute('to'));
a.setAttribute('to',from);
a.beginElement();
}"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18">
<text x="2" y="12" font-size="10px" fill="currentColor">Gold</text>
<rect fill="gold" x="33" y="3" width="12" height="12">
<animate attributeName="opacity" dur=".3s" from="0" to="1"
fill="freeze" begin="indefinite"></animate>
</rect>
</svg>
See Enxaneta his pointer-events
answer
Upvotes: 2
Reputation: 33054
This is how I would do it: I'm using 2 overlapped text elements and I'm setting the pointer events to all or none on click: This way you'll click once on one text and next on the other.
The rect has 2 animate elements: one animation will start when you click on the first text, the second animation will start when clicking on the second text.
text{font-size:8.46667px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583;}
<svg width="300" viewBox="0 0 47.652294 10.096307" id="svg8">
<g id="layer1" transform="translate(-29.085516,-61.315985)">
<rect fill="#ff0000" opacity="0" id="rect" width="9.8317242" height="9.8317242" x="66.773796" y="61.448277">
<animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext1.click" />
<animate attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="toggletext2.click" />
</rect>
<text x="29.085516" y="68.002762" id="toggletext2">
<tspan x="29.085516" y="68.002762">Toggle</tspan>
<set attributeName="pointer-events" from="none" to="all" begin="toggletext1.click" />
<set attributeName="pointer-events" from="all" to="none" begin=".click" />
</text>
<text x="29.085516" y="68.002762" id="toggletext1">
<tspan x="29.085516" y="68.002762">Toggle</tspan>
<set attributeName="pointer-events" from="none" to="all" begin="toggletext2.click" />
<set attributeName="pointer-events" from="all" to="none" begin=".click" />
</text>
</g>
</svg>
Upvotes: 2
Reputation: 14565
Add a second rectangle fade animation
<animate id="hide" attributeName="opacity" fill="freeze"
from="1" to="0" dur="2s" begin="indefinite" />
And add a JS trigger that toggles the animation of the appearance and disappearance of the rectangle
var svg_1 = document.getElementById("svg8"),
hide = document.getElementById("hide"),
visable = document.getElementById("visable");
let flag = true;
svg_1.addEventListener('click', function() {
if (flag == true) {
visable.beginElement();
flag = false;
} else {
hide.beginElement();
flag = true;
}
});
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
width="47.652294mm"
height="10.096307mm"
viewBox="0 0 47.652294 10.096307"
version="1.1"
id="svg8">
<style
id="style861"></style>
<defs
id="defs2" />
<g
id="layer1"
transform="translate(-29.085516,-61.315985)">
<rect
fill="#ff0000"
opacity="0"
id="rect"
width="9.8317242"
height="9.8317242"
x="66.773796"
y="61.448277">
<animate id="visable" attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="indefinite" />
<animate id="hide" attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="indefinite" />
</rect>
<text
xml:space="preserve"
style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
x="29.085516"
y="68.002762"
id="toggletext"><tspan
id="tspan857"
x="29.085516"
y="68.002762"
style="stroke-width:0.264583">Toggle</tspan></text>
</g>
</svg>
Upvotes: 2