SVG - Animation Problems - javascript

I have some shapes which I rotate using two buttons, one for clockwise and the other for anti-clockwise direction:
var svgNS = "http://www.w3.org/2000/svg";
function cwAnim(evt) {
if (window.svgDocument == null)
svgDoc = evt.target.ownerDocument;
addRotateTransform('someShape', 1, -1, 180);
addRotateTransform('shape1', 1, 1, 360);
addRotateTransform('shape2', 1, 1, 360);
}
function acwAnim(evt) {
if (window.svgDocument == null)
svgDoc = evt.target.ownerDocument;
addRotateTransform('someShape', 1, 1, 180);
addRotateTransform('shape1', 1, -1, 360);
addRotateTransform('shape2', 1, -1, 360);
}
function addRotateTransform(target_id, dur, dir, angle) {
var my_element = svgDoc.getElementById(target_id);
var a = svgDoc.createElementNS(svgNS, "animateTransform");
var bb = my_element.getBBox();
var cx = bb.x + bb.width / 2;
var cy = bb.y + bb.height / 2;
a.setAttributeNS(null, "attributeName", "transform");
a.setAttributeNS(null, "attributeType", "XML");
a.setAttributeNS(null, "type", "rotate");
a.setAttributeNS(null, "dur", dur + "s");
a.setAttributeNS(null, "repeatCount", "1");
a.setAttributeNS(null, "fill", "freeze");
a.setAttributeNS(null, "additive", "sum");
a.setAttributeNS(null, "accumulate", "sum");
a.setAttributeNS(null, "from", "0 " + cx + " " + cy);
a.setAttributeNS(null, "to", angle * dir + " " + cx + " " + cy);
my_element.appendChild(a);
a.beginElement();
}
<svg width="600px" height="600px" viewBox="0 0 1000 1000">
<circle stroke="#000000" stroke-miterlimit="10" cx="468.451" cy="474.385" r="350" />
<g id="someShape">
<circle fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" cx="468.451" cy="474.385" r="350" />
<path id="shape1" fill="#DC5A00" stroke="#000000" stroke-miterlimit="10" d="M692.373,317.798C692.373,418.425,606.244,500,500,500
c-106.245,0-192.374-81.575-192.374-182.202c0-100.629,86.128-182.204,192.374-182.204
C606.244,135.595,692.373,217.169,692.373,317.798z M553.391,220.34c-39.316,0-71.188,31.87-71.188,71.187
s31.871,71.187,71.188,71.187c39.314,0,71.186-31.87,71.186-71.187S592.705,220.34,553.391,220.34z" />
<path id="shape2" fill="#3DFF63" stroke="#000000" stroke-miterlimit="10" d="M577.368,647.034c0,69.738-56.913,126.271-127.119,126.271
c-70.206,0-127.119-56.533-127.119-126.271c0-69.737,56.913-126.271,127.119-126.271
C520.455,520.764,577.368,577.297,577.368,647.034z M466.352,617.373c-21.998,0-39.831,17.453-39.831,38.983
c0,21.529,17.833,38.982,39.831,38.982s39.831-17.453,39.831-38.982C506.182,634.826,488.349,617.373,466.352,617.373z" />
</g>
<g onclick="cwAnim(evt)">
<rect x="89.762" y="815.369" stroke="#000000" stroke-miterlimit="10" width="217.865" height="134.426" />
<text transform="matrix(1 0 0 1 127.4673 864.5491)" fill="#FFFFFF" font-family="'MyriadPro-Regular'" font-size="21">AntiClockwise</text>
</g>
<g onclick="acwAnim(evt)">
<rect x="692.373" y="815.369" stroke="#000000" stroke-miterlimit="10" width="194.11" height="134.426" />
<text transform="matrix(1 0 0 1 752.0576 869.467)" fill="#FFFFFF" font-family="'MyriadPro-Regular'" font-size="21">Clockwise</text>
</g>
</svg>
You can also see it on this CodePen: http://codepen.io/Daolagajao/pen/qdyBLo
When I click once, the shape animates fine. The problem arises when I click on the buttons in quick successions (double click, triple click, etc). The axis of rotation shifts and that is certainly not desirable.
Is there a way around this problem? Something like when i click on the next button, the previous animation in progress stops retaining the position and the new animation takes place.

