How to fix issue with svg element not rendering into image (dom2img) - javascript

I'm using amcharts4 to build heat map and then via dom2img I'm transforming that into image. Everything is fine and map itself is rendered but legend is not captured with dom2img. Legend itself is an svg element.
I thought maybe it has something to do with gradient because that's the only element with it.
svg element:
<g fill-opacity="1" fill="url("http://localhost:4200/campaigns/pptx/2#gradient-id-257")" style="pointer-events: none;"><path d="M0,0 L455,0 a0,0 0 0 1 0,0 L455,20 a0,0 0 0 1 -0,0 L0,20 a0,0 0 0 1 -0,-0 L0,0 a0,0 0 0 1 0,-0 Z"></path></g>
Dom2img should render legend element.

Use amCharts built-in export feature instead: https://www.amcharts.com/docs/v4/concepts/exporting/
// First enable export
chart.exporting.menu = new am4core.ExportMenu();
chart.exporting.extraSprites.push({
"sprite": legendContainer,
"position": "bottom",
"marginTop": 20
});
// Then you can get the Base64 image
var imgData = chart.exporting.getImage("png");
You can also include the legend using extraSprites as shown above. Read more about including external legend in export.

Related

Svg animations replaying in the same spot

I am working on a tower defense game with HTML, CSS, and JS. I want to be able to create svg circles that will follow a path using an svg animation. In order to do this, I wrote this code:
<div id="trackPart">
<progress id="healthBar" max="200" value="200" onclick="this.value = randInt(0, this.max)" ></progress>
<svg viewbox="0 0 1000 3000" id="track" xmlns="http://www.w3.org/2000/svg">
<path id="path" fill="none" stroke="red" stroke-width="15" d="M 0, 50 h 900 v 100 h -800 v 100 h 800 v 300 h -900" style="cursor: pointer"/>
</svg>
</div>
var path = document.getElementById("path")
var svgurl = "http://www.w3.org/2000/svg"
var svg = document.getElementById("track")
listOfColors = ["green", "blue", "purple", "lime", "yellow"]
function attackerSVG(color) {
let element = document.createElementNS(svgurl, "circle")
element.setAttribute("cx", 0)
element.setAttribute("cy", 0)
element.setAttribute("r", 15)
element.setAttribute("fill", color)
let animation = document.createElementNS(svgurl, "animateMotion")
animation.setAttribute("dur", "30s")
animation.setAttribute("repeatCount", "indefinite")
animation.setAttribute("rotate", "auto")
animation.setAttribute("path", String(path.getAttribute("d")))
animation.setAttribute("onrepeat", "console.log(\"repeat\")")
animation.setAttribute("restart", "always")
animation.beginElement()
element.appendChild(animation)
svg.appendChild(element)
return element
}
attackerSVG("black")
The first time I run the attackerSvg function, everything works fine. A circle is created at the start of the path and follows it. However, once I create another one, it starts its animation sequence where the other svg circles are. If you want to see what I mean, you can go here
https://replit.com/#hello1964/Tower-defense-game#script.js
Whenever you see the circle change color, it's a new circle being created. When you look in the console it will print "repeat" every time a circle finishes a cycle. Since they are all in the same spot, it will print it multiple times. I would really appreciate the help, thank you.
SVG maintain their own timings. Adding an element to the SVG, will start it at the current clock. To do what you want, you actually need to delay the start
// outside globally somwhere
let count = 0;
// inside function attackerSVG
animation.setAttribute('begin', `${++count}s`)

How to animate SVG fill and path in a similar style to vivus.js

