Change svg fill color onScroll - html

How can i change the fill color of an SVG logo onScroll; ¿thats posible with html or any css property?
The color change when you scroll when on another DIV

The idea is to use mix-blend-mode: differencein CSS together with isolation: isolate; for the group.
It's up to you how you want to move the layers. I'm using an input type range for this. You may use scroll or wheel.
I hope this helps.
percent.addEventListener("input",()=>{
let val = ~~(percent.value);
let _var = map(100-val,0,100,3,27);
txt.textContent = val+"%";
pth.setAttributeNS(null,"d",`M3,27H27V${_var}H3z`)
})
function map(n, a, b, _a, _b) {
let d = b - a;
let _d = _b - _a;
let u = _d / d;
return _a + n * u;
}
svg{border:1px solid; font-size:10px; background:lightblue}
[type="range"]{width:150px;}
<svg viewBox="0 0 30 30" width="150">
<g style="isolation: isolate;">
<path d="M3,27H27V3H3z" fill="white" />
<path id="pth" d="M3,27H27V15H3z" />
<text id="txt" x="15" y="15" dominant-baseline="middle" text-anchor="middle" fill="white" style="mix-blend-mode: difference;">50%</text>
</g>
</svg>
<p><input id="percent" type="range" value="50" /></p>

Related

Conditionally change fill colour on SVG Polygon