By adding a busy variable you can indicate that you are currently animating and you shouldn't do any animations. You can do more with this, but it fixes the basic issue.
var svgNS = "http://www.w3.org/2000/svg";
var busy = false;
function animationDelegate(evt, direction){
/* Check if we are busy before doing anything.*/
/* If we are, stop executing this request, otherwise, set busy to true.*/
if(busy) return; else busy = true;
if(direction === 'cw') cwAnim(evt);
else if(direction === 'ccw') acwAnim(evt);
/* Your duration is 1 second, so 1000ms.*/
/* Set a timeout that resets busy.*/
setTimeout(function(){ busy = false}, 1000);
}
function cwAnim(evt){
if ( window.svgDocument == null)
svgDoc = evt.target.ownerDocument;
addRotateTransform('someShape', 1, -1, 180);
addRotateTransform('shape1', 1, 1, 360);
addRotateTransform('shape2', 1, 1, 360);
}
function acwAnim(evt){
if ( window.svgDocument == null)
svgDoc = evt.target.ownerDocument;
addRotateTransform('someShape', 1, 1, 180);
addRotateTransform('shape1', 1, -1, 360);
addRotateTransform('shape2', 1, -1, 360);
}
function addRotateTransform(target_id, dur, dir, angle){
var my_element = svgDoc.getElementById(target_id);
var a = svgDoc.createElementNS(svgNS, "animateTransform");
var bb = my_element.getBBox();
var cx = bb.x + bb.width/2;
var cy = bb.y + bb.height/2;
a.setAttributeNS(null, "attributeName", "transform");
a.setAttributeNS(null, "attributeType", "XML");
a.setAttributeNS(null, "type", "rotate");
a.setAttributeNS(null, "dur", dur + "s");
a.setAttributeNS(null, "repeatCount", "1");
a.setAttributeNS(null, "fill", "freeze");
a.setAttributeNS(null, "additive", "sum");
a.setAttributeNS(null, "accumulate", "sum");
a.setAttributeNS(null, "from", "0 "+cx+" "+cy);
a.setAttributeNS(null, "to", angle*dir+" "+cx+" "+cy);
my_element.appendChild(a);
a.beginElement();
}
<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" width="600px" height="600px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
<circle stroke="#000000" stroke-miterlimit="10" cx="468.451" cy="474.385" r="350" />
<g id="someShape">
<circle fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" cx="468.451" cy="474.385" r="350" />
<path id="shape1" fill="#DC5A00" stroke="#000000" stroke-miterlimit="10" d="M692.373,317.798C692.373,418.425,606.244,500,500,500
c-106.245,0-192.374-81.575-192.374-182.202c0-100.629,86.128-182.204,192.374-182.204
C606.244,135.595,692.373,217.169,692.373,317.798z M553.391,220.34c-39.316,0-71.188,31.87-71.188,71.187
s31.871,71.187,71.188,71.187c39.314,0,71.186-31.87,71.186-71.187S592.705,220.34,553.391,220.34z" />
<path id="shape2" fill="#3DFF63" stroke="#000000" stroke-miterlimit="10" d="M577.368,647.034c0,69.738-56.913,126.271-127.119,126.271
c-70.206,0-127.119-56.533-127.119-126.271c0-69.737,56.913-126.271,127.119-126.271
C520.455,520.764,577.368,577.297,577.368,647.034z M466.352,617.373c-21.998,0-39.831,17.453-39.831,38.983
c0,21.529,17.833,38.982,39.831,38.982s39.831-17.453,39.831-38.982C506.182,634.826,488.349,617.373,466.352,617.373z" />
</g>
<g onclick="animationDelegate(evt, 'ccw')">
<rect x="89.762" y="815.369" stroke="#000000" stroke-miterlimit="10" width="217.865" height="134.426" />
<text transform="matrix(1 0 0 1 127.4673 864.5491)" fill="#FFFFFF" font-family="'MyriadPro-Regular'" font-size="21">AntiClockwise</text>
</g>
<g onclick="animationDelegate(evt, 'cw')">
<rect x="692.373" y="815.369" stroke="#000000" stroke-miterlimit="10" width="194.11" height="134.426" />
<text transform="matrix(1 0 0 1 752.0576 869.467)" fill="#FFFFFF" font-family="'MyriadPro-Regular'" font-size="21">Clockwise</text>
</g>
</svg>

Related

SVG arc slider for range input