I am trying to animate an SVG with vivus.js but it doesn't seem to be doing anything.
Vivus seems to be able to read the SVG correctly, I can run the following code:
var test = new Vivus('welcome_message', {duration: 200});
console.table(test.map);
And get the following result in the console (this is how vivus recommends you debug it):
(index) el startAt duration progress
0 path 0 133.33333333333331 0.0075000000000000015
1 path 5.555555555555556 133.33333333333331 0
2 path 11.111111111111112 133.33333333333331 0
3 path 16.666666666666668 133.33333333333331 0
4 path 22.222222222222225 133.33333333333331 0
5 path 27.777777777777782 133.33333333333331 0
6 path 33.333333333333336 133.33333333333331 0
7 path 38.88888888888889 133.33333333333331 0
8 path 44.44444444444445 133.33333333333331 0
9 path 50.00000000000001 133.33333333333331 0
10 path 55.555555555555564 133.33333333333331 0
11 path 61.11111111111112 133.33333333333331 0
12 path 66.66666666666667 133.33333333333331 0
This says to me the vivus is working, but when the code runs, nothing happens.
Please see the following very simple jsfiddle example:
https://jsfiddle.net/zzgnkwtn/2/
All I can thinks is that perhaps there is something wrong with my SVGs but seeing as it is picking up on the paths correctly this doesn't seem right.
Here is a link to the vivus documentation incase anyone wants to have a look, I am stumped.
https://github.com/maxwellito/vivus#vivusjs
Thanks in advance for any help.
EDIT
(changed title from 'SVG animation with vivus.js not working' to 'How to animate SVG fill and path in a similar style to vivus.js')
I have realised that the end goal I am trying to achieve isn't actually the above question, this is because I am new to SVG animation and started on the wrong path.
What I am more looking to achieve is something like the following tutorial:
https://medium.com/#gordonnl/stylised-line-animations-ded23320ffe5#.v6va1kc1w
The key difference and the main thing I am struggling is I want that exact type of animation but I want to animate the fill and the path, not just the path.
With help from #Alvin I have realised that some kind of clipPath or mask is required, I have tried to do this by following the above tutorial but this is the result I am getting:
http://codepen.io/anon/pen/dMeQLB
This seems to animate some of the paths but it is definitely not doing what I want.
Any help from anyone would be amazing!
FINAL RESULT
I finally managed to achieve what I was trying to do, most of the problem is that the SVG was not crated in the best way for animation in Illustrator, I needed to convert my object into a Compound Path so I could use it as clipPath and then have a simple path underneath which I can easily animate.
Demo: http://codepen.io/anon/pen/zqjyzX
See my demo, click on blue circle to restart animation.
var sign = new Vivus('mySign', {
type: 'scenario-sync',
duration: 20,
start: 'autostart',
dashGap: 20,
forceRender: false
},
function() {
if (window.console) {
console.log('Animation finished. [log triggered from callback]');
}
$('#mySign').find('path').attr('class', 'fill_path');
});
function runme() {
$('#mySign').find('path').attr('class', '');
sign.reset().play();
};
svg * {
stroke: blue;
stroke-width: 1;
fill: none;
}
.fill_path {
fill: blue;
}
<script src="//code.jquery.com/jquery-2.2.2.min.js"></script>
<script src="//cdn.jsdelivr.net/vivus/latest/vivus.min.js"></script>
<svg id="mySign" 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 100 100" enable-background="new 0 0 100 100" xml:space="preserve" >
<path data-duration="30" d="M27.9,9.6c0,0,0.2,1.1,0.4,2.7c0.1,0.4,0.2,1.1,0,1.6c-0.2,0.6-0.7,0.8-1.1,0.9c-0.4,0.1-1,0.1-1.3-0.2c-0.7,0.7-0.6,0.6-1.1,0.7c0,0-0.3,0.1-0.4,0.1c-0.1,0-0.2-0.1-0.3-0.1c-0.6,0.1-0.9-1-1-1.4c-0.3-1.4-0.6-2.8-0.9-4.2C22.2,9.5,22,9,22,8.9c-0.1-0.2-0.1-0.5-0.2-0.7c0.1,0,0.4-0.1,0.5-0.1c0.4-0.1,0.5,0.1,0.6,0.5c0.4,1.5,0.7,3,1.1,4.5c0.1,0.2,0.4,1.4,0.9,1.3c0.3-0.1,0.1-1.1,0.1-1.3c-0.2-0.8-0.3-1.6-0.5-2.4c-0.1-0.8-0.2-1.5-0.4-2.3c-0.2-0.7-0.1-0.8,0.3-0.9c0.2,0,0.6,0,0.6,0.2c0,0,0,0.4,0,0.4c0,0.2,0.1,0.4,0.1,0.6c0.3,1.4,0.6,2.9,0.8,4.3c0.1,0.4,0.6,1,1.1,0.9c0.4-0.1,0.1-2,0.1-2.1c0-0.2-0.1-0.4-0.1-0.6c0,0-0.1-1.3-0.2-2c-0.1-0.7-0.2-1.3-0.4-1.9C26.8,7.1,27.4,7,27.4,7c0.3-0.1,0.2,0.2,0.3,0.2C27.8,8,27.9,8.8,27.9,9.6z"></path>
<path data-duration="30" d="M32.3,9.4c-0.1,0-0.8,0.1-0.9,0.1c-0.3,0-0.6,0.1-0.8,0.1c0.2,1.1,0.4,2.2,0.6,3.2c0.4-0.1,0.9-0.2,1.3-0.2c0.1,0,0.3,0,0.4,0c0.1,0.1,0.2,0.7,0.3,0.9c0.1,0.4-0.1,0.3-0.4,0.4c-0.1,0-0.7,0.1-0.8,0.1c-0.5,0.1-1,0.2-1.5,0.3c-0.1-0.1-0.2-0.2-0.2-0.2c0-0.2,0-0.1,0-0.4c-0.2-1-0.4-2.1-0.6-3.1c-0.2-0.9-0.3-1.9-0.5-2.8C29.1,7.4,29,7.1,29,6.8c0.3-0.1,0.7-0.2,1.1-0.3c0.5-0.1,1-0.2,1.5-0.3c0.3-0.1,0.5,0.8,0.5,1c0,0-0.3,0.2-0.3,0.2c-0.4,0-0.8,0.1-1.2,0.1c-0.3,0.1-0.3,0-0.3,0.3c0,0,0.1,0.5,0.2,0.7c0.2,0.1,0.3,0,0.6,0c1.3-0.2,1.4-0.3,1.5,0.5C32.6,9.1,32.5,9.3,32.3,9.4z"></path>
<path data-duration="30" d="M36.6,13.2c-0.5,0.1-1.6,0.4-1.7,0.4c-0.2,0-0.2-0.1-0.3-0.2c-0.3-2.4-0.7-4.8-1-7.1c0,0-0.2-0.4,0.1-0.5c1.1-0.2,1.2-0.4,1.3,0.7c0,0.3,0.1,0.7,0.1,1c0,0.1,0,0.4,0,0.5c0.2,1.1,0.4,2.2,0.5,3.3c0.1,0.1,0,0.7,0.2,0.6c0.1,0,0.5-0.1,0.5-0.1c0.2,0,0.4,1.2,0.4,1.2C36.8,13,36.7,13.1,36.6,13.2z"></path>
<path data-duration="30" d="M39.4,6.9c-0.3,0.6-0.5,1.2-0.6,1.7c0,0.2-0.1,0.5-0.1,0.7c0,0.1,0,1,0,1.2c0.1,0.8,0.6,1.3,1.5,1.2c0.1,0,0.1,0,0.1,0c0.3,0,0.5,0.8,0.5,0.9c0,0.1-0.1,0.2-0.2,0.2c-0.1,0-0.1,0-0.2,0c-0.1,0-0.5,0-0.7-0.1c-0.1,0-0.3,0-0.3,0c-1.4-0.4-1.6-1.3-1.7-2c0,0-0.1-1-0.1-1.4c0-0.4,0.1-1.3,0.2-1.9c0-0.1,1-2.3,1.7-2.4c0.1,0,0.1,0,0.2,0.1c0.1,0.3,0.2,0.6,0.2,0.6C40.1,5.7,39.6,6.5,39.4,6.9z"></path>
<path data-duration="30" d="M43.9,12.4c-1.5,0.1-2.2-2.5-2.2-3.1c0-0.4-0.1-0.7-0.1-1.1c0-0.1,0.1-0.7,0.1-1c0.1-1.4,0.9-3,2.4-3.1c0.6-0.1,1.3,0.6,1.6,1.1c0.6,1,0.8,2.1,0.9,3.2C46.5,10.1,46.2,12.2,43.9,12.4z M44.5,5.7c-0.2-0.2-0.5-0.5-0.7-0.5c-1,0.1-1.2,3.1-1.1,3.6c0,0.2,0.3,2.7,1.4,2.6c0.3,0,0.7-0.4,0.9-0.7c0.5-0.8,0.5-1.6,0.4-2.3C45.3,7.7,45,6.2,44.5,5.7z"></path>
<path data-duration="30" d="M55.1,11.9c-0.2,0-0.4,0-0.6-0.1c-0.3-1.1-0.4-1.5-0.5-2.3c-0.2-0.9-0.4-1.9-0.6-2.8c0,0,0,0,0,0c-0.4,1.2-1,3.3-1.3,5c-0.1,0.1-0.9,0.3-1.1,0.3c-0.1,0-0.1,0-0.2,0c-0.4-1.3-0.9-2.9-1.3-4.3c-0.3,1.1-0.3,2.5-0.6,4.2c0,0-0.2,0.1-0.2,0.1c-0.4,0-0.2,0-1,0.1c0-0.1,0-0.1,0-0.2c0,0,0.1-0.9,0.1-1.4c0.1-0.8,0.4-3.4,0.5-4.8c0-0.1,0.1-0.3,0.1-0.5c0-0.1,0-1,0.3-1c0.3,0,1.1,0.1,1.1,0.1c0.5,1.5,0.9,3.1,1.6,5c0,0,0,0,0,0c0.5-1.7,1.1-3.5,1.4-4.4C52.8,4.6,53,4.3,53,4.1C53.1,4,53.8,4,53.9,4c0.4,0,0.5,1,0.5,1.4c0.1,0.3,0.2,0.7,0.2,1.1c0.1,0.3,0.2,0.9,0.2,1.3c0.1,0.6,0.3,1.4,0.4,2.2c0.1,0.4,0.4,1.4,0.4,1.5C55.8,11.9,55.2,11.8,55.1,11.9z"></path>
<path data-duration="30" d="M59.7,7.2c-0.1,0-0.8-0.1-0.9-0.1c-0.3,0-0.6,0-0.8,0c0,1.1,0,2.2,0,3.3c0.5,0,0.9,0,1.4,0c0.1,0,0.3,0,0.4,0.1c0,0.1,0.1,0.7,0.1,0.9c0,0.4-0.2,0.3-0.5,0.3c-0.1,0-0.7-0.1-0.8-0.1c-0.5,0-1,0-1.6,0c-0.1-0.1-0.1-0.2-0.1-0.3c0-0.2,0-0.1,0-0.4c0-1.1,0-2.1,0-3.2c0-0.9,0-1.9,0-2.8c0-0.3,0-0.6,0-0.9C57.3,4,57.7,4,58.1,4c0.5,0,1,0,1.5,0c0.3,0,0.3,0.9,0.3,1c0,0-0.3,0.1-0.4,0.1c-0.4,0-0.8-0.1-1.2-0.1C58.1,5,58,4.9,58,5.3c0,0,0,0.5,0,0.7c0.2,0.1,0.3,0.1,0.6,0.1C60,6.1,60,6,60,6.9C60,7,59.9,7.2,59.7,7.2z"></path>
<path data-duration="30" d="M68.6,5.4c-0.4,0-0.9-0.1-1.3-0.1c-0.1,1.9-0.2,3.8-0.4,5.7c0,0.1,0.1,0.6,0.1,0.7c0,0.1-0.1,0.2-0.1,0.2c-0.2,0-0.7,0-0.8,0c-0.1,0-0.2-0.1-0.3-0.1c-0.1-0.1-0.1-0.6-0.1-0.8c0.1-1.5,0.2-3.1,0.3-4.6c0.1-1.4,0.2-0.9-1.7-1.2c0-0.1-0.1-0.7-0.1-0.8c0,0,0-0.2,0.1-0.3c1.1,0.1,2.1,0.1,3.2,0.2c0.4,0,1.3,0.1,1.7,0.1c0,0.1,0.1,0.2,0,0.4C69.1,5.1,69.1,5.4,68.6,5.4z"></path>
<path data-duration="30" d="M71.6,12.5C70.1,12.3,69.9,9.6,70,9c0-0.4,0.1-0.7,0.1-1.1c0-0.1,0.2-0.7,0.2-1c0.3-1.4,1.4-2.8,2.9-2.6c0.6,0.1,1.2,0.9,1.4,1.4C74.9,6.8,75,7.9,74.8,9C74.7,10.6,73.9,12.7,71.6,12.5z M73.5,6c-0.1-0.2-0.3-0.6-0.6-0.7c-1-0.1-1.7,2.8-1.8,3.4c0,0.2-0.2,2.7,0.9,2.8c0.3,0,0.8-0.3,1-0.6c0.6-0.7,0.7-1.5,0.8-2.2C73.9,8.1,73.9,6.5,73.5,6z"></path>
<path data-duration="30" d="M83.6,7.2c-0.4-0.1-0.9-0.1-1.3-0.2c-0.3,1.9-0.6,3.8-0.9,5.6c0,0.1,0,0.6,0,0.7c0,0.1-0.1,0.1-0.1,0.2c-0.2,0-0.7,0-0.8,0c-0.1,0-0.2-0.1-0.3-0.2c0-0.1,0-0.6,0-0.8c0.2-1.5,0.5-3.1,0.7-4.6c0.2-1.4,0.3-0.8-1.6-1.3c0-0.1,0-0.7,0-0.8c0,0,0.1-0.2,0.1-0.3c1,0.2,2.1,0.3,3.1,0.5c0.4,0.1,1.3,0.2,1.6,0.3c0,0.1,0,0.3,0,0.4C84.1,7,84,7.3,83.6,7.2z"></path>
<path data-duration="30" d="M87.8,14.4c0,0.1-0.1,0.2-0.1,0.2c-0.1,0.1-0.8,0.1-1,0c-0.2,0-0.2-0.3-0.1-0.4c0.2-0.8,0.4-1.6,0.6-2.4c0.1-0.5,0.2-0.9,0.3-1.4c-0.4-0.1-0.9-0.2-1.3-0.2c-0.1,0.6-0.2,1.3-0.4,1.9c0,0,0,0.4,0,0.4c0,0.2-0.1,0.4-0.1,0.7c0,0.1,0,0.6-0.1,0.6c0,0.1-0.1,0.3-0.2,0.2c-0.1,0-0.6-0.1-0.6-0.1C84.2,14,84,14,84.3,13.4c0.4-2.1,0.7-4.2,1.1-6.2c0-0.2,0.1-0.5,0.1-0.7c0.1,0,0.3-0.1,0.3,0c0.1,0,0.2,0.1,0.3,0.1c0.1,0,0.1,0,0.1,0c0.1,0,0.4,0.1,0.4,0.2c0.1,0.5-0.1,1.2-0.2,1.8c0,0.2-0.1,0.5-0.1,0.8c0.2,0.1,0.4,0.1,0.5,0.2c0.9,0.2,0.9,0.2,1-0.3c0.1-0.3,0.1-0.5,0.2-0.8c0-0.1,0.1-0.9,0.2-1.3c0.1,0,0.3,0,0.4,0c0.2,0,0.8,0.2,0.6,0.6C88.7,9.9,88.3,12.1,87.8,14.4z"></path>
<path data-duration="30" d="M92.7,11.3c-0.1,0-0.8-0.2-0.8-0.3c-0.3-0.1-0.6-0.1-0.8-0.2c-0.2,1.1-0.5,2.1-0.7,3.2c0.4,0.1,0.9,0.2,1.3,0.3c0.1,0,0.3,0.1,0.4,0.2c0,0.1-0.1,0.7-0.1,0.9c-0.1,0.4-0.2,0.3-0.5,0.2c-0.1,0-0.7-0.2-0.7-0.2c-0.5-0.1-1-0.2-1.5-0.3c0-0.1-0.1-0.3-0.1-0.3c0-0.2,0.1-0.1,0.1-0.4c0.2-1,0.5-2.1,0.7-3.1c0.2-0.9,0.4-1.8,0.6-2.8c0-0.3,0.1-0.6,0.2-0.9c0.4,0,0.7,0.1,1.1,0.1c0.5,0.1,1,0.2,1.5,0.3c0.3,0.1,0.1,1,0.1,1.1c0,0-0.3,0-0.4,0c-0.4-0.1-0.8-0.2-1.1-0.3c-0.3-0.1-0.3-0.2-0.4,0.2c0,0-0.1,0.5-0.1,0.7c0.1,0.1,0.3,0.1,0.6,0.2c1.3,0.3,1.4,0.3,1.2,1.1C93.1,11.2,92.9,11.4,92.7,11.3z"></path>
<path transform="translate(-270,30)" data-duration="30" d="M324.6,61.2c16.6,0,29.5-12.9,29.5-29.5c0-16.6-12.9-29.5-29.5-29.5c-16.6,0-29.5,12.9-29.5,29.5C295.1,48.4,308,61.2,324.6,61.2z" onclick="runme();" />
</svg>

