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>
Related
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.
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>
I am trying to create a shape using an svg path that follows the outline of an svg polygon.
I need to set the d attribute of the svg path element because the size of the shape will be different depending on how much text there is in the <text /> element that is added to the document. I will have a number of these shapes but I'm just showing one here for brevity.
MY logic is to try and basically start at the middle, move down and right for 60 degrees, move back to the middle and move down left 60 degrees and then join up what is left of the box.
My shape is not quite right. There are a number of problems:
The shape is not in line with the point of the hexagon.
The lengths of each flat line or H that appears the diagonal line are not the same length.
There is a rogue line trying to join back to the point of the shape.
I need to add curves to the corners and I'm not sure how to do this.
const getPoint = ({
sides,
size,
center,
rotate,
side
}) => {
const degrees = (360 / sides) * side - rotate;
const radians = (Math.PI / 180) * degrees;
return {
x: center.x + size * Math.cos(radians),
y: center.y + size * Math.sin(radians)
};
};
const path = document.querySelector('path');
const gRef = document.querySelector('.hierarchy-label__container');
gRef.setAttribute('transform', 'translate(0, -40)');
const gbBox = gRef.getBBox();
let startingX = gbBox.x + gbBox.width / 2;
const startingY = gbBox.y + gbBox.height;
startingX = startingX - 0.7;
const [bottomRight, bottomLeft] = [1, 4].map((side) =>
getPoint({
sides: 6,
size: 30,
center: { x: startingX, y: startingY },
side,
rotate: 30
})
);
const bottomRightCoords = `${bottomRight.x} ${bottomRight.y}`;
path.setAttribute(
'd',
`M ${startingX} ${startingY} L ${bottomRightCoords} H ${gbBox.width} M ${startingX} ${startingY} L ${
bottomLeft.x
} ${bottomRight.y} H -${gbBox.width + 4} V -${gbBox.height} H ${gbBox.width} L ${gbBox.width} ${
bottomRight.y
} M ${startingX} ${startingY} Z`
);
.hierarchy-label__text {
fill: white;
text-anchor: middle;
font-size: 1.2rem;
}
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 990 759">
<g class="vx-group vx-cluster" transform="translate(100, 100)">
<g class="vx-group" transform="translate(0, 0)">
<g class="vx-group node-vertical__container" transform="translate(490, 0)">
<polygon points="25.98076211353316,-14.999999999999998 25.98076211353316,14.999999999999998 1.83697019872103e-15,30 -25.98076211353316,14.999999999999998 -25.980762113533157,-15.000000000000004 -5.510910596163089e-15,-30"></polygon>
<g class="vx-group node-vertical__business-unit" transform="translate(0, 0)">
<use xlink:href="#icon-BusinessUnit"></use>
</g>
<g class="hierarchy-label__container" transform="translate(0,-40)">
<text class="hierarchy-label__text" width="50" fill="white" x="0" y="0" text-anchor="middle" style="pointer-events: none;">
<tspan x="0" dy="0em">Finance</tspan>
</text>
<path></path>
</g>
</g>
</g>
</g>
</svg>
For the sake of clarity I've simplified a lot your code. Also: I've chosen to draw the hexagon in JavaScript in order to be able to use the hexagon vertices to draw the path where you put the text.
Please read code's comments
function drawHex(r){
// this function draws a hexagon with the center in 0,0
// and returns the array of points
// r is the radius of the circumscribed circle
let pointsRy = [];
let a = Math.PI/3;
for( let i = 0; i < 6; i++ ){
let aRad = (a*i) - Math.PI/2;
let Xp = parseFloat(r * Math.cos( aRad )).toFixed(3);
let Yp = parseFloat(r * Math.sin( aRad )).toFixed(3);
pointsRy.push({x:Xp,y:Yp,a:aRad});
}
// the points for the hexagon
let points = pointsRy.map(p => `${p.x}, ${p.y}`).join(" ");
hex.setAttributeNS(null,"points", points)
// the function returns the array of points
return pointsRy;
}
// ry: the array of points used to draw the hexagon: I'll be using the first & the second point to drae the textRect path
let ry = drawHex(30);
function drawTextPath(W,H){
// W: the width of the text "rectangle"
// H: the height of the text "rectangle"
// the textRect & the text art translated upwards (in y). Please see svg
let w = W/2 - (Number(ry[1].x) - Number(ry[0].x));
let d = `M${ry[0].x},${ry[0].y} L${ry[1].x},${ry[1].y} h${w} v-${H} h-${W} v${H} h${w}`;
textRect.setAttributeNS(null,"d",d)
}
drawTextPath(180,50)
svg{font-family: "Century Gothic",CenturyGothic,AppleGothic,sans-serif;}
text{fill:white; text-anchor:middle;pointer-events: none;}
<svg viewBox="-100 -70 200 200">
<polygon id="hex" />
<g transform="translate(0,-6)">
<path id="textRect" />
<text y="-40">
<tspan>Finance</tspan>
</text>
</g>
</svg>
Without any doubt there are other ways to draw this. Y hope you'll find my solution useful.
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
I'm trying to turn the following pattern...
...into a perfect sine wave pattern. Which control points should I use for it (how can I calculate them?)? Do I have to make the pattern wider also?
Here is how the above was generated:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="800" width="800">
<defs>
<!-- Geometry -->
<g>
<rect id="square" x="0" y="0" width="200" height="200" />
</g>
<!-- Patterns -->
<pattern id="wave" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 0 7.5 Q 2.5 7.5 2.5 5 Q 2.5 2.5 5 2.5 Q 7.5 2.5 7.5 5 Q 7.5 7.5 10 7.5" stroke="black" fill="transparent" stroke-width="1" stroke-linecap="square" stroke-linejoin="miter" />
</pattern>
</defs>
<!-- Graphics -->
<use xlink:href="#square" transform="translate(000, 000)" fill="url(#wave)"/>
</svg>
Demo to play with is here: http://jsfiddle.net/uEULF/
Thank you for the help.
Use the example here in this answer by Asad : How to draw sine waves with SVG (+JS)?
Here is the demonstration : http://jsfiddle.net/HyTad/
var svg = document.getElementById('sine_wave').children[0];
var origin = { //origin of axes
x: 100,
y: 100
};
var amplitude = 10; // wave amplitude
var rarity = 1; // point spacing
var freq = 0.1; // angular frequency
var phase = 20; // phase angle
for (var i = -100; i < 1000; i++) {
var line = document.createElementNS("http://www.w3.org/2000/svg", "line");
line.setAttribute('x1', (i - 1) * rarity + origin.x);
line.setAttribute('y1', Math.sin(freq * (i - 1 + phase)) * amplitude + origin.y);
line.setAttribute('x2', i * rarity + origin.x);
line.setAttribute('y2', Math.sin(freq * (i + phase)) * amplitude + origin.y);
line.setAttribute('style', "stroke:black;stroke-width:1");
svg.appendChild(line);
}