I have an SVG with about 60 different polygons that I need to change colour based on the temperature value that's being passed in, yet Html.Raw isn't inserting the text I need it to when it compiles.
Here's what I have in the view.
<svg id="_mapa" class="dragger" xmlns="http://www.w3.org/2000/svg">
<polygon class="polyArea"
onclick="window.location.href='#Url.Action("Red","Home")'"
name="13" points="1345,955 1345,1080 1875,1080 1875,1015 1935,1015 1935,955"
stroke="black" stroke-width="5"
#if ((int)ViewBag.Thirteen >= 100) { Html.Raw("fill='#E74C3C'"); } //red
else if ((int)ViewBag.Thirteen > 80 && (int)ViewBag.Thirteen < 100) { Html.Raw("fill='#F4D03F'"); } //yellow
else { Html.Raw("fill='#2ECC71'"); }
opacity="0.5" style="cursor: pointer;">
<title>Cuiseur a Vapeur
Current Temperature: #ViewBag.Thirteen °F</title>
</polygon>
</svg>
But this is what it's returning on the actual page.
<svg id="_mapa" class="dragger" xmlns="http://www.w3.org/2000/svg">
<polygon class="polyArea" name="13"
onclick="window.location.href='/Home/Red'"
points="1345,955 1345,1080 1875,1080 1875,1015 1935,1015 1935,955" stroke="black"
stroke-width="5" opacity="0.5" style="cursor: pointer;">
<title>Cuiseur a Vapeur
Current Temperature: 135 °F</title></polygon>
</svg>
As you can see, the fill property isn't getting inserted at all, is this even possible?
After banging my head on my desk for a while, I figured it out:
<svg id="_mapa" class="dragger" xmlns="http://www.w3.org/2000/svg">
<polygon class="polyArea"
onclick="window.location.href='#Url.Action("Red","Home")'"
name="13" points="1345,955 1345,1080 1875,1080 1875,1015 1935,1015 1935,955"
stroke="black" stroke-width="5"
#if ((int)ViewBag.Thirteen >= 100) { #Html.Raw("fill='#E74C3C'"); } //red
else if ((int)ViewBag.Thirteen > 80 && (int)ViewBag.Thirteen < 100) { #Html.Raw("fill='#F4D03F'"); } //yellow
else { #Html.Raw("fill='#2ECC71'"); }
opacity="0.5" style="cursor: pointer;">
<title>Cuiseur a Vapeur
Current Temperature: #ViewBag.Thirteen °F</title>
</polygon>
</svg>
I was missing the '#' before the Html.Raw inside the if statements.

SVG - Inline position element after text

I want to position SVG several elements inline inside a single SVG.
I have the following:
<svg>
<g>
<use width="28" height="28"class="cls-11"></use>
<text id="policyRectText" transform="translate(50,20)" class="cls-54">Runs on a small number of endpoints</text>
<circle r="15" stroke-width="4" transform="translate(250,15)" class="cls-8"></circle>
</g>
</svg>
So to position an element before dynamic text is easy, but how can i position after it?
This is how I would do it:
First I'm putting the <text> in a <g> element since it's transformed and I need to get the bounding box:
let bb = theTransformedText.getBBox();
Once I have the position and the size of the text (bb) I'm using the data to set the cx and the cy attributes for the circle.
I've commented out the <use> element since it has no xlink:href attribute.
let bb = theTransformedText.getBBox();
let r = parseFloat(theCircle.getAttribute("r"));// the circle's radius.
let sw = parseFloat(theCircle.getAttribute("stroke-width"));// the stroke width
theCircle.setAttributeNS(null, "cx", bb.x + bb.width + r + sw/2);
// assuming that the font size is 16 you need to offset the circle half font size
theCircle.setAttributeNS(null, "cy", bb.y + 8);
<svg viewBox="0 -50 400 100">
<g>
<!--<use width="28" height="28"class="cls-11"></use>-->
<g id="theTransformedText">
<text id="policyRectText" transform="translate(50,20)" class="cls-54">Runs on a small number of endpoints</text>
</g>
<circle id="theCircle" r="15" stroke-width="4" class="cls-8"></circle>
</g>
</svg>

Svg Spray chart

Actually I am new to SVG. I need's to draw baseball spray chart as have in below.Please help how to get this exactly and each section dynamically varies.Is it possible to draw using HTML CSS.Thanks.
UPDATE
I'm updating the question with the OP's comments that are important:
I forget to tell you it is a baseball ground. Consider batter on PA and we are showing batter hit location in percentage around the ground in a game.
There are a few steps:
you draw the individual paths
you draw the paths for the text (text on a path)
you draw the text
let R = 200;// the outer radius
let r = 120;// the middle radius
let objects = [];// an array for the paths
class pathObj{
constructor(a1,a2,R,r,path,text){
this.a1 = a1; //starting angle
this.a2 = a2; // ending angle
this.R = R;//the outer radius
this.r = r;// the middle radius
this.rm = r + 2*(R - r)/3; // the radius for the text path
this.path = document.querySelector(`#${path}`);
this.textpath = document.querySelector(`#p${path}`);
this.textElement = document.querySelector(`#t${path}`);
this.textPath = document.querySelector(`#t${path} textPath`);
this.textPath.textContent = text;
// points to draw the paths
this.p1 = {
x:this.R*Math.cos(this.a1),
y:this.R*Math.sin(this.a1)
};
this.p2 = {
x:this.R*Math.cos(this.a2),
y:this.R*Math.sin(this.a2)
}
this.p3 = {
x:this.r*Math.cos(this.a2),
y:this.r*Math.sin(this.a2)
}
this.p4 = {
x:this.r*Math.cos(this.a1),
y:this.r*Math.sin(this.a1)
}
this.p5 = {
x:this.rm*Math.cos(this.a1),
y:this.rm*Math.sin(this.a1)
}
this.p6 = {
x:this.rm*Math.cos(this.a2),
y:this.rm*Math.sin(this.a2)
}
}
draw(){
// the d attribute for the main path
let d = `M${this.p1.x},${this.p1.y}
A${this.R},${this.R} 0 0 1 ${this.p2.x},${this.p2.y}
L${this.p3.x},${this.p3.y}
A${this.r},${this.r} 0 0 0 ${this.p4.x},${this.p4.y}
Z`;
// the d attribute for the text path
let d1 = `M${this.p5.x},${this.p5.y}
A${this.R},${this.R} 0 0 1 ${this.p6.x},${this.p6.y}`
this.path.setAttributeNS(null,"d",d);
this.textpath.setAttributeNS(null,"d",d1);
}
}
// create the objects and push into the objects array
objects.push(new pathObj(0,Math.PI/6,R,r,"a","11%"));
objects.push(new pathObj(Math.PI/6,Math.PI/3,R,r,"b","18%"));
objects.push(new pathObj(Math.PI/3,Math.PI/2,R,r,"c","23%"));
objects.push(new pathObj(0,Math.PI/8,r,0,"d","3%"));
objects.push(new pathObj(Math.PI/8,Math.PI/4,r,0,"e","3%"));
objects.push(new pathObj(Math.PI/4,3*Math.PI/8,r,0,"f","29%"));
objects.push(new pathObj(3*Math.PI/8,Math.PI/2,r,0,"g","13%"));
objects.forEach(o=>o.draw())
svg{border:1px solid }
path{stroke:black; fill:transparent;}
text{ text-anchor:middle;}
<svg viewBox="-200 -250 400 250">
<defs>
<path id="pa" />
<path id="pb" />
<path id="pc" />
<path id="pd" />
<path id="pe" />
<path id="pf" />
<path id="pg" />
</defs>
<g transform="rotate(-135)">
<path id="a" /><text id="ta"><textPath startOffset="50%" xlink:href="#pa"></textPath></text>
<path id="b" /><text id="tb"><textPath startOffset="50%" xlink:href="#pb"></textPath></text>
<path id="c" /><text id="tc"><textPath startOffset="50%" xlink:href="#pc"></textPath></text>
<path id="d" /><text id="td"><textPath startOffset="50%" xlink:href="#pd"></textPath></text>
<path id="e" /><text id="te"><textPath startOffset="50%" xlink:href="#pe"></textPath></text>
<path id="f" /><text id="tf"><textPath startOffset="50%" xlink:href="#pf"></textPath></text>
<path id="g" /><text id="tg"><textPath startOffset="50%" xlink:href="#pg"></textPath></text>
</g>
</svg>

SVG grid overlaid image

I'm not even sure how to explain this.
I have a floor layout that is in a JPG format, I would like to enable user to mouseOver or click on different area of the map to get dynamically information about it. To get different area of the map interactive, I was thinking I could overlaid a dynamically SVG grid on top/or behind it, that way, I can assign the dynamic data to a grid coordinates.
I am having problem with implementing this, I can't get the grid and the image to resize with each other. Also, since the SVG grid is dynamic, when the image is bigger, by default, the my grid has more lines which throws the coordinates off when zoom in/out.
Is there away to generate a fixed grid (e.g., 20x10) matched and overlaid it to the image. Make it zoom with the image? I just got started with SVG today so any help would be greatly appreciated.
Code in Angular2
<svg attr.width="{{w}}" attr.height="{{h}}" id="mySVG">
<image xlink:href="../asset/building.jpg" x="0" y="0" height="100%" width="100%"/>
<rect x="0" y="0" attr.width="{{w}}" attr.height="{{h}}" stroke="black" fill="url(#GridPattern)" stroke-width="5"
class="hello"/>
<defs>
<pattern id="GridPattern" x="0" y="0" attr.width="{{wGap}}" attr.height="{{hGap}}" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" attr.x2="{{wGap}}" y2="0" stroke="lightblue" stroke-width="1px" />
<line x1="0" y1="0" x2="0" attr.y2="{{hGap}}" stroke="lightblue" stroke-width="1px" />
</pattern>
</defs>
<g id="group1" fill="red">
<!--<rect x="1cm" y="1cm" width="1cm" height="1cm"/>
<rect x="3cm" y="1cm" width="1cm" height="1cm"/>-->
<text *ngFor="let h of loc" attr.x="{{h.x*wGap}}" attr.y="{{h.y*hGap}}" fill="red" text-anchor='middle'>{{h.text}},h:{{h.y*hGap}},w:{{h.x*wGap}}</text>
<text *ngFor="let x of xNum; let i = index" attr.x="{{i*wGap}}" y="20" fill="red" style="writing-mode: tb; glyph-orientation-vertical: 0;
letter-spacing: -3;" >{{i}}</text>
<text *ngFor="let x of yNum; let i = index" x="20" attr.y="{{i*hGap}}" fill="red">{{i}}</text>
</g>
</svg>
import {Component, OnInit,NgZone} from '#angular/core';
#Component({
selector: 'home',
template: require('./home.html')
})
export class Home implements OnInit{
bol:boolean = true;
w:number;
h:number;
wGap:number;
hGap:number;
hMultiplier:number = 30;
wMultiplier:number = 40;
yNum = new Array(this.hMultiplier);
xNum = new Array(this.wMultiplier);
hLine:any;
wLine:any;
loc:Object[] = [{x:37,y:3,text:"testing 1"},
{x:31,y:25,text:"testing 2"},
{x:2,y:9,text:"testing 3"},
{x:35,y:8,text:"testing 4"},
{x:22,y:10,text:"testing 5"}]
constructor(ngZone:NgZone){
console.log(this.xNum,this.yNum)
console.log(window.innerHeight, window.innerWidth);
this.w = window.innerWidth-50;
this.h = window.innerHeight-200;
this.wGap = Math.ceil(this.w/this.wMultiplier);
this.hGap = Math.ceil(this.h/this.hMultiplier);
//////
//this.calculateLocation();
window.onresize = (e)=>{
ngZone.run(()=>{
console.log(window.innerWidth,window.innerHeight);
//this.w = document.getElementById('bldImg')['width'];
//this.h = document.getElementById('bldImg')['height'];
this.w = window.innerWidth-50;
this.h = window.innerHeight-200;
this.wGap = Math.ceil(this.w/this.wGap);
this.hGap = Math.ceil(this.h/this.hGap);
//////
// this.calculateLocation();
console.log(this.wGap)
console.log(this.hGap)
});
}
}
ngOnInit(){
}
}
I managed to do it using svg tag and create my own interactive map using Angular version 4 you can view the plnkr in this link - Please view the code
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 2000 800">
<svg:path
*ngFor="let country of countries; let i = index"
[attr.data-index]="i"
(mouseover)="countryHover = country.country_country"
(click)="clicked(i)"
class="blob"
[class.selected]="hasCountry(i)"
[attr.d]="country.country_path"
fill="currentColor">
</svg:path>
</svg>
Image of the Output incase if Plunker doesnt work
https://plnkr.co/edit/2heVmd7l4UokqR2OcNgm?p=preview