Animating growing/shrinking pie using SVG and Javascript

I want to create a growing pie animation using Javascript and SVG embedded in HTML. Input should be percentage and output should be an image. It should animate like this:
This should work like GUI mouse hold action feedback (user needs long press something). This is also why I can't use GIF animation as the timeout may vary.
I tried to make this in Inkscape and then reverse-engineer the XML but I don't understand it at all. There's a <path> node which has property d full of gibberish numbers:
d="m 250.78761,446.46564 a 28.183382,28.183382 0 0 1 -24.596,27.95413 28.183382,28.183382 0 0 1 -30.85751,-20.83773"
I assume these are some points of path. But can't I just make circle and mention percentage of how it's full? How are these points even generated?
This is what I played with:
body, html {
padding: 0px;
margin: 0px;
border: 1px solid grey;
}
svg {
/** to fit svg in the viewbox**/
max-height: 400px;
border: 1px solid black;
}
<svg class="test" viewBox="-20 -20 1000 1000">
<path
id="circle4146"
style="stroke:#61a4ff;stroke-width:15.00000095;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;"
sodipodi:type="arc"
sodipodi:cx="140.71873"
sodipodi:cy="446.46564"
sodipodi:rx="28.183382"
sodipodi:ry="28.183382"
sodipodi:start="0"
sodipodi:end="1.1720792"
sodipodi:open="true"
d="m 168.90212,446.46564 a 28.183382,28.183382 0 0 1 -17.24157,25.97267" />
</svg>
The sodipodi stuff is probably used by inkscape, changing it has no effect. I know that the d attribute describes complex path. What I really need is for someone to highlight me which points should be moved (using sin and cos I assume) to achieve desired effect.
Also I was unable to adjust the viewport to the circle. Apparently some of the coordinates are not X and Y.
Something like this? Here I am just calculating what the length of the circumference that the percentage represents. Then I give the circle a stroke dash pattern with that length and a big gap.
setCircleTo(70);
function setCircleTo(percent)
{
// Get the circle element via the DOM
var circle = document.getElementById('mycircle');
// Calculate the circle circumference via the circles 'r' attribute
var circumference = Math.PI * 2 * circle.r.baseVal.value;
// Calculate what <percent> of the circumference is
var adjustedLen = percent * circumference / 100;
// Set the circle's dashpattern one with a dash that length
circle.setAttribute('stroke-dasharray', adjustedLen+' '+circumference);
}
<svg class="test" viewBox="-20 -20 1000 1000">
<circle id="mycircle" cx="100" cy="75" r="50" stroke-width="30" fill="none" stroke="#61a4ff"/>
</svg>

