How to draw SVG element in center of click? - javascript

User makes a click then gets coordinates (x, y). I try to draw the balloon svg in place of click:
The balloon has code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg 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"
viewBox="0 0 88 88" style="enable-background:new 0 0 88 88;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;}
.st1{fill:#007dbb;}
</style>
<g id="marker2">
<g>
<g>
<path class="st0" d="M74.8,84.5c-20.1,0-39.9,0-59.7,0c0-27.1,0-54.2,0-81.4c19.9,0,39.8,0,59.7,0C74.8,30.2,74.8,57.3,74.8,84.5
z"/>
</g>
<g>
<g>
<path class="st1" d="M68,31.3c0,11.2-7.6,20.7-17.8,23.5c-2,0.5-4.9,2.2-6.2,8.3c-1.6-6-4.3-7.8-6.3-8.3
C27.5,51.9,20,42.5,20,31.3C20,17.9,30.7,7,44,7C57.3,7,68,17.9,68,31.3z"/>
<path class="st1" d="M44,67c3.3,0,6,2.7,6,6s-2.7,6-6,6c-3.3,0-6-2.7-6-6S40.7,67,44,67z"/>
</g>
</g>
</g>
</g>
<g id="Layer_1_1_">
</g>
</svg>
It is SVG image.
First how to set width, height for this balloon and how to the point of ballooin in center of click (highlighted by red)?
I tried to set width and height like this:
let group = document.getElementById('marker2');
group.setAttribute("width", '30px');
group.setAttribute("height", '30px');
But it does now work, when I have tried to find a center of circle:
<path class="st1" d="M44,67c3.3,0,6,2.7,6,6s-2.7,6-6,6c-3.3,0-6-2.7-6-6S40.7,67,44,67z"/>
Could you explain me how to place ballooin in the center of click?
My idea is to get rectangle outbox of id="marker2" then find the center:
let rectSmallCircle = getRect('st1');
let box = getRect('marker2');
let height = box.height;
let centerWidth = box.width / 2;
let startPositionX = centerWidth;
let startPositionY = height - rectSmallCircle.height / 2;

Play with the positions
const svg = `<svg viewBox="0 0 88 88"><style>.st0{fill:none;}.st1{fill:#007dbb;}</style><g id="marker2"><path class="st0" d="M74.8,84.5c-20.1,0-39.9,0-59.7,0c0-27.1,0-54.2,0-81.4c19.9,0,39.8,0,59.7,0C74.8,30.2,74.8,57.3,74.8,84.5 z"/><path class="st1" d="M68,31.3c0,11.2-7.6,20.7-17.8,23.5c-2,0.5-4.9,2.2-6.2,8.3c-1.6-6-4.3-7.8-6.3-8.3 C27.5,51.9,20,42.5,20,31.3C20,17.9,30.7,7,44,7C57.3,7,68,17.9,68,31.3z"/><path class="st1" d="M44,67c3.3,0,6,2.7,6,6s-2.7,6-6,6c-3.3,0-6-2.7-6-6S40.7,67,44,67z"/></g></svg>`
const container = document.getElementById("svgContainer");
document.getElementById("lnk").addEventListener("click", function(e) {
e.preventDefault();
container.innerHTML = svg;
const dot = document.querySelectorAll(".st1")[1]
dot.setAttribute('style', 'fill: green');
const bbox = dot.getBBox();
const center = [bbox.x / 2 + bbox.height / 2, bbox.y / 2 + bbox.width / 2]
const clickX = e.clientX,
clickY = e.clientY;
document.getElementById("output").innerHTML = `${clickX},${clickY} - ${center}`
container.style.top = (clickX-100-center[0]+88)+"px"
container.style.left = (clickY-100-88)+"px"
})
#svgContainer {
height: 200px;
position:absolute;
}
svg {
height: 200px
}
<h1>Test svg</h1>
<p id="output"></p>
<p><br/></p>
<p><br/></p>
<div id="svgContainer"></div>
<p>Click the <a id="lnk" href="#">Link</a></p>

Related

SVG put circular text over a line