My goal is to design an arc slider which looks something like that
I have the following structure of the template
<svg width="500" height="300">
<path id="track" stroke="lightgrey" fill="transparent" stroke-width="20" d="
M 50 50
A 90 90 0 0 0 300 50
"/>
<path id="trackFill" fill="cyan" stroke-width="20" d="
M 50 50
A 90 90 0 0 0 [some dynamic value?] [some dynamic value?]
"/>
<circle id="knob" fill="lightblue" cx="[dynamic, initial - 50]" cy="[dynamic, initial - 50]" r="25"/>
</svg>
knob - the control which user is supposed to drag in order to change the value
track - the full arc of the slide
trackFill - the portion of the slider path before the knob
Is it possible to make trackFill cover the portion of the slider before the knob as it is being dragged along the slider curve? If so which APIs or CSS rules will help me to achieve such a result?
Is it something like this you are after?
let svg = document.getElementById("slider");
let trackFill = document.getElementById("trackFill");
let knob = document.getElementById("knob");
let isDragging = false;
let sliderDragOffset = {dx: 0, dy: 0};
let ARC_CENTRE = {x: 175, y: 50};
let ARC_RADIUS = 125;
let sliderValue = 0;
setSliderValue(sliderValue);
function setSliderValue(value)
{
// Limit value to (0..sliderMax)
let sliderMax = track.getTotalLength();
sliderValue = Math.max(0, Math.min(value, sliderMax));
// Calculate new position of knob
let knobRotation = sliderValue * Math.PI / sliderMax;
let knobX = ARC_CENTRE.x - Math.cos(knobRotation) * ARC_RADIUS;
let knobY = ARC_CENTRE.y + Math.sin(knobRotation) * ARC_RADIUS;
// Adjust trackFill dash patter to only draw the portion up to the knob position
trackFill.setAttribute("stroke-dasharray", sliderValue + " " + sliderMax);
// Update the knob position
knob.setAttribute("cx", knobX);
knob.setAttribute("cy", knobY);
}
knob.addEventListener("mousedown", evt => {
isDragging = true;
// Remember where we clicked on knob in order to allow accurate dragging
sliderDragOffset.dx = evt.offsetX - knob.cx.baseVal.value;
sliderDragOffset.dy = evt.offsetY - knob.cy.baseVal.value;
// Attach move event to svg, so that it works if you move outside knob circle
svg.addEventListener("mousemove", knobMove);
// Attach move event to window, so that it works if you move outside svg
window.addEventListener("mouseup", knobRelease);
});
function knobMove(evt)
{
// Calculate adjusted drag position
let x = evt.offsetX + sliderDragOffset.dx;
let y = evt.offsetY + sliderDragOffset.dy;
// Position relative to centre of slider arc
x -= ARC_CENTRE.x;
y -= ARC_CENTRE.y;
// Get angle of drag position relative to slider centre
let angle = Math.atan2(y, -x);
// Positions above arc centre will be negative, so handle them gracefully
// by clamping angle to the nearest end of the arc
angle = (angle < -Math.PI / 2) ? Math.PI : (angle < 0) ? 0 : angle;
// Calculate new slider value from this angle (sliderMaxLength * angle / 180deg)
setSliderValue(angle * track.getTotalLength() / Math.PI);
}
function knobRelease(evt)
{
// Cancel event handlers
svg.removeEventListener("mousemove", knobMove);
window.removeEventListener("mouseup", knobRelease);
isDragging = false;
}
<svg id="slider" width="500" height="300">
<g stroke="lightgrey">
<path id="track" fill="transparent" stroke-width="20" d="
M 50 50
A 125 125 0 0 0 300 50
"/>
</g>
<use id="trackFill" xlink:href="#track" stroke="cyan"/>
<circle id="knob" fill="lightblue" cx="50" cy="50" r="25"/>
</svg>
I've kept this code simple for clarity, but at the expense of some limitations.
It assumes there is only one slider per page. If you need more than that, you will have to keep the slider-specific values (eg sliderValue and, isDragging) separate. You could use data attributes for that. You would also need to switch from accessing the SVG elements via id attributes to another way (eg. class attributes), because id attributes must be unique on the page.
Here is a simple example:
const radius = 50;
const offsetX = 10;
const offsetY = 10;
// 0 <= pos <= 1
const setSliderPos = (svg, pos) => {
const angle = Math.PI * pos;
const x = offsetX + radius - Math.cos(angle) * radius;
const y = offsetY + Math.sin(angle) * radius;
svg.select('.knob').attr('cx', x).attr('cy', y);
svg.select('.first').attr('d', `M ${offsetX},${offsetY} A ${radius},${radius} 0 0 0 ${x},${y}`);
svg.select('.second').attr('d', `M ${x},${y} A ${radius},${radius} 0 0 0 ${offsetX + radius * 2},${offsetY}`);
}
setSliderPos(d3.select('#svg-1'), 0.3);
setSliderPos(d3.select('#svg-2'), 0.6);
setSliderPos(d3.select('#svg-3'), 1);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="svg-1" width="150" height="80">
<path class="first" stroke-width="5" stroke="lightblue" fill="none"/>
<path class="second" stroke-width="5" stroke="cyan" fill="none"/>
<circle class="knob" r="10" fill="lightblue"/>
</svg>
<svg id="svg-2" width="150" height="80">
<path class="first" stroke-width="5" stroke="lightblue" fill="none"/>
<path class="second" stroke-width="5" stroke="cyan" fill="none"/>
<circle class="knob" r="10" fill="lightblue"/>
</svg>
<svg id="svg-3" width="150" height="80">
<path class="first" stroke-width="5" stroke="lightblue" fill="none"/>
<path class="second" stroke-width="5" stroke="cyan" fill="none"/>
<circle class="knob" r="10" fill="lightblue"/>
</svg>
To mark the progress you can use stroke-dasharray with a percentage; for example
<g stroke="lightgrey">
<path id="track" fill="transparent" stroke-width="20"
stroke-dasharray="40% 60%"
d="M 50 50 A 125 125 0 0 0 300 50"/>
</g>
This will show 40% of the arc and hide 60% of the arc.
If you need to use two colors, for example the whole arc in grey and the progress in black, you need to use two arcs on top of one another; the one at the bottom would be the one you already have, and the one at the top would have a stroke in black and use stroke-dasharray as shown.