How to set a External SVG color in HTML using CSS? [duplicate]

This question already has answers here:
Manipulating external svg file style properties with CSS
(4 answers)
Closed 8 months ago.
I am trying to use SVG on my web page.
But it's color is black.
So, I want it to be changed.
So, I have done-
.red_color_svg
{
color: red;
border: 5px solid currentColor;
fill: currentColor;
}
<object type="image/svg+xml" data="https://rawcdn.githack.com/encharm/Font-Awesome-SVG-PNG/master/black/svg/heart.svg" class="weather_icon red_color_svg circle"></object>
To import heart_border.svg file and make its color red. But it does not work as you see i the output.
Can anyone help me please to solve this?
Thank you very much in advance for helping.
CSS does not apply cross document and you've two documents here heart_border.svg and the container html document.
You need to include the CSS in heart_border.svg e.g. by adding a <link> element or an <xml-stylesheet> processing instruction or by adding it inline in that file via a <style> element.
Alternatively if you add the SVG inline in the html document itself so that you only have one document the CSS will then apply.
This thread is old but I wanted to share my solution, based on SVG filters. You just need to define a feColorMatrix filter as you want and apply it to the external image. See example below for more details.
Compatible with any browsers that can handle SVG.
<svg width="100%" height="100%" class="draggable">
<defs>
<filter id="customColor1">
<!-- Match hex color for #50A -->
<feColorMatrix
in="SourceGraphic"
type="matrix"
values="0 0 0 0 0.3333333333333333 0 0 0 0 0 0 0 0 0 0.6666666666666666 0 0 0 1 0"
></feColorMatrix>
</filter>
<filter id="customColor2">
<!-- Match hex color for #0F0 -->
<feColorMatrix
in="SourceGraphic"
type="matrix"
values="0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0"
></feColorMatrix>
</filter>
</defs>
<image href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/icon-bike-black.svg" width="50" height="50"></image>
<image href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/icon-bike-black.svg" filter="url(#customColor1)" width="50" height="50" x="100"></image>
<image href="https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/icon-bike-black.svg" filter="url(#customColor2)" width="50" height="50" x="200"></image>
</svg>
[BONUS]
// A little helper to generate matrix color from source and destination colors
// To easily dive in : https://codepen.io/jacobberglund/pen/ORNQAr
// To understand what's going on here read this article by A List Apart
// https://alistapart.com/article/finessing-fecolormatrix/
interface RgbColor {
/** Values are in percent (ex: 255,127,0,255 => 1,0.5,0,1) */
r: number;
g: number;
b: number;
a: number;
}
export class ColorMatrixHelper {
public static getMatrix(hexColor: string) {
const rgbColor: RgbColor = ColorMatrixHelper.hexToRgb(hexColor);
return ColorMatrixHelper.computeMatrixColor(rgbColor);
}
// Inspired by this answer : https://stackoverflow.com/a/5624139/11480016
private static hexToRgb(hex3or6): RgbColor {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
const hex6 = hex3or6.replace(shorthandRegex, (m, r, g, b) => r + r + g + g + b + b);
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i.exec(hex6);
const base = 1 / 255;
return result
? {
r: parseInt(result[1], 16) * base,
g: parseInt(result[2], 16) * base,
b: parseInt(result[3], 16) * base,
a: result[4] ? parseInt(result[4], 16) * base : 1,
}
: null;
}
private static computeMatrixColor(rgbColor: RgbColor): string {
let matrix;
if (rgbColor) {
// Ignore original colors and apply the new one
matrix =
`0 0 0 0 ${rgbColor.r} ` + // Red
`0 0 0 0 ${rgbColor.g} ` + // Green
`0 0 0 0 ${rgbColor.b} ` + // Blue
`0 0 0 ${rgbColor.a} 0`; // Alpha
} else {
// Identity (keep orignal colors)
matrix =
`1 0 0 0 0 ` + // Red
`0 1 0 0 0 ` + // Green
`0 0 1 0 0 ` + // Blue
`0 0 0 1 0`; // Alpha
}
return matrix;
}
}
With your current code you set the fill on the object element.
Instead, you need to set it on the svg element.
Something like this:
.red_color_svg svg {
fill: currentColor;
}
The problem is that you don't target the actual SVG element, you target the "SVG container". To be able to change the color of one of the elements inside the SVG you have to target that specific element.
E.g change the fill color of all paths in a SVG:
.weather_icon path {
fill: yellow;
}
If you want to make it easier to handle add class names to the different elements inside the svg.
<path class="my-class" ......... />
This will make it possible to target a specific element by its class:
.weather_icon .my-class {
fill:blue;
stroke:green;
}
Do you really need SVG to be external file? you might want to put svg locally once in document.
<div style="display: none">
<svg><g id="svg1"><path d="some exampe path"/></g></svg>
</div>
And link to it in several places
<svg viewBox="0 0 64 64"><use xlink:href="#svg1"></use></svg>
Than you can style every link separately

How to draw non-scalable circle in SVG with Javascript

I'm developing a map, in Javascript using SVG to draw the lines.
I would like to add a feature where you can search for a road, and if the road is found, a circle appears on the map.
I know i can draw a circle in SVG, but my problem is that, the size of the circle should not change depending on the zoom-level. In other words the circle must have the same size at all times.
The roads on my map have this feature, all i had to do was add
vector-effect="non-scaling-stroke"
to the line attributes..
A line looks like this.
<line vector-effect="non-scaling-stroke" stroke-width="3" id = 'line1' x1 = '0' y1 = '0' x2 = '0' y2 = '0' style = 'stroke:rgb(255,215,0);'/>
The circle looks like this.
<circle id = "pointCircle" cx="0" cy="0" r="10" stroke="red" stroke-width="1" fill = "red"/>
Is it possible to define the circle as "non-scaling" somehow?
It took me a while, but I finally got the math clean. This solution requires three things:
Include this script in your page (along with the SVGPan.js script), e.g.
<script xlink:href="SVGPanUnscale.js"></script>
Identify the items you want not to scale (e.g. place them in a group with a special class or ID, or put a particular class on each element) and then tell the script how to find those items, e.g.
unscaleEach("g.non-scaling > *, circle.non-scaling");
Use transform="translate(…,…)" to place each element on the diagram, not cx="…" cy="…".
With just those steps, zooming and panning using SVGPan will not affect the scale (or rotation, or skew) of marked elements.
Demo: http://phrogz.net/svg/scale-independent-elements.svg
Library
// Copyright 2012 © Gavin Kistner, !#phrogz.net
// License: http://phrogz.net/JS/_ReuseLicense.txt
// Undo the scaling to selected elements inside an SVGPan viewport
function unscaleEach(selector){
if (!selector) selector = "g.non-scaling > *";
window.addEventListener('mousewheel', unzoom, false);
window.addEventListener('DOMMouseScroll', unzoom, false);
function unzoom(evt){
// getRoot is a global function exposed by SVGPan
var r = getRoot(evt.target.ownerDocument);
[].forEach.call(r.querySelectorAll(selector), unscale);
}
}
// Counteract all transforms applied above an element.
// Apply a translation to the element to have it remain at a local position
function unscale(el){
var svg = el.ownerSVGElement;
var xf = el.scaleIndependentXForm;
if (!xf){
// Keep a single transform matrix in the stack for fighting transformations
// Be sure to apply this transform after existing transforms (translate)
xf = el.scaleIndependentXForm = svg.createSVGTransform();
el.transform.baseVal.appendItem(xf);
}
var m = svg.getTransformToElement(el.parentNode);
m.e = m.f = 0; // Ignore (preserve) any translations done up to this point
xf.setMatrix(m);
}
Demo Code
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Scale-Independent Elements</title>
<style>
polyline { fill:none; stroke:#000; vector-effect:non-scaling-stroke; }
circle, polygon { fill:#ff9; stroke:#f00; opacity:0.5 }
</style>
<g id="viewport" transform="translate(500,300)">
<polyline points="-100,-50 50,75 100,50" />
<g class="non-scaling">
<circle transform="translate(-100,-50)" r="10" />
<polygon transform="translate(100,50)" points="0,-10 10,0 0,10 -10,0" />
</g>
<circle class="non-scaling" transform="translate(50,75)" r="10" />
</g>
<script xlink:href="SVGPan.js"></script>
<script xlink:href="SVGPanUnscale.js"></script>
<script>
unscaleEach("g.non-scaling > *, circle.non-scaling");
</script>
</svg>
If you are looking for a fully static way of doing this, you might be able to combine non-scaling-stroke with markers to get this, since the markers can be relative to the stroke-width.
In other words, you could wrap the circles in a <marker> element and then use those markers where you need them.
<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000">
<marker id="Triangle"
viewBox="0 0 10 10" refX="0" refY="5"
markerUnits="strokeWidth"
markerWidth="4" markerHeight="3"
orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" />
</marker>
<path d="M 100 100 l 200 0" vector-effect="non-scaling-stroke"
fill="none" stroke="black" stroke-width="10"
marker-end="url(#Triangle)" />
<path d="M 100 200 l 200 0"
fill="none" stroke="black" stroke-width="10"
marker-end="url(#Triangle)" />
</svg>
The same can also be viewed and tweaked here. The svg spec isn't fully explicit about what should happen in this case (since markers are not in SVG Tiny 1.2, and vector-effect isn't in SVG 1.1). My current line of thinking was that it should probably affect the size of the marker, but it seems no viewers do that at the moment (try in a viewer that supports vector-effect, e.g Opera or Chrome).
Looks like some work was done in webkit (maybe related to this bug: 320635) and the new transform doesn't stick around when simply appended like that
transform.baseVal.appendItem
This seems to work better. Even works in IE 10.
EDIT: Fixed the code for more general case of multiple translate transformations in the front and possible other transformations after. First matrix transformation after all translates must be reserved for unscale though.
translate(1718.07 839.711) translate(0 0) matrix(0.287175 0 0 0.287175 0 0) rotate(45 100 100)
function unscale()
{
var xf = this.ownerSVGElement.createSVGTransform();
var m = this.ownerSVGElement.getTransformToElement(this.parentNode);
m.e = m.f = 0; // Ignore (preserve) any translations done up to this point
xf.setMatrix(m);
// Keep a single transform matrix in the stack for fighting transformations
// Be sure to apply this transform after existing transforms (translate)
var SVG_TRANSFORM_MATRIX = 1;
var SVG_TRANSFORM_TRANSLATE = 2;
var baseVal = this.transform.baseVal;
if(baseVal.numberOfItems == 0)
baseVal.appendItem(xf);
else
{
for(var i = 0; i < baseVal.numberOfItems; ++i)
{
if(baseVal.getItem(i).type == SVG_TRANSFORM_TRANSLATE && i == baseVal.numberOfItems - 1)
{
baseVal.appendItem(xf);
}
if(baseVal.getItem(i).type != SVG_TRANSFORM_TRANSLATE)
{
if(baseVal.getItem(i).type == SVG_TRANSFORM_MATRIX)
baseVal.replaceItem(xf, i);
else
baseVal.insertItemBefore(xf, i);
break;
}
}
}
}
EDIT2:
Chrome killed getTransformToElement for some reason, so the matrix needs to be retrieved manually:
var m = this.parentNode.getScreenCTM().inverse().multiply(this.ownerSVGElement.getScreenCTM());
It's discussed here and here
It looks like current browsers don't do the expected thing, so one needs to apply the inverse transform of the zoom (scale) on the contents of the <marker>, eg. transorm: scaleX(5) on the user of the <marker> etc. will need to be accompanied by a transform: translate(...) scaleX(0.2) inside the <pattern>, also factoring in possible x/y/width/height/transform-origin values inside the pattern if needed

Categories

Resources