I has this clock and I need to put the numbers above as you can see:
I know the position of each line but if I create a text with x, y and angle like:
<text *ngFor="let line of lines; let index = i" [attr.rotate]="line.angle"
[attr.x]="line.x1" [attr.y]="line.y1" [id]="'text'+index">
<tspan class="number">20</tspan>
</text>
I get this:
How could I put the text exactly in middle of line and has circle text instead of this?
For each <text> set x = 0, y = -radius and transform='rotate(index * angle)':
const SECTORS = 16;
const RADIUS = 70;
const g = d3.select('g');
for (let i = 0; i < SECTORS; i++) {
g.append('text')
.text('20')
.attr('x', 0)
.attr('y', -RADIUS)
.attr('transform', `rotate(${i * 360 / SECTORS})`)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="200">
<g transform="translate(100,100)"/>
</svg>
In this example each <text> is the child of a <g> and all the <g>s are children of a container <g>. The container is translated to the center and each <text> has a negative value of -44 that moves the text out to the circle. All the <g>s are then rotated. The container is also rotated (-20) to rotate the entire thing in place.
const container = document.getElementById('container');
Object.keys([...Array(13)]).forEach(i => {
let t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
t.setAttribute('y', '-44');
t.textContent = i*5;
let g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
g.setAttribute('transform', `rotate(${i*20})`);
g.append(t);
container.append(g);
});
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="250">
<circle cx="50" cy="50" r="40" fill="none" stroke="black" stroke-width="1" />
<g id="container" transform="translate(50 50) rotate(-20)"
font-size="8" text-anchor="middle" dominant-baseline="text-bottom">
</g>
</svg>
The <text>s can also be horizontal by rotating them "back" after rotating the parent <g> (but you can see that they are more difficult to place with an equal distance from the circle):
const container = document.getElementById('container');
Object.keys([...Array(13)]).forEach(i => {
let t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
t.setAttribute('transform', `translate(0 -46) rotate(${-i*20+20})`);
t.textContent = i*5;
let g2 = document.createElementNS('http://www.w3.org/2000/svg', 'g');
g2.setAttribute('transform', `rotate(${i*20-20})`);
g2.append(t);
container.append(g2);
});
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="250">
<circle cx="50" cy="50" r="40" fill="none" stroke="black" stroke-width="1" />
<g id="container" transform="translate(50 50)"
font-size="8" text-anchor="middle" dominant-baseline="middle">
</g>
</svg>

How to scale a svg polygon dynamically to fit into 24X 24 size?

My sample code is
<svg width="24" height="24" viewBox="0 0 24 24" preserveAspectRatio="xMinYMin"
xmlns="http://www.w3.org/2000/svg">
<polygon points="150,75 258,137.5 258,262.5 150,325 42,262.6 42,137.5"/>
</svg>
the polygon points will be loaded from service and i want to resize and scale the polygon to fit into 24 x 24 area.
Thanks in advance
You need to use the values from the getBBox() function and update your viewBox with those.
var myPath = document.getElementById("p1")
var mySvg = document.getElementById("s1")
var b = myPath.getBBox();
mySvg.setAttribute("viewBox", [b.x, b.y, b.width, b.height].join(" "));
<svg width="24" height="24" id="s1">
<polygon id="p1" points="150,75 258,137.5 258,262.5 150,325 42,262.6 42,137.5"/>
</svg>
As your points are dynamic. You can dynamically calculate the polygon dimension to set the height and width of viewport
var myPath = document.getElementById("p1")
var w = myPath.getBoundingClientRect().width;
var h = myPath.getBoundingClientRect().height;
document.getElementById("s1").setAttribute("viewBox", "0 0 " + h + " " + w);
<svg width="24" height="24" id="s1"
xmlns="http://www.w3.org/2000/svg">
<polygon id="p1" points="150,75 258,137.5 258,262.5 150,325 42,262.6 42,137.5"/>
</svg>

How to turn the center of the X Y coordinates of a SVG object to the center of the plane?

I have a circle and a SVG plane:
<svg width="400" height="400">
<g class="point" transform="translate(6.5,1)">
<g class="delete-point" transform="translate(11,-11)"></g>
<circle></circle>
<text class="point-index" y="4"><tspan text-anchor="middle">0</tspan></text>
</g>
</svg>
And this is how I'm getting x and y of the circle:
d3.selectAll('.point')
.each(function() {
var c = d3.select(this)
var cX = d3.transform(c.attr('transform')).translate[0]
var cY = d3.transform(c.attr('transform')).translate[1]
list.push({ x: Math.round(cX), y: Math.round(cY), index: k++ })
}
Right now 0 of X and Y are in the top left corner. How to do it so that the 0 of X and Y are in the middle of the plane?
Note: .point is the circle and svg is the plane.
You can change the origin of the SVG file by using a translate transform:
<svg width="400" height="400">
<g transform="translate(200 200)">
<!-- coordinate (0,0) is now in the center of the SVG -->
<circle cx="0" cy="0" r="150" fill="red"/>
</g>
</svg>

SVG animation reduces width of separate vector in file

I've been messing around with SVG path animations. My SVG has 2 layers in it; a grey egg and an orange egg. The orange egg animates over the top of the grey egg, this part works fine. However, both paths technically have the same width, but when I animate the orange egg, the grey egg's path looks like it's reduced by 50%.
Below is an image and the code.
var path = document.querySelector('#egg-orange path');
var length = path.getTotalLength();
var percent = length/100;
var desiredPercent = 50;
var currentPercent = percent * (100 - desiredPercent); // 50%
// Clear any previous transition
path.style.transition = path.style.WebkitTransition =
'none';
// Set up the starting positions
path.style.strokeDasharray = length + ' ' + length;
path.style.strokeDashoffset = length;
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
path.getBoundingClientRect();
// Define our transition
path.style.transition = path.style.WebkitTransition =
'stroke-dashoffset 2s ease-in-out';
// Go!
path.style.strokeDashoffset = currentPercent;
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 104.7 144.3" enable-background="new 0 0 104.7 144.3" xml:space="preserve">
<g id="egg-grey">
<path fill="#FFFFFF" stroke="#414042" stroke-width="10" stroke-miterlimit="10" d="M99.3,92.2c0,25.7-20.8,46.5-46.5,46.5
S6.3,117.8,6.3,92.2S27.2,5.7,52.8,5.7S99.3,66.5,99.3,92.2z"/>
</g>
<g id="egg-orange">
<path fill="#FFFFFF" stroke="#F15A29" stroke-width="10" stroke-miterlimit="10" d="M99.3,92.2c0,25.7-20.8,46.5-46.5,46.5
S6.3,117.8,6.3,92.2S27.2,5.7,52.8,5.7S99.3,66.5,99.3,92.2z"/>
</g>
</svg>
I found out that the issue was because of the white fill on the orange egg. All i needed to do was add fill-opacity="0" and hey presto, all sorted!
<g id="egg-orange">
<path fill="#fff" fill-opacity="0" stroke="#F15A29" stroke-width="10" stroke-miterlimit="10" d="M99.3,92.2c0,25.7-20.8,46.5-46.5,46.5
S6.3,117.8,6.3,92.2S27.2,5.7,52.8,5.7S99.3,66.5,99.3,92.2z"/>
</g>
EDIT:
As Paul mentioned below, you can just set fill to none and not need to touch opacity.
<g id="egg-orange">
<path fill="none" stroke="#F15A29" stroke-width="10" stroke-miterlimit="10" d="M99.3,92.2c0,25.7-20.8,46.5-46.5,46.5
S6.3,117.8,6.3,92.2S27.2,5.7,52.8,5.7S99.3,66.5,99.3,92.2z"/>
</g>

SVG Path Overlay and Animate Out Another Path

I have an SVG of a dashed gray line. What I want to do is overlay that on top of a green SVG dashed line, and animate out the gray to reveal the green. Sorta like a meter moving from right to left.
I saw this example of how to make a dash line:
http://jsfiddle.net/ehan4/2/
and was able to do it but my line is already dashed.
I ended up doing this:
<svg 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"
viewBox="0 0 666.9 123.8" enable-background="new 0 0 666.9 123.8" xml:space="preserve">
<path opacity="0.4" stroke-width="3" fill="none" stroke="#66CD00" stroke-linecap="round" stroke-miterlimit="5" stroke-dasharray="1,6" d="
M656.2,118.5c0,0-320.4-251-645.9-0.7" />
<path id="top" opacity="0.4" fill="none" stroke="#AEAEAE" stroke-linecap="round" stroke-miterlimit="5" stroke-dasharray="1,6" d="
M656.2,118.5c0,0-320.4-251-645.9-0.7"/>
</svg>
var path = document.querySelector('#top');
var length = path.getTotalLength();
// Clear any previous transition
path.style.transition = path.style.WebkitTransition =
'none';
// Set up the starting positions
path.style.strokeDasharray = 1 + ' ' + 6;
path.style.strokeDashoffset = length;
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
path.getBoundingClientRect();
// Define our transition
path.style.transition = path.style.WebkitTransition =
'stroke-dashoffset 20s linear';
// Go!
path.style.strokeDashoffset = '0';
https://jsfiddle.net/ps5yLyab/
How can I overlay the two dash lines and animate out the gray?
You can do it with a clip path.
First we add a clipPath to the SVG.
<defs>
<clipPath id="myclip">
<rect id="cliprect" x="100%" y="0%" width="100%" height="100%"/>
</clipPath>
</defs>
This clip path is sized the same size as the SVG (width and height 100%) and starts with its x postion at the far right of the SVG (100%). So at the start it is not revealing anything.
Then every 10mS we reduce it's x coord by 1% (ie 100% -> 99% -> 98% etc). until it reached zero.
var cliprect = document.getElementById("cliprect");
var offsetX = 100;
var speed = 10;
function clipAdjust()
{
cliprect.setAttribute("x", offsetX+"%");
offsetX -= 1;
if (offsetX >= 0) {
window.setTimeout(clipAdjust, speed);
}
}
window.setTimeout(clipAdjust, speed);
Working demo below:
var path = document.querySelector('#top');
var length = path.getTotalLength();
// Clear any previous transition
path.style.transition = path.style.WebkitTransition =
'none';
// Set up the starting positions
path.style.strokeDasharray = 1 + ' ' + 6;
path.style.strokeDashoffset = length;
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
path.getBoundingClientRect();
// Define our transition
path.style.transition = path.style.WebkitTransition =
'stroke-dashoffset 20s linear';
// Go!
path.style.strokeDashoffset = '0';
var cliprect = document.getElementById("cliprect");
var offsetX = 100;
var speed = 10;
function clipAdjust()
{
cliprect.setAttribute("x", offsetX+"%");
offsetX -= 1;
if (offsetX >= 0) {
window.setTimeout(clipAdjust, speed);
}
}
window.setTimeout(clipAdjust, speed);
<svg 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"
viewBox="0 0 666.9 123.8" enable-background="new 0 0 666.9 123.8" xml:space="preserve">
<defs>
<clipPath id="myclip">
<rect id="cliprect" x="100%" y="0%" width="100%" height="100%"/>
</clipPath>
</defs>
<path opacity="0.4" fill="none" stroke="#AEAEAE" stroke-linecap="round"
stroke-miterlimit="5" stroke-dasharray="1,6" stroke-width="2"
d="M656.2,118.5c0,0-320.4-251-645.9-0.7"/>
<g clip-path="url(#myclip)">
<path stroke-width="3" fill="none" stroke="white"
stroke-linecap="round" stroke-miterlimit="5"
d="M656.2,118.5c0,0-320.4-251-645.9-0.7" />
<path id="top" opacity="0.4" stroke-width="3" fill="none" stroke="#66CD00"
stroke-linecap="round" stroke-miterlimit="5" stroke-dasharray="6,6"
d="M656.2,118.5c0,0-320.4-251-645.9-0.7" />
</g>
</svg>

Categories

Resources