Getting the coordinates of a moving SVG Circle which is moving over a path set in animateMotion

I need to check the response of a user by tracking the mouse movement over a moving object (in this case a circle). If the mouse is not over the circle I need to calculate the offset by comparing the mouse coordinates and the circle coordinates.
But whenever I check the circle values, they are not changing and will stay on their initial value.
Here's a simple example:
function clickCircle() {
var circle = document.getElementById("circle");
console.log('baseVal x: ' + circle.cx.baseVal.value);
console.log('animVal x: ' + circle.cx.animVal.value);
}
<p>Click on the moving circle</p>
<svg width="1200" height="1200">
<circle id="circle" cx="60" cy="60" r="20" fill="green" onclick="clickCircle();">
<animateMotion id="ani" dur="10s" repeatCount="indefinite"
path="M20, 60 C20,
-50 180, 150 180,
60 C180-60 20,
150 20, 60 z" />
</circle>
</svg>
Does anybody have any idea on how to get the coordinates from a moving circle that is being animated with animateMotion?
You could drag in an animation Icon, and track its properties
Or with JavaScript you calculate its center x,y position with:
let {width,height} = circle.getBBox();
let {x,y} = circle.getBoundingClientRect();
x = x + width/2;
y = y + height/2;
Also read: https://schneide.blog/2018/03/05/some-tricks-for-working-with-svg-in-javascript/
note! this code below will forever add circle Nodes!
<style> svg { width: 300px } </style>
<svg viewBox="0 0 300 200">
<rect width="100%" height="100%" fill="lightgreen"></rect>
<text x=10 y="20">Click the circle!</text>
<circle id="circle" cx="40" cy="40" r="40" fill="green"
onclick="clickCircle(event)">
<animateMotion id="ani" dur="10s" repeatCount="indefinite" path="m20 40c0-110 160 90 160 0c0-120-160 90-160 0z" />
</circle>
<text id="position" x="200" y="20">21</text>
</svg>
<script>
function clickCircle(evt) {
point("gold");
}
function point(color) {
let circle = document.getElementById("circle");
let {width,height} = circle.getBBox();
let {x,y} = circle.getBoundingClientRect();
let c = document.createElementNS("http://www.w3.org/2000/svg", "circle");
x = x + width/2;
y = y + height/2;
c.setAttribute("cx", x);
c.setAttribute("cy", y);
c.setAttribute("r", color == "black" ? 3 : 6);
c.setAttribute("fill", color);
circle.parentNode.append(c);
position.innerHTML = `${~~x} , ${~~y}`;
}
setInterval(() => point("black"), 250);
</script>
Or try the JSFiddle: https://jsfiddle.net/dannye/ph705b49/
Dan was Authorware toch heel wat makkelijker...
Alles goed?

Javascript SVG curve change path attirbut C