React - html tags inside svg

I am making circle menu, so I use SVG to create a circle, and now I want to show a link with some image inside of part of the circle. How i can do it? My code -
render(){
return(
<svg id={"menuLevel" + index} width={200} height={200}>
<path fill="white" stroke="rgba(0,0,0,0.2)" strokeWidth="2" d={"M"+width+","+width+" L"+previousX+", "+previousY+" A"+width+","+width+" 0 0,0 "+x+", "+y+" z"}></path>
</svg>
)
}
I tried something like this -
<path fill="white" stroke="rgba(0,0,0,0.2)" strokeWidth="2" d={"M"+width+","+width+" L"+previousX+", "+previousY+" A"+width+","+width+" 0 0,0 "+x+", "+y+" z"}>
<foreignobject x="120" y="120" width="180" height="180">
<Link ...><Image .../></Link>
</foreignobject>
</path>
But it doesn't work, this foreign object have still 0 width and 0 height and content doesn't show.
UPDATE
I need to assign link component to all path objects
<svg id={"menuLevel" + index} width={width*2+2} height={width*2+2}>
{arr.map(function(item){
let angleInRadians = -item * Math.PI / 180.0;
let previousX = x;
let previousY = y;
x = width + width * Math.cos(angleInRadians);
y = width + width * Math.sin(angleInRadians);
return(
<path fill="white" stroke="rgba(0,0,0,0.2)" strokeWidth="2" d={"M"+width+","+width+" L"+previousX+", "+previousY+" A"+width+","+width+" 0 0,0 "+x+", "+y+" z"}>
</path>
)
})}
</svg>
Please check it here JSFiddle. Use image element to add the image to SVG: https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_Image_Tag
<svg width="5cm" height="4cm" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<circle x="0" y="0" r="200"></circle>
<image xlink:href="https://www.google.co.uk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" x="0" y="0" height="200px" width="200px"/>
</svg>
Please note:
If you do not set the x or y attributes, they will be set to 0.
If you do not set the height or width attributes, they will be set to 0.
Having a height or width attribute of 0 will disable rendering of the image.
Update 1
Here is a working example to add a React component together with the image: JSFiddle. But I make the Link component as a sibling of the SVG, and then using absolute to position them. Not a perfect solution.
Update 2
To make a path clickable: JSFiddle.
Update 3
This is an image with clickable paths, integrated with ReactJS: JSFiddle:
var Link = React.createClass({
render: function() {
return <a className="link" href={this.props.href} target="_blank">{this.props.children}</a>
}
});
var Hello = React.createClass({
render: function() {
return <div id="container"><svg xmlns="http://www.w3.org/2000/svg" width="300px" height="300px">
<Link href="http://www.google.com">
<g transform="translate(100, 100)"><image href="https://www.google.co.uk/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" x="0" y="0" height="200px" width="200px"/></g>
</Link>
<Link href="http://www.facebook.com">
<g><image href="https://www.facebook.com/images/fb_icon_325x325.png" x="0" y="0" height="100px" width="100px"/></g>
</Link>
</svg></div>
}
});