I can get this working successfully within the html body, example...
<div id="myContainer" style="float: left; background-color: Blue; height: 100px;
width: 100px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%">
<defs>
<lineargradient id="myLinearGradient" x1="0%" x2="0%" y1="0%" y2="100%">
<stop id="start" offset="50%" style="stop-color: White; stop-opacity: 1" />
<stop id="stop" offset="60%" style="stop-color: #99cd9f; stop-opacity: 1" />
</lineargradient>
</defs>
<circle cx="50px" cy="50px" r="50px" fill="url(#myLinearGradient)" />
</svg>
</div>
However, I need to create this dynamically using javascript. Creating just the circle works fine, it's when I point the circle's Fill to the lineargradient it fails - I just get a black circle.
I think I'm not setting the stop 'style' attribute correctly. I have tried an alternative way to no avail, see below...
I'm using Chrome, and thanks in advance!
Within the body tages:
<body>
<div style="float: left; background-color: Blue; height: 100px;
width: 100px;">
<svg id="Svg1" xmlns="http://www.w3.org/2000/svg">
<defs id="mydefs">
</defs>
</svg>
</div>
</body>
My script:
<script>
// lineargradient
var myLinearGradient = document.createElementNS("http://www.w3.org/2000/svg", "lineargradient");
myLinearGradient.setAttribute("id", "myLGID");
myLinearGradient.setAttribute("x1", "0%");
myLinearGradient.setAttribute("x2", "0%");
myLinearGradient.setAttribute("y1", "0%");
myLinearGradient.setAttribute("y2", "100%");
document.getElementById("mydefs").appendChild(myLinearGradient);
//stops
var stop1 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop1.setAttribute("id", "myStop1");
stop1.setAttribute("offset", "70%");
//stop1.setAttribute("style", "stop-color: White; stop-opacity: 1");
stop1.setAttribute("stop-color", "White");
document.getElementById("mydefs").appendChild(stop1);
var stop2 = document.createElementNS("http://www.w3.org/2000/svg", "stop");
stop2.setAttribute("id", "myStop2");
stop2.setAttribute("offset", "80%");
//stop2.setAttribute("style", "stop-color: #99cd9f; stop-opacity: 1");
stop2.setAttribute("stop-color", "#99cd9f");
document.getElementById("mydefs").appendChild(stop2);
// Circle
var myCircle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
myCircle.setAttribute("id", "idCircle");
myCircle.setAttribute("cx", "50px");
myCircle.setAttribute("cy", "50px");
myCircle.setAttribute("r", "50px");
myCircle.setAttribute("fill", "url(#myLGID)");
document.getElementById("Svg1").appendChild(myCircle);
</script>
Two things:
The element name for linear gradients is linearGradient, not lineargradient.
You have to append the stops to the linearGradient element, not the defs element.
See this codepen for an MIT-licensed example:
// Store the SVG namespace for easy reuse.
var svgns = 'http://www.w3.org/2000/svg';
// Create <svg>, <defs>, <linearGradient> and <rect> elements using createElementNS to apply the SVG namespace.
// (https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS)
var svg = document.createElementNS(svgns, 'svg');
var defs = document.createElementNS(svgns, 'defs');
var gradient = document.createElementNS(svgns, 'linearGradient');
var rect = document.createElementNS(svgns, 'rect');
// Store an array of stop information for the <linearGradient>
var stops = [
{
"color": "#2121E5",
"offset": "0%"
},{
"color": "#206DFF",
"offset": "100%"
}
];
// Parses an array of stop information and appends <stop> elements to the <linearGradient>
for (var i = 0, length = stops.length; i < length; i++) {
// Create a <stop> element and set its offset based on the position of the for loop.
var stop = document.createElementNS(svgns, 'stop');
stop.setAttribute('offset', stops[i].offset);
stop.setAttribute('stop-color', stops[i].color);
// Add the stop to the <lineargradient> element.
gradient.appendChild(stop);
}
// Apply the <lineargradient> to <defs>
gradient.id = 'Gradient';
gradient.setAttribute('x1', '0');
gradient.setAttribute('x2', '0');
gradient.setAttribute('y1', '0');
gradient.setAttribute('y2', '1');
defs.appendChild(gradient);
// Setup the <rect> element.
rect.setAttribute('fill', 'url(#Gradient)');
rect.setAttribute('width', '100%');
rect.setAttribute('height', '100%');
// Assign an id, classname, width and height
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%')
svg.setAttribute('version', '1.1');
svg.setAttribute('xmlns', svgns);
// Add the <defs> and <rect> elements to <svg>
svg.appendChild(defs);
svg.appendChild(rect);
// Add the <svg> element to <body>
document.body.appendChild(svg);
Related
My javascript animated SVG not working in WordPress. Can anybody help with the best practice of how to make an animated SVG work?
My first thought is - is it not working because of the security of having javascript inside an SVG file...
The file works as-is:
https://aau.gagarindigital.dk/wp-content/uploads/2021/07/stack-test-menu_3x.svg
My SVG file looks like this:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 912.83 882.07">
<defs>
<style>
:root {
--color-1: #F2B705;
--color-2: #C30F0E;
--color-3: #264ABA;
--color-4: #009CD2;
--color-5: #E25727;
--color-11: #C29204;
--color-22: #990B0B;
--color-33: #172E73;
--color-44: #006B8E;
--color-55: #BA4620;
}
#c1 {
fill: var(--color-1);
}
#c2 {
fill: var(--color-2);
}
#c3 {
fill: var(--color-3);
}
#c4 {
fill: var(--color-4);
}
#c5 {
fill: var(--color-5);
}
.cls-7 {
fill: #a1ac75;
}
circle:hover:not(.active) {
stroke: #999;
stroke-width: 11.5;
}
circle.active {
stroke: black;
stroke-width: 11.5;
}
line {
stroke: gold;
stroke-width: 4;
}
line.highlight {
stroke: black;
}
</style>
</defs>
<polygon id="fem-kant" class="cls-7" points="456.29 7.49 898.87 329.05 729.82 849.33 182.76 849.33 13.71 329.05 456.29 7.49"/>
<line class="c3 c5" x1="656.32" y1="748.57" x2="133.5" y2="368.62"/>
<line class="c2 c4" x1="258.55" y1="747.22" x2="779.33" y2="368.43"/>
<line class="c1 c4" x1="258.55" y1="747.22" x2="457.43" y2="133.5"/>
<line class="c1 c3" x1="457.43" y1="133.5" x2="657.03" y2="748.57"/>
<line class="c5 c2" x1="133.5" y1="368.62" x2="779.33" y2="368.43"/>
<line class="c1 c2" x1="779.33" y1="368.43" x2="457.43" y2="133.5"/>
<line class="c2 c3" x1="779.33" y1="368.43" x2="657.03" y2="748.57"/>
<line class="c3 c4" x1="258.55" y1="747.22" x2="657.03" y2="748.57"/>
<line class="c4 c5" x1="258.55" y1="747.22" x2="133.5" y2="368.62"/>
<line class="c5 c1" x1="133.5" y1="368.62" x2="457.43" y2="133.5"/>
<a href="#0">
<circle id="c1" cx="455.9" cy="133.5" r="133"/>
</a>
<a href="#0">
<circle id="c2" cx="779.33" cy="368.43" r="133"/>
</a>
<a href="#0">
<circle id="c3" cx="656.32" cy="748.57" r="133"/>
</a>
<a href="#0">
<circle id="c4" cx="256.18" cy="748.57" r="133"/>
</a>
<a href="#0">
<circle id="c5" cx="133.5" cy="368.62" r="133"/>
</a>
<script>
<![CDATA[
var allCircles = document.querySelectorAll("circle");
window.addEventListener('DOMContentLoaded', (event) => {
// Add an click handler to every circle that
// adds the class "active" to the clicked circle.
allCircles.forEach(element => {
element.addEventListener("click", clickHandler);
element.addEventListener("mouseover", mouseoverHandler);
element.addEventListener("mouseout", mouseoutHandler);
});
});
function clickHandler(evt) {
// Clear current selection (remove class "active" from any circle)
allCircles.forEach((circle) => circle.classList.remove("active"));
// Mark clicked circle selected
evt.target.classList.add("active");
// Clear any currently highlighted lines
clearHighlightedLines();
}
function mouseoverHandler(evt) {
let activeCircle = document.querySelector("circle.active");
let hoveredCircle = evt.target;
if (activeCircle && (activeCircle != hoveredCircle)) {
// Get the line that has classes matching both the actibve and hovered circle
let line = document.querySelector("line."+activeCircle.id+"."+hoveredCircle.id);
// Add the class "highlight" to that line
if (line)
line.classList.add("highlight");
}
}
function mouseoutHandler(evt) {
clearHighlightedLines();
}
function clearHighlightedLines() {
// Find the line with class "highlight" (if any)
var line = document.querySelector("line.highlight");
// Remove the class "highlight"
if (line)
line.classList.remove("highlight");
}
//]]>
</script>
</svg>
But the javascript is not working and the SVG file looks like this in WordPress:
https://aau.gagarindigital.dk/sample-page/
If the script should be outside the SVG file - can I link to the script placed at the root of the site. What would a link inside an SVG file look like?
I made a little mistake in my answer to your last question. The
var allCircles = document.querySelectorAll("circle");
part should be inside the DOMContentLoaded event. I believe that if you fix that, it will make a difference.
I'm trying to add a text element to my dynamically created SVG chart so that I can have labels generated but can't make it work.
There is a WHILE loop for adding boxes for a chart. I use the same while loop for text labels and it isn't working, it makes the boxes also fail. When I remove text labels, it works perfectly.
Your help much appreciated.
<svg id="mysvg" width="1000" height="800"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<text x="50" y="60" fill="black"
font-family="Arial, Helvetica, sans-serif"
font-size="28">Revenue and Expenses</text>
<line x1="150" y1="80" x2="150" y2="320"
style="stroke:rgb(155, 144, 144);stroke-width:5" />
<script type="application/ecmascript">
<![CDATA[
var mysvg = document.getElementById("mysvg");
var chartStart = [152, 84, 152]
var chartWidth = [100,64,36]
var chartNames = ["Revenue", "Expenses","Profit"]
var chartColor = ["#28CE6D","#DF3456","#4DC7EC"]
var num = chartNames.length;
while (num-- > 0)
{
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", chartStart[num]);
rect.setAttribute("y", [num] * 70 + 100);
rect.setAttribute("width", chartWidth[num]);
rect.setAttribute("height", "50");
rect.setAttribute("style", "fill:" + chartColor[num] + ";stroke:black;stroke-width:0;opacity:1");
var text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", "280");
text.setAttribute("y", [num] *70 + 130);
text.setAttribute("style", "fill:black");
text.setAttribute("font-family", "Arial, Helvetica, sans-serif");
text.setAttribute("font-size","18");
text.setAttribute("textContent", chartNames[num]);
mysvg.appendChild(rect);
mysvg.appendChild(text);
}
]]>
</script>
</svg>
Ok, found my own solution, I had not created a text node to append to the value for text element. Fixed it using the below. I'm sharing so that this question doesn't go unanswered. Thanks
<!DOCTYPE html>
<body>
<svg id="mysvg" width="1000" height="800"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<text x="50" y="60" fill="black"
font-family="Arial, Helvetica, sans-serif"
font-size="28">Revenue and Expenses</text>
<line x1="150" y1="80" x2="150" y2="320"
style="stroke:rgb(155, 144, 144);stroke-width:5" />
<script type="application/ecmascript">
<![CDATA[
var mysvg = document.getElementById("mysvg");
var chartStart = [152, 84, 152]
var chartWidth = [100,64,36]
var chartNames = ["Revenue", "Expenses","Profit"]
var chartColor = ["#28CE6D","#DF3456","#4DC7EC"]
var num = chartNames.length;
while (num-- > 0)
{
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", chartStart[num]);
rect.setAttribute("y", [num] * 70 + 100);
rect.setAttribute("width", chartWidth[num]);
rect.setAttribute("height", "50");
rect.setAttribute("style", "fill:" + chartColor[num] + ";stroke:black;stroke-width:0;opacity:1");
var label = document.createElementNS("http://www.w3.org/2000/svg", "text");
label.setAttribute("x", "280");
label.setAttribute("y", [num] *70 + 130);
label.setAttribute("style", "fill:black");
label.setAttribute("font-family", "Arial, Helvetica, sans-serif");
label.setAttribute("font-size","18");
var txt = document.createTextNode(chartNames[num]);
label.appendChild(txt);
mysvg.appendChild(rect);
mysvg.appendChild(label);
}
]]>
</script>
</svg>
</body>
</html>
I have a set of arrays to set the start position, weidth and name of some chart elements in a SVG. What I haven't been able to achieve is setting the colour of each of those elements using HEX values from an array called chart_color.
Currently you'll see I've set the value of fill to green. What I tried and failed with is fill: chart_color[num].
<!DOCTYPE html>
<body>
<svg id="mysvg" width="1000" height="800"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<text x="50" y="60" fill="black"
font-family="Arial, Helvetica, sans-serif"
font-size="28">Revenue and Expenses</text>
<line x1="150" y1="80" x2="150" y2="320"
style="stroke:rgb(155, 144, 144);stroke-width:5" />
<script type="application/ecmascript"> <![CDATA[
var mysvg = document.getElementById("mysvg");
var chart_start = [152, 84, 152]
var chart_width = [100,64,36]
var chart_names = ["Revenue", "Expenses","Profit"]
var chart_color = ["#28CE6D","#DF3456","#4DC7EC"]
var num = 3;
while (num-- > 0)
{
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", chart_start[num]);
rect.setAttribute("y", [num] * 70 + 100);
rect.setAttribute("width", chart_width[num]);
rect.setAttribute("height", "50");
rect.setAttribute("style", "fill:green;stroke:black;stroke-width:0;opacity:1");
mysvg.appendChild(rect);
}
]]></script>
</svg>
</body>
</html>
With style attributes you can actually assign them a value without using the setAttribute function:
rect.style.fill = chart_color[num]
But use this method consistently so that you don't override the style attribute, like setting color to "red" then setting style to "width:100px".
You just need to get the index (num) of the chart_color array and apply that as a variable within in your style declaration (breaking out of the string).
Note that you are also setting the border to stroke: black - but hte stroke width to 0 - So it can be seen I have amended that to stroke-width: 1 so that the border on each shows up.
<!DOCTYPE html>
<body>
<svg id="mysvg" width="1000" height="800"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<text x="50" y="60" fill="black"
font-family="Arial, Helvetica, sans-serif"
font-size="28">Revenue and Expenses</text>
<line x1="150" y1="80" x2="150" y2="320"
style="stroke:rgb(155, 144, 144);stroke-width:5" />
<script type="application/ecmascript"> <![CDATA[
var mysvg = document.getElementById("mysvg");
var chart_start = [152, 84, 152]
var chart_width = [100,64,36]
var chart_names = ["Revenue", "Expenses","Profit"]
var chart_color = ["#28CE6D","#DF3456","#4DC7EC"]
var num = 3;
while (num-- > 0)
{
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", chart_start[num]);
rect.setAttribute("y", [num] * 70 + 100);
rect.setAttribute("width", chart_width[num]);
rect.setAttribute("height", "50");
rect.setAttribute("style", "fill:" + chart_color[num] + ";stroke:black;stroke-width:1;opacity:1");
mysvg.appendChild(rect);
}
]]></script>
</svg>
</body>
</html>
I am using vanilla javascript to navigate the pages of a comic book. However, I need to setup a condition that checks if the points in the current polygon intersects with the points in the next polygon. If true, I want the viewBox to animate from the current points to the next points, if false let nothing happen (use the default fade transition).
Here is a section of my code
var DELAY = 400;
function nextArea() {
if (isFirstPage() || areaIndex >= areas.length - 1) {
changePage(true);
changeArea();
} else {
fade();
areaIndex++;
setTimeout(changeArea, DELAY);
}
}
function prevArea() {
if (isLastPage() || areaIndex <= 0) {
changePage(false);
changeArea();
} else {
fade();
areaIndex--;
setTimeout(changeArea, DELAY);
}
}
function changeArea() {
if (isFirstPage() || isLastPage()) {
return;
}
var activeArea = areas[areaIndex];
var points = activeArea.getAttribute('points').split(' ');
var xy1 = points[0].split(',');
var xy2 = points[1].split(',');
var xy3 = points[2].split(',');
var box = [xy1[0], xy1[1], xy2[0] - xy1[0], xy3[1] - xy2[1]];
activePage.classList.remove('fade');
activePage.setAttribute('viewBox', box.join(' '));
activeRect = rects[pageIndex - 1];
activeRect.setAttribute('x', xy1[0]);
activeRect.setAttribute('y', xy1[1]);
}
My code repository is here: https://github.com/cnario/svg-carousel
Here is what I have thus far: https://cnario.github.io/svg-carousel/
Here is how I expect it to act: https://read.marvel.com/#book/41323
I suppose this is what you may need: a way to transition the viewBox from one value to another so that every time you have only one part of the svg in the viewBox.
let BB = {};
BB.tomato = tomato.getBBox();
BB.skyblue = skyblue.getBBox();
BB.gold = gold.getBBox();
let radios = document.querySelectorAll("#controls input");
radios.forEach(r =>{
let color = r.dataset.color;
let bb = BB[color];
r.addEventListener("change",()=>{
svg.setAttributeNS(null,"viewBox", `${bb.x} ${bb.y} ${bb.width} ${bb.height}`)
svg.style.height = `${bb.height * 300 / bb.width}px`;
})
})
svg {
width: 300px;
border: 1px solid;
height: 600px;
transition: height 1s;
}
<p id="controls">
<label>tomato: <input type="radio" name="selector" data-color="tomato" /></label>
<label>skyblue: <input type="radio" name="selector" data-color="skyblue" /></label>
<label>gold: <input type="radio" name="selector" data-color="gold" /></label>
</p>
<svg id="svg" viewBox="0 0 100 200">
<g id="tomato">
<circle cx="35" cy="70" r="25" fill="tomato" />
</g>
<g id="skyblue">
<ellipse cx="75" cy="160" rx="15" ry="35" fill="skyblue" />
</g>
<g id="gold">
<polygon fill="gold" points="75,15 60,30 90,30" />
</g>
</svg>
HTML:
<div>
<svg class="velveti-grid-point" width="100" height="100" style="height: 120px; width: 625px;">
<circle class="myPoint" cx="500" cy="105" r="5" fill="#80E1EE" />
</svg>
<div class="theContainer">bg-color</div>
</div>
CSS:
.myPoint:hover + .theContainer {
background-color: black;
}
Problem: When i am hovering on the blue svg circle the background-color should be displayed on the text, but with svg it doesn't work. What is the problem? what do i need to do?
The demo: http://jsfiddle.net/wy6y66ox/
This has nothing to do with SVG per se.
+ is referred to as an adjacent selector. It will select only the element that is immediately preceded by the former element.
Because .myPoint is not a sibling of .theContainer your selector will not work.
You would need javascript in this instance.
Paulie_D is right.You have to use Javascript.
HTML
<div>
<svg class="velveti-grid-point" width="100" height="100" style="height: 120px; width: 625px;">
<circle id="svgid" class="myPoint" cx="500" cy="105" r="5" fill="#80E1EE" />
</svg>
<div id="divcolor" class="theContainer">bg-color</div>
</div>
JS
var svg = document.getElementById( 'svgid' );
var div = document.getElementById( 'divcolor' );
svg.onmouseover = function() {
div.style.backgroundColor = 'blue';
};
svg.onmouseout = function() {
div.style.backgroundColor = 'transparent';
};
Demo Here