Thank you in advance.
i try to move un SVG curve element in html using javascript.
I would like to change the path of my svg so that my blue curve transforms like the red curve but with the transition to see the displacement of the curve.
i understand how to get or create an element but i am not sure how to set the attribude 'd' like change every 'c' in path.
alert(document.getElementById('s3').getAttribute('d'));
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<svg viewBox="0 0 990 180" height="200" width="1100" id="mySVG">
<g>
<path id="s2" d="M 241,128 C 272,113 293,152 369,125 C 434,80 471,72 580,114 "
fill="none" stroke="red" stroke-width="5px" />
<path id="s3" d="M 241,128 C266,131 298,100 369,125 C 441,150 482,151 580,114 "
fill="none" stroke="blue" stroke-width="5px" />
</g>
</svg>
<script src="app.js"></script>
</body>
</html>
I hope I understand your question: If you want to animate from one curve to the other you can use SMIL animations.
Since the paths in your code have the same number and the same type of commands you can use an <animate> element to animate the d attribute.
The values attribute is a list of values separated with semicolons (;) The first and the last value is the d attribute of the curve. The second value is the d attribute of the other one.
In my code the duration of the animation is 5 seconds: dur="5s
<svg viewBox="235 80 350 70" width="300" id="mySVG">
<g>
<path id="s2" d="M 241,128
C 272,113 293,152 369,125
C 434,80 471,72 580,114" fill="none" stroke="red" stroke-width="5px">
</path>
<path id="s3" d="M 241,128
C266,131 298,100 369,125
C 441,150 482,151 580,114" fill="none" stroke="blue" stroke-width="5px">
<animate attributeName="d" attributeType="XML" values="M 241,128
C266,131 298,100 369,125
C 441,150 482,151 580,114;
M 241,128
C 272,113 293,152 369,125
C 434,80 471,72 580,114;
M 241,128
C266,131 298,100 369,125
C 441,150 482,151 580,114; " dur="5s" repeatCount="indefinite" />
</path>
</g>
</svg>
UPDATE
The OP is commenting:
can i make it in javascript?
Making it in javascript is more complicated. You will need to set an array of values and an array of target values and recalculate each value of the curve with every frame of the animation. Next comes an example that is animating the blue curve on click:
Please read the comments in the code.
//I've hard coded the values and the target array
//you may want to do it dimamicaly from the d attribute
let vals = [
["M", 241, 128],
["C", 272, 113, 293, 152, 369, 125],
["C", 434, 80, 471, 72, 580, 114]
];
let target = [
["M", 241, 128],
["C", 266, 131, 298, 100, 369, 125],
["C", 441, 150, 482, 151, 580, 114]
];
//the request animation id
let rid = null;
//build the memory array used for the animation
let memory = [];
for (let i = 0; i < vals.length; i++) {
memory[i] = [];
memory[i][0] = target[i].slice();
memory[i][1] = vals[i].slice();
}
function Frame() {
rid = window.requestAnimationFrame(Frame);
updateValues();
updatePath();
}
window.addEventListener("load", updatePath, false);
// I'm animating the curve on click
svg.addEventListener(
"mousedown",
function () {
// if there is an animation running stop it before start another one
if (rid) {
window.cancelAnimationFrame(rid);
rid = null;
}
//reverse the animation
for (let i = 0; i < memory.length; i++) {
memory[i].reverse();
target[i] = memory[i][1].slice();
}
//call the Frame function
Frame();
},
false
);
function updateValues() {
//a function to update all the values of the curve except the move to part that is not changing anyway
for (let i = 1; i < vals.length; i++) {
for (let j = 1; j < vals[i].length; j++) {
let dist = target[i][j] - vals[i][j];
let vel = dist / 10;
vals[i][j] += vel;
}
}
}
//a function to reset the value of the d attribute
function updatePath() {
let d = `M${vals[0][1]},${vals[0][2]}`;
for (let i = 1; i < vals.length; i++) {
d += `C${vals[i][1]},${vals[i][2]},${vals[i][3]},${vals[i][4]},${vals[i][5]},${vals[i][6]}`;
}
s3.setAttributeNS(null, "d", d);
}
svg{border:1px solid}
<svg id="svg" viewBox="235 80 350 70" width="300" id="mySVG">
<g>
<path id="s2" d="M 241,128
C 272,113 293,152 369,125
C 434,80 471,72 580,114" fill="none" stroke="red" stroke-width="5px">
</path>
<path id="s3" d="M 241,128
C266,131 298,100 369,125
C 441,150 482,151 580,114" fill="none" stroke="blue" stroke-width="5px">
</path>
</g>
</svg>

SVG Pan and Zoom not working

Trying to carry out this svg pan and zoom for two rectangles, I can't seem to get it to work. Really new at this but I was able to come up with this but it just doesn't seem to work . I have added my javascript code as well if anyone can seem t find what is wrong, i Used the group id for the elements to try to pan and zoom based on my js but it doesn't still work
<!DOCTYPE html>
<head>
</head>
<body>
<svg width="24cm" height="16cm" viewBox="-4 -1 26 20"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="myfield" transform="scale(1,1) translate(0,0)" >
<g transform=" scale(1,-1) translate(0,-16)"><g id="togglefield">
<rect id= "rectangle1" class= "rectangles" x= 2 y= 0 width= 4 height= 6 onClick="fieldDetails(event)"></rect>
<rect id="rectangle2" class= "rectangles" x= 2 y= 6 width= 4
height= 5 onClick="fieldDetails(event)"></rect>
</g>
<g id="togglefind">
<circle class="circles" id="find1" cx= 4 cy= 1 r="0.2" onClick="findDetails(event)"></circle>
<text class="text" x= 4 y= 1 transform="translate(0 2 ) scale(-1,1) rotate(180)"> 1 </text>
<circle class="circles" id="find5" cx= 4 cy= 7 r="0.2" onClick="findDetails(event)"></circle>
<text class="text" x= 4 y= 7 transform="translate(0 14 ) scale(-1,1) rotate(180)"> 5 </text>
</g>
</g>
<rect x="12.6" y="12.4" width="3" height="3" fill="white" stroke="grey" stroke-width="0.05"/>
<text x="13" y="13.2" font-family="Arial" font-size="0.6">Legend</text>
<g id="Fieldlgd">
<rect x="12.8" y="13.6" width="0.6" height="0.6" fill="none" stroke="green" stroke-width="0.04"/>
<text x="13.6" y="14" font-family="Georgia" font-size="0.5">Field ID</text>
<text x="13" y="14" font-family="Georgia" font-size="0.5">1</text>
</g><g id="Findlgd">
<circle cx="13.1" cy="14.7" r="0.12" fill="blue" stroke="red" stroke-width="0.05"/>
<text x="13.6" y="14.85" font-family="Georgia" font-size="0.5">Find ID</text>
<text x="13.2" y="14.7" font-family="Arial" font-size="0.45">1</text>
</g>
</g>
<rect x="-4" y="0.3" width="3.2" height="12" fill="white" stroke="black" stroke-width="0" fill-opacity="0.8"/>
<text x="-4" y="0.8" font-family="San Serif" font-size="3%">Pan and Zoom </text>
<text x="-2.7" y="1.8" font-family="Georgia" font-size="4%">N</text>
<polyline id="panup" onclick="panup_click(evt)" points="-3,2 -2.5,1 -2,2 -2.3,1.9 -2.7,1.9 -3,2" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-2.7" y="3.4" font-family="Georgia" font-size="4%">S</text>
<polyline id="pandown" onclick="pandown_click(evt)" points="-3,2.8 -2.5,3.8 -2,2.8 -2.3,2.9 -2.7,2.9 -3,2.8" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-3.3" y="2.6" font-family="Georgia" font-size="4%">W</text>
<polyline id="panleft" onclick="panleft_click(evt)" points="-2.7,1.9 -3.7,2.4 -2.7,2.9 -2.8,2.6 -2.8,2.2 -2.7,1.9" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-2.1" y="2.6" font-family="Georgia" font-size="4%">E</text>
<polyline id="panright" onclick="panright_click(evt)" points="-2.3,1.9 -1.3,2.4 -2.3,2.9 -2.2,2.6 -2.2,2.2 -2.3,1.9" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-2.7" y="2.6" font-family="Georgia" font-size="4%">C</text>
<circle id="recenter" onclick="recenter_click(evt)" cx="-2.5" cy="2.4" r="0.3" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<polyline id="zoomcross" points="-2.6,4.2 -2.4,4.2 -2.4,4.6 -2.1,4.6 -2.1,4.8 -2.4,4.8 -2.4,5.3 -2.6,5.3 -2.6,4.8 -2.9,4.8 -2.9,4.6 -2.6,4.6 -2.6,4.2" fill="cornflowerblue"/>
<rect id="zoomIn" onclick="zoomin_click(evt)" x="-3" y="4" width="1" height="1.5" fill="white" stroke="black" stroke-width="0.05" opacity="0.2"/>
<rect id="zoomdash" x="-2.9" y="5.9" width="0.8" height="0.2"
fill="cornflowerblue"/>
<rect id="zoomOut" onclick="zoomout_click(evt)" x="-3" y="5.7" width="1" height="0.6" fill="white" stroke="black" stroke-width="0.05" opacity="0.2"/>
</svg>
// Pan left function
function panleft_click(evt) {
var root = document.documentElement;
// variable to get the group transform object
var myfield = null;
myfield = document.getElementById("myfield");
// Incase the group has no Id then get the first '<g>' element
if(myfield === null)
myfield = document.getElementsByTagName('g')[0];
var goIn = evt.target; // thus line is currently not used
// Fetch the current transform state of the group element into a
var matrix = myfield.getCTM();
// Read the translate attributes from the matrix into variable x and y
// Below is the desired leftward change
var x = (matrix.e + 2) / matrix.a ; // Divide change by current scale to remove exponential movement
var y = matrix.f / matrix.a ; // To keep motion only along one axis
// Set the new transform attribute for the group
myfield.setAttributeNS(null,"transform", "scale(" + matrix.a + "," + matrix.d + ") translate(" + x + "," + y + ")");
}
// Pan right function
function panright_click(evt) {
// variable to get the group transform object
var myfield = null;
myfield = document.getElementById("myfield");
// Incase the group has no Id then get the first '<g>' element
if(myfield === null)
myfield = document.getElementsByTagName('g')[0];
var goIn = evt.target; // thus line is currently not used
// Fetch the current transform state of the group element into a matrix
var matrix = myfield.getCTM();
// Read the translate attributes from the matrix into variable x and y
// Below is the desired rightward change
var x = (matrix.e - 2) / matrix.a; // Divide change by current scale to remove exponential movement
var y = matrix.f / matrix.a; // To keep motion only along one axis
// Set the new transform attribute for the group
myfield.setAttributeNS(null,"transform", "scale(" + matrix.a + "," + matrix.d + ") translate(" + x + "," + y + ")");
}
// Pan up function
function panup_click(evt) {
// variable to get the group transform object
var myfield = null;
myfield = document.getElementById("myfield");
// Incase the group has no Id then get the first '<g>' element
if(myfield === null)
myfield = document.getElementsByTagName('g')[0];
var goIn = evt.target; // thus line is currently not used
// Fetch the current transform state of the group element into a matrix
var matrix = myfield.getCTM();
// Read the translate attributes from the matrix into variable x and y
var x = matrix.e / matrix.a ; // Divided by scale to keep motion only along one axis
// Below is the desired upward change
var y = (matrix.f + 2) / matrix.a; // Divide change by current scale to remove exponential movement
// Set the new transform attribute for the group
myfield.setAttributeNS(null,"transform", "scale(" + matrix.a + "," + matrix.d + ") translate(" + x + "," + y + ")");
}
// Pan down function
function pandown_click(evt) {
// variable to get the group transform object
var myfield = null;
myfield = document.getElementById("myfield");
// Incase the group has no Id then get the first '<g>' element
if(myfield === null)
myfield = document.getElementsByTagName('g')[0];
var goIn = evt.target; // thus line is currently not used
// Fetch the current transform state of the group element into a matrix
var matrix = myfield.getCTM();
// Read the translate attributes from the matrix into variable x and y
var x = matrix.e / matrix.a; // Divided by scale to keep motion only along one axis
// Below is the desired downward change
var y = (matrix.f - 2) / matrix.a; // Divide change by current scale to remove exponential movement
// Set the new transform attribute for the group
myfield.setAttributeNS(null,"transform", "scale(" + matrix.a + "," + matrix.d + ") translate(" + x + "," + y + ")");
}
// Zoom in (enlarge) function
function zoomin_click(evt) {
// variable to get the group transform object
var myfield = null;
myfield = document.getElementById("myfield");
// Incase the group has no Id then get the first '<g>' element
if(myfield === null)
myfield = document.getElementsByTagName('g')[0];
var goIn = evt.target; // thus line is currently not used
// Fetch the current transform state of the group element into a matrix
var matrix = myfield.getCTM();
// Read the scale attributes from the matrix into variable x and y
// then apply an increment by multiplying
var x = matrix.a * 1.2;
var y = matrix.d * 1.2;
// Set the new transform attribute for the group
myfield.setAttributeNS(null,"transform", "scale(" + x + "," + y + ") translate(" + matrix.e + "," + matrix.f + ")");
}
// Zoom out (reduce) function
function zoomout_click(evt) {
// variable to get the group transform object
var myfield = null;
myfield = document.getElementById("myfield");
// Incase the group has no Id then get the first '<g>' element
if(myfield === null)
myfield = document.getElementsByTagName('g')[0];
var goIn = evt.target; // thus line is currently not used
// Fetch the current transform state of the group element into a matrix
var matrix = myfield.getCTM();
// Read the scale attributes from the matrix into variable x and y
// then apply a reduction in scale by dividing
var x = matrix.a / 1.2;
var y = matrix.d / 1.2;
// Set the new transform attribute for the group
myfield.setAttributeNS(null,"transform", "scale(" + x + "," + y + ") translate(" + matrix.e + "," + matrix.f + ")");
}
// Recenter/reset function
function recenter_click(evt) {
// Reset the group to the original transform
document.getElementById("myfield").setAttributeNS(null,"transform", "scale(1,1) translate(0,0)");
}
getCTM is the wrong function to use for this purpose. It includes any transforms applied to the parent SVG due to the viewBox etc. You are just wanting to manipulate the transform attribute of the "myfield" group.
I would recommend just manipulating the tranform attribute using the predefined DOM functions.
You access the transform attribute DOM by using:
<myfield DOM object>.transform.baseVal
That returns a SVGTransformList object, which is basically an array containing two SVGTramnsform objects. One for the scale() part, and one for the transform() part. Once you have those objects you can just give them new values, and the SVG will update.
function panup_click(evt) {
pan(0,-2);
}
function pandown_click(evt) {
pan(0,2);
}
function panleft_click(evt) {
pan(-2,0);
}
function panright_click(evt) {
pan(2,0);
}
// Pan function
function pan(dx, dy) {
// variable to get the group transform object
var myfield = document.getElementById("myfield");
// Fetch the current value of the transform attribute
var transformList = myfield.transform.baseVal;
var translate = transformList.getItem(1);
// Adjust the translate transform item
var tx = translate.matrix.e;
var ty = translate.matrix.f;
translate.setTranslate(tx + dx, ty + dy);
}
// Zoom in (enlarge) function
function zoomin_click(evt) {
zoom(1.2);
}
// Zoom out (reduce) function
function zoomout_click(evt) {
zoom(1 / 1.2);
}
function zoom(scaleFactor)
{
// variable to get the group transform object
var myfield = document.getElementById("myfield");
// Fetch the current value of the transform attribute
var transformList = myfield.transform.baseVal;
var scale = transformList.getItem(0);
// Adjust the translate transform item
var s = scale.matrix.a;
scale.setScale(s * scaleFactor, s * scaleFactor);
}
// Recenter/reset function
function recenter_click(evt) {
// Reset the group to the original transform
document.getElementById("myfield").setAttributeNS(null,"transform", "scale(1,1) translate(0,0)");
}
<svg width="24cm" height="16cm" viewBox="-4 -1 26 20"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="myfield" transform="scale(1,1) translate(0,0)" >
<g transform=" scale(1,-1) translate(0,-16)">
<g id="togglefield">
<rect id= "rectangle1" class= "rectangles" x= 2 y= 0 width= 4 height= 6 onClick="fieldDetails(event)"></rect>
<rect id="rectangle2" class= "rectangles" x= 2 y= 6 width= 4 height= 5 onClick="fieldDetails(event)"></rect>
</g>
<g id="togglefind">
<circle class="circles" id="find1" cx= 4 cy= 1 r="0.2" onClick="findDetails(event)"></circle>
<text class="text" x= 4 y= 1 transform="translate(0 2 ) scale(-1,1) rotate(180)"> 1 </text>
<circle class="circles" id="find5" cx= 4 cy= 7 r="0.2" onClick="findDetails(event)"></circle>
<text class="text" x= 4 y= 7 transform="translate(0 14 ) scale(-1,1) rotate(180)"> 5 </text>
</g>
</g>
<rect x="12.6" y="12.4" width="3" height="3" fill="white" stroke="grey" stroke-width="0.05"/>
<text x="13" y="13.2" font-family="Arial" font-size="0.6">Legend</text>
<g id="Fieldlgd">
<rect x="12.8" y="13.6" width="0.6" height="0.6" fill="none" stroke="green" stroke-width="0.04"/>
<text x="13.6" y="14" font-family="Georgia" font-size="0.5">Field ID</text>
<text x="13" y="14" font-family="Georgia" font-size="0.5">1</text>
</g>
<g id="Findlgd">
<circle cx="13.1" cy="14.7" r="0.12" fill="blue" stroke="red" stroke-width="0.05"/>
<text x="13.6" y="14.85" font-family="Georgia" font-size="0.5">Find ID</text>
<text x="13.2" y="14.7" font-family="Arial" font-size="0.45">1</text>
</g>
</g>
<rect x="-4" y="0.3" width="3.2" height="12" fill="white" stroke="black" stroke-width="0" fill-opacity="0.8"/>
<text x="-4" y="0.8" font-family="San Serif" font-size="3%">Pan and Zoom </text>
<text x="-2.7" y="1.8" font-family="Georgia" font-size="4%">N</text>
<polyline id="panup" onclick="panup_click(evt)" points="-3,2 -2.5,1 -2,2 -2.3,1.9 -2.7,1.9 -3,2" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-2.7" y="3.4" font-family="Georgia" font-size="4%">S</text>
<polyline id="pandown" onclick="pandown_click(evt)" points="-3,2.8 -2.5,3.8 -2,2.8 -2.3,2.9 -2.7,2.9 -3,2.8" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-3.3" y="2.6" font-family="Georgia" font-size="4%">W</text>
<polyline id="panleft" onclick="panleft_click(evt)" points="-2.7,1.9 -3.7,2.4 -2.7,2.9 -2.8,2.6 -2.8,2.2 -2.7,1.9" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-2.1" y="2.6" font-family="Georgia" font-size="4%">E</text>
<polyline id="panright" onclick="panright_click(evt)" points="-2.3,1.9 -1.3,2.4 -2.3,2.9 -2.2,2.6 -2.2,2.2 -2.3,1.9" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<text x="-2.7" y="2.6" font-family="Georgia" font-size="4%">C</text>
<circle id="recenter" onclick="recenter_click(evt)" cx="-2.5" cy="2.4" r="0.3" fill="lavender" stroke="cornflowerblue" stroke-width="0.05" fill-opacity="0.6"/>
<polyline id="zoomcross" points="-2.6,4.2 -2.4,4.2 -2.4,4.6 -2.1,4.6 -2.1,4.8 -2.4,4.8 -2.4,5.3 -2.6,5.3 -2.6,4.8 -2.9,4.8 -2.9,4.6 -2.6,4.6 -2.6,4.2" fill="cornflowerblue"/>
<rect id="zoomIn" onclick="zoomin_click(evt)" x="-3" y="4" width="1" height="1.5" fill="white" stroke="black" stroke-width="0.05" opacity="0.2"/>
<rect id="zoomdash" x="-2.9" y="5.9" width="0.8" height="0.2" fill="cornflowerblue"/>
<rect id="zoomOut" onclick="zoomout_click(evt)" x="-3" y="5.7" width="1" height="0.6" fill="white" stroke="black" stroke-width="0.05" opacity="0.2"/>
</svg>

How to find the midPoint of arc in SVG with javascript

I want to the mid point of arc in svg.. Can any one tell formula to find the midbpoint of arc.,
The answer to that question is not straightforward. That's because in SVG arcs can be an elliptical arc. For example, what would you say is the midpoint on the following arc?
<svg width="200" height="200" viewBox="0 0 100 100">
<path fill="none" stroke="black" stroke-width="2"
d="M 20,75 A 21,50, 45, 1 1, 60,75"/>
</svg>
Anyway, without getting into complicated formulae, the simplest method is probably to take advantage of SVG's pointAtLength() method.
var myarc = document.getElementById("myarc");
// Get the length of the path
var pathLen = myarc.getTotalLength();
// How far along the path to we want the position?
var pathDistance = pathLen * 0.5;
// Get the X,Y position
var midpoint = myarc.getPointAtLength(pathDistance)
// For fun, let's add a dot at that position to mark it.
var svg = myarc.ownerSVGElement;
var circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", midpoint.x);
circle.setAttribute("cy", midpoint.y);
circle.setAttribute("r", "5");
circle.setAttribute("fill", "red");
svg.appendChild(circle);
<svg width="200" height="200" viewBox="0 0 100 100">
<path id="myarc" fill="none" stroke="black" stroke-width="2"
d="M 20,75 A 21,50, 45, 1 1, 60,75"/>
</svg>

Categories

Resources