I have to adjust a SVG's position and scale using DIV having handles.
Everything is working as expected except SVG is jumping the first time at the time of dragging the div handles. Only the SVG is jumping.
See here that the class "selection" is a DIV controlling the selected SVG with attribute ("[selection=true]")
$( ".selector" ).resizable({
aspectRatio: false,
handles: {
'nw': '#nwgrip',
'ne': '#negrip',
'sw': '#swgrip',
'se': '#segrip' },
resize: function(event, ui) {
console.log(ui.size.width);
$('#posW').text('Width: ' + Math.round(ui.size.width) );
$('#posH').text('Height: ' + Math.round(ui.size.height) );
$("[selection=true]").attr("width", Math.round(ui.size.width) );
$("[selection=true]").attr("height", Math.round(ui.size.height) );
$("[selection=true]").attr("x", Math.round(ui.position.left) );
$("[selection=true]").attr("y", Math.round(ui.position.top) );
}
}).draggable({
drag: function(event, ui) {
var offset = $(this).offset();
var xPos = offset.left;
var yPos = offset.top;
$("[selection=true]").attr("x", Math.round(xPos) );
$("[selection=true]").attr("y", Math.round(yPos) );
$('#posX').text('x: ' + xPos);
$('#posY').text('y: ' + yPos);
}
}
);
Please check jsfiddle link
Is this a bug that can be fixed?
Here's a working version of your original. It handles all clicking, dragging, and selector box behaviour within the "canvas" SVG.
It uses pure JS and manipulates the badge <svg> and selector box elements using DOM methods.
Hopefully it is fairly easily to follow what is going on.
let selectedBadge = null;
let isDraggingRect = null;
let isDraggingHandle = null;
let dragOffsetX = 0;
let dragOffsetY = 0;
// Event handlers for the badges
let badges = document.querySelectorAll(".canvas > .badge");
badges.forEach(b => b.addEventListener("click", select));
// Event handlers for selector box and handles
let selectorRect = document.querySelector("#selector rect");
selectorRect.addEventListener("mousedown", selectorMouseDown);
let handleNW = document.getElementById("nwgrip");
let handleNE = document.getElementById("negrip");
let handleSW = document.getElementById("swgrip");
let handleSE = document.getElementById("segrip");
let grips = document.querySelectorAll("#selector > circle");
grips.forEach(g => {
g.addEventListener("mousedown", gripMouseDown);
});
// Attach mousemove and mouseup events to SVG for dragging purposes
// We attach to the parent SVG because mouse events on small elements
// will be missed if you move the mouse outside the element.
let canvasSVG = document.querySelector("#svg_obj > .canvas");
canvasSVG.addEventListener("mousemove", mouseMove);
canvasSVG.addEventListener("mouseup", mouseUp);
// select a badge
function select(evt) {
hideSelector(selectedBadge);
selectedBadge = evt.target.ownerSVGElement;
showSelector(selectedBadge)
}
function showSelector(badge) {
setSelectorDimensionsTo({x: badge.x.baseVal.value,
y: badge.y.baseVal.value,
width: badge.width.baseVal.value,
height: badge.height.baseVal.value});
document.getElementById("selector").classList.add("show");
}
function hideSelector(badge) {
if (selectedBadge) {
document.getElementById("selector").classList.remove("show");
selectedBadge = null;
}
}
function setSelectorDimensionsTo(bounds) {
selectorRect.x.baseVal.value = bounds.x;
selectorRect.y.baseVal.value = bounds.y;
selectorRect.width.baseVal.value = bounds.width;
selectorRect.height.baseVal.value = bounds.height;
handleNW.cx.baseVal.value = bounds.x;
handleNW.cy.baseVal.value = bounds.y;
handleNE.cx.baseVal.value = bounds.x + bounds.width;
handleNE.cy.baseVal.value = bounds.y;
handleSW.cx.baseVal.value = bounds.x;
handleSW.cy.baseVal.value = bounds.y + bounds.height;
handleSE.cx.baseVal.value = bounds.x + bounds.width;
handleSE.cy.baseVal.value = bounds.y + bounds.height;
}
function moveSelectorTo(x, y) {
selectorRect.x.baseVal.value = x;
selectorRect.y.baseVal.value = y;
let w = selectorRect.width.baseVal.value;
let h = selectorRect.height.baseVal.value;
handleNW.cx.baseVal.value = x;
handleNW.cy.baseVal.value = y;
handleNE.cx.baseVal.value = x + w;
handleNE.cy.baseVal.value = y;
handleSW.cx.baseVal.value = x;
handleSW.cy.baseVal.value = y + h;
handleSE.cx.baseVal.value = x + w;
handleSE.cy.baseVal.value = y + h;
}
function moveSelectedBadgeTo(x, y) {
selectedBadge.x.baseVal.value = x;
selectedBadge.y.baseVal.value = y;
}
function selectorMouseDown(evt) {
isDraggingRect = selectedBadge;
let mousePos = mouseCoordsToSVGCoords(evt.offsetX, evt.offsetY);
dragOffsetX = mousePos.x - selectedBadge.x.baseVal.value;
dragOffsetY = mousePos.y - selectedBadge.y.baseVal.value;
}
function mouseUp(evt) {
isDraggingRect = null;
isDraggingHandle = null;
}
// Handles both:
// - dragging selector rect
// - dragging selector grip/handle
function mouseMove(evt) {
if (isDraggingRect)
{
// Move selector
let mousePos = mouseCoordsToSVGCoords(evt.offsetX, evt.offsetY);
moveSelectorTo(mousePos.x - dragOffsetX, mousePos.y - dragOffsetY);
// Move badge
moveSelectedBadgeTo(mousePos.x - dragOffsetX, mousePos.y - dragOffsetY);
}
else if (isDraggingHandle)
{
gripMouseMove(evt);
}
}
// Convert page mouse coords to SVG coords.
// Takes into account any scaling due to the presence of a viewBox.
function mouseCoordsToSVGCoords(mouseX, mouseY) {
var pt = canvasSVG.createSVGPoint();
pt.x = mouseX;
pt.y = mouseY;
return pt.matrixTransform(canvasSVG.getScreenCTM().inverse());
}
function gripMouseDown(evt) {
isDraggingHandle = evt.target;
let mousePos = mouseCoordsToSVGCoords(evt.offsetX, evt.offsetY);
dragOffsetX = mousePos.x - isDraggingHandle.cx.baseVal.value;
dragOffsetY = mousePos.y - isDraggingHandle.cy.baseVal.value;
}
function gripMouseUp(evt) {
isDraggingHandle = null;
}
function gripMouseMove(evt) {
// Move handle thus resizing selector
let mousePos = mouseCoordsToSVGCoords(evt.offsetX, evt.offsetY);
mousePos.x -= dragOffsetX;
mousePos.y -= dragOffsetY;
let bounds = {};
let oldX = selectorRect.x.baseVal.value;
let oldY = selectorRect.y.baseVal.value;
let oldW = selectorRect.width.baseVal.value;
let oldH = selectorRect.height.baseVal.value;
switch (isDraggingHandle.id) {
case "nwgrip":
bounds = {x: mousePos.x, y: mousePos.y, width: oldX + oldW - mousePos.x, height: oldY + oldH - mousePos.y};
break;
case "negrip":
bounds = {x: oldX, y: mousePos.y, width: mousePos.x - oldX, height: oldY + oldH - mousePos.y};
break;
case "swgrip":
bounds = {x: mousePos.x, y: oldY, width: oldX + oldW - mousePos.x, height: mousePos.y - oldY};
break;
case "segrip":
bounds = {x: oldX, y: oldY, width: mousePos.x - oldX, height: mousePos.y - oldY};
break;
}
setSelectorDimensionsTo(bounds);
// Resize badge
resizeBadgeTo(bounds);
}
function resizeBadgeTo(bounds) {
selectedBadge.x.baseVal.value = bounds.x;
selectedBadge.y.baseVal.value = bounds.y;
selectedBadge.width.baseVal.value = bounds.width;
selectedBadge.height.baseVal.value = bounds.height;
}
.canvas {
background-color: #cecece;
border: 2px solid #cecece;
width: 400px;
height: 400px;
position: relative;
}
#svg_obj {
position: absolute;
width: 100px;
height: 100px;
background: yellow;
}
#selector {
display: none;
}
#selector.show {
display: block;
}
#selector rect {
fill: transparent;
stroke: #f50;
stroke-width: 1px;
}
#nwgrip, #negrip, #swgrip, #segrip, #ngrip, #egrip, #sgrip, #wgrip {
fill: #ffffff;
stroke: #000000;
stroke-width: 1px;
}
<div id='svg_obj'>
<svg class="canvas" viewBox="0 0 400 400" width="400" height="400" >
<svg class="badge" width="200" height="200" viewBox="0 0 400 400">
<g>
<polygon fill="#21574B" points="342.6,0 324.1,19.8 324.1,294.2 302.3,308.3 200,374.1 97.7,308.3 75.8,294.2 57.3,308.3 200,400
342.6,308.3 "/>
<polygon fill="#578677" points="342.6,0 324.1,19.8 75.8,19.8 75.8,294.2 57.3,308.3 57.3,0 "/>
<polygon fill="#8CB2B0" points="324.1,19.8 324.1,294.2 302.3,308.3 200,374.1 97.7,308.3 75.8,294.2 75.8,19.8 "/>
</g>
</svg>
<svg class="badge" width="200" height="200" viewBox="0 0 400 400">
<g>
<polygon fill="#F1F74B" points="342.6,0 324.1,19.8 324.1,294.2 302.3,308.3 200,374.1 97.7,308.3 75.8,294.2 57.3,308.3 200,400
342.6,308.3 "/>
<polygon fill="#578677" points="342.6,0 324.1,19.8 75.8,19.8 75.8,294.2 57.3,308.3 57.3,0 "/>
<polygon fill="#8CB2B0" points="324.1,19.8 324.1,294.2 302.3,308.3 200,374.1 97.7,308.3 75.8,294.2 75.8,19.8 "/>
</g>
</svg>
<svg class="badge" width="200" height="200" viewBox="0 0 400 400">
<g>
<polygon fill="#81579B" points="342.6,0 324.1,19.8 324.1,294.2 302.3,308.3 200,374.1 97.7,308.3 75.8,294.2 57.3,308.3 200,400
342.6,308.3 "/>
<polygon fill="#578677" points="342.6,0 324.1,19.8 75.8,19.8 75.8,294.2 57.3,308.3 57.3,0 "/>
<polygon fill="#8CB2B0" points="324.1,19.8 324.1,294.2 302.3,308.3 200,374.1 97.7,308.3 75.8,294.2 75.8,19.8 "/>
</g>
</svg>
<g id="selector">
<rect width="20" height="20"/>
<circle cx="0" cy="0" r="5" id="nwgrip"/>
<circle cx="20" cy="0" r="5" id="negrip"/>
<circle cx="0" cy="20" r="5" id="swgrip"/>
<circle cx="20" cy="20" r="5" id="segrip"/>
</g>
</svg>
</div>
var data =
[{"seq":"1","start":"Account","end":"Order","relation":"Account","rows":"1"},
{"seq":"2","start":"Account","end":"Attachment","relation":"Parent","rows":"10"}
,{"seq":"3","start":"Order","end":"Account","relation":"Account","rows":"15"}
,{"seq":"4","start":"Attachment","end":"Account","relation":"Parent","rows":"55"}
,{"seq":"5","start":"Attachment","end":"Campaign","relation":"Parent","rows":"45"}
,{"seq":"6","start":"Attachment","end":"Lead","relation":"Parent","rows":"47"}
,{"seq":"7","start":"Lead","end":"Attachment","relation":"Parent","rows":"75"}
,{"seq":"8","start":"Campaign","end":"Attachment","relation":"Parent","rows":"34"},
{"seq":"9","start":"Order","end":"Account","relation":"Account","rows":"99"}
,{"seq":"10","start":"Attachment","end":"Account","relation":"Parent","rows":"12"}
,{"seq":"11","start":"Attachment","end":"Campaign","relation":"Parent","rows":"5"}
,{"seq":"12","start":"Attachment","end":"Lead","relation":"Parent","rows":"75"}];
var ellipseSelected, pathSelected, parentNodeX, parentNodeY, relationshipName, indexEdge, fromData, toData, nodeSelected, startNodeSelected;
//flag =1 ,when we have both src and trg
var flag = 1;
var newCount = 0;
var edges = d3.selectAll('.edge');
var path = d3.selectAll('.path')
var allEllipse = d3.selectAll('ellipse');
var allNodes = d3.selectAll('.node');
var theGraph = document.getElementById('graph0') //getContainer
var polygon = document.getElementsByTagName('polygon')[0] //getPolygon to insert after
var allEdgesJS = document.getElementsByClassName("edge"); //select all Edges
for (var i = 0; i < allEdgesJS.length; i++) { //Loop through edges to move
theGraph.insertBefore(allEdgesJS[i], polygon.nextSibling); //insert after polygon
}
function ellipseAdd() {
d3.select(ellipseSelected.parentNode)
.append("circle")
.attr('cx', parentNodeX) //thisParentBBox.left + thisParentBBox.width/2)
.attr('cy', parentNodeY)
.attr("r", 10)
.attr("stroke-width", 1)
.attr("stroke", "white")
.style('fill', '#CE2029');
d3.select(ellipseSelected.parentNode)
.data([toData])
.append("text")
.attr('x', parentNodeX - 8)
.attr('y', parentNodeY + 4).text(0).style('fill', 'white')
.attr("font-size", "8px")
.transition()
.duration(3000)
.tween("text", function(d) {
var i = d3.interpolate(fromData, d),
prec = (d + "").split("."),
round = (prec.length > 1) ? Math.pow(10, prec[1].length) : 1;
return function(t) {
this.textContent = Math.round(i(t) * round) / round;
};
});
}
function blinker() {
if (flag == 0) {
//for adding ellipse and text to it
ellipseAdd();
} else {
//blink 3 things\
//ellipse
ellipseAdd();
d3.select('#' + indexAndEdge[indexEdge].id + ' path').style('opacity', 1)
.transition().style('stroke', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('stroke-width', 1)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('stroke-width', 1)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('stroke-width', 1)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('stroke-width', 1).duration(300).style('opacity', 1)
.transition().style('stroke', "#ff800e").style('stroke-width', 1); //select current id from array //select current id from array
d3.select('#' + indexAndEdge[indexEdge].id + ' polygon')
.transition().style('stroke', 'grey').style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029').style('stroke', '#CE2029').style('stroke-width', 2)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029').style('stroke', '#CE2029').style('stroke-width', 2)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('fill', '#CE2029').style('stroke-width', 2)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('fill', 'grey').style('stroke-width', 1).duration(300).style('opacity', 1)
.transition().style('stroke', "#ff800e").style('fill', "#ff800e").style('stroke-width', 1); //select current id from array
d3.select('#' + indexAndEdge[indexEdge].id + ' text').style('opacity', 0)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029')
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029')
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029').style('fill', '#CE2029')
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', "#ff800e");
}
}
edges.style('opacity', 1);
allNodes.style('fill', "white");
path.style('fill', "yellow");
var indexAndEdge = [];
var countOnNode = [];
edges.each(function(d, i) {
var thisEdgeCount = this.id.substring(4);
debugger
indexAndEdge.push({ //push index you are at, the edge count worked out above and the id
index: i,
count: thisEdgeCount,
id: this.id,
start: String(this.childNodes[0].childNodes[0].nodeValue).split("->")[0],
destination: String(this.childNodes[0].childNodes[0].nodeValue).split("->")[1],
relation: this.childNodes[6].childNodes[0]
})
d3.select('#' + indexAndEdge[i].id + ' polygon').style('fill', 'grey').style('stroke', 'grey');
d3.select('#' + indexAndEdge[i].id + ' path').style('stroke', 'grey');
});
d3.selectAll('.node').each(function(d, i) {
var thisNodeCount = this.id;
debugger
countOnNode.push({ //push index you are at, the edge count worked out above and the id
id: thisNodeCount,
prevData: 0,
incrementData: 0,
title: this.childNodes[0].childNodes[0].nodeValue,
name: String(this.childNodes[4].childNodes[0].nodeValue)
})
});
function timer() {
setTimeout(function(d) {
if (newCount < data.length) { //if we havent gone through all edges
if (data[newCount].end.length == 0) {
flag = 0;
for (j = 0; j < allNodes[0].length; j++) {
//if sourseName matches
if (String(allNodes[0][j].childNodes[4].childNodes[0].nodeValue) == data[newCount].start) {
ellipseSelected = d3.selectAll('.node')[0][j].childNodes[2];
parentNodeX = ellipseSelected.attributes.cx.value - ellipseSelected.attributes.rx.value + (2 * ellipseSelected.attributes.rx.value);
parentNodeY = ellipseSelected.attributes.cy.value - (ellipseSelected.attributes.ry.value / 2);
//send the data to interpolate
//match id and update prevData ,incrementData
for (var l = 0; l < countOnNode.length; l++) {
if (countOnNode[l].id == d3.selectAll('.node')[0][j].id) {
countOnNode[l].prevData = countOnNode[l].incrementData;
countOnNode[l].incrementData = data[newCount].rows;
fromData = countOnNode[l].prevData;
toData = countOnNode[l].incrementData;
}
}
blinker();
flag = 1;
if (flag == 1) {
break;
}
}
}
} else {
//check relation and targetNode
//check target
flag = 1;
for (var j = 0; j < allNodes[0].length; j++) {
if (String(allNodes[0][j].childNodes[4].childNodes[0].nodeValue) == data[newCount].end) {
ellipseSelected = d3.selectAll('.node')[0][j].childNodes[2];
parentNodeX = ellipseSelected.attributes.cx.value - ellipseSelected.attributes.rx.value + (2 * ellipseSelected.attributes.rx.value);
parentNodeY = ellipseSelected.attributes.cy.value - (ellipseSelected.attributes.ry.value / 2);
for (var l = 0; l < countOnNode.length; l++) {
if (countOnNode[l].id == d3.selectAll('.node')[0][j].id) {
countOnNode[l].prevData = countOnNode[l].incrementData;
countOnNode[l].incrementData = +data[newCount].rows + +countOnNode[l].prevData;
fromData = countOnNode[l].prevData;
toData = countOnNode[l].incrementData;
nodeSelected = l;
// console.log(" j =" + j + "l "+l+ " fromData " + fromData + " toData "+toData);
}
}
debugger
for (var ll = 0; ll < countOnNode.length; ll++) {
if (countOnNode[ll].name == data[newCount].start) {
debugger;
// console.log(data[newCount]);
startNodeSelected = ll;
}
}
debugger
//set the edge by checking relation
for (var k = 0; k < indexAndEdge.length; k++) {
//if(edges[0][k].childNodes[7].childNodes[0] == indexAndEdge)
if ((data[newCount].relation.trim() == (String(indexAndEdge[k].relation.nodeValue).trim()) &&
(((countOnNode[nodeSelected].title == indexAndEdge[k].destination) && (countOnNode[startNodeSelected].title == indexAndEdge[k].start)) || ((countOnNode[nodeSelected].title == indexAndEdge[k].start) && (countOnNode[startNodeSelected].title == indexAndEdge[k].destination))))) {
indexEdge = k;
}
}
blinker();
flag = 0;
if (flag == 0) {
break;
}
}
}
}
//allEllipse
newCount++;
timer();
} else {
// count =0 ;
timer()
console.log('end') //end
}
}, 3000)
}
timer();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: graphname Pages: 1 -->
<svg width="308pt" height="131pt"
viewBox="0.00 0.00 308.09 131.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 127)">
<title>graphname</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-127 304.095,-127 304.095,4 -4,4"/>
<!-- 0 -->
<g id="node1" class="node"><title>0</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="51.4971" cy="-105" rx="42.4939" ry="18"/>
<text text-anchor="middle" x="51.4971" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Account</text>
</g>
<!-- 1 -->
<g id="node2" class="node"><title>1</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="177.497" cy="-18" rx="51.9908" ry="18"/>
<text text-anchor="middle" x="177.497" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">Attachment</text>
</g>
<!-- 0->1 -->
<g id="edge1" class="edge"><title>0->1</title>
<path fill="none" stroke="#cd0000" d="M81.6636,-83.6496C104.156,-68.4764 134.397,-48.0758 154.846,-34.2806"/>
<polygon fill="#cd0000" stroke="#cd0000" points="79.4899,-80.894 73.1573,-89.388 83.4047,-86.697 79.4899,-80.894"/>
<text text-anchor="middle" x="143.997" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Parent </text>
</g>
<!-- 2 -->
<g id="node3" class="node"><title>2</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="32.4971" cy="-18" rx="32.4942" ry="18"/>
<text text-anchor="middle" x="32.4971" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">Order</text>
</g>
<!-- 0->2 -->
<g id="edge2" class="edge"><title>0->2</title>
<path fill="none" stroke="#cd0000" d="M36.6269,-78.2339C35.365,-75.1966 34.2755,-72.0805 33.4971,-69 30.8171,-58.3937 30.5329,-46.1155 30.9355,-36.3806"/>
<polygon fill="#cd0000" stroke="#cd0000" points="33.6008,-80.0185 41.0756,-87.527 39.9146,-76.9959 33.6008,-80.0185"/>
<text text-anchor="middle" x="61.4971" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Account </text>
</g>
<!-- 3 -->
<g id="node4" class="node"><title>3</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="177.497" cy="-105" rx="47.3916" ry="18"/>
<text text-anchor="middle" x="177.497" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Campaign</text>
</g>
<!-- 3->1 -->
<g id="edge3" class="edge"><title>3->1</title>
<path fill="none" stroke="#cd0000" d="M177.497,-76.7339C177.497,-63.4194 177.497,-47.806 177.497,-36.1754"/>
<polygon fill="#cd0000" stroke="#cd0000" points="173.997,-76.7989 177.497,-86.799 180.997,-76.799 173.997,-76.7989"/>
<text text-anchor="middle" x="198.997" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Parent </text>
</g>
<!-- 4 -->
<g id="node5" class="node"><title>4</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="271.497" cy="-105" rx="28.6953" ry="18"/>
<text text-anchor="middle" x="271.497" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Lead</text>
</g>
<!-- 4->1 -->
<g id="edge4" class="edge"><title>4->1</title>
<path fill="none" stroke="#cd0000" d="M251.874,-81.4584C243.735,-72.5603 233.988,-62.4628 224.497,-54 216.904,-47.2291 208.071,-40.4074 200.126,-34.6078"/>
<polygon fill="#cd0000" stroke="#cd0000" points="249.277,-83.8042 258.567,-88.8979 254.481,-79.1226 249.277,-83.8042"/>
<text text-anchor="middle" x="259.997" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Parent </text>
</g>
</g>
</svg>
Here,circle is added to .svg file.And then text to that circle is added. I want to add text at centre of circle and also if number is bigger like 10000 ,it should fit to that circle.
I tried with
.attr('height', 'auto')
.attr('text-anchor', 'middle')
But,as position of text is decided on which node is added(present in .svg file) and not on circle position,it is not working.
Create SVG groups to hold the circles and text labels. Adjust the position of both circles and text using transform attribute of group elements. Set text-anchor attribute of text element as middle.
var circles = d3.select(ellipseSelected.parentNode)
.append("g")
.attr("transform","translate("+parentNodeX+","+parentNodeY+")")
circles.append("circle")
.attr("r", 10)
.attr("stroke-width", 1)
.attr("stroke", "white")
.style('fill', '#CE2029');
d3.select(ellipseSelected.parentNode)
.data([toData]);
circles.data([toData]).append("text")
.style("dominant-baseline","central")
.attr("dx", 0)
.attr('text-anchor',"middle")
.attr("font-size", "8px")
.transition()
.duration(3000)
.tween("text", function(d) {
var i = d3.interpolate(fromData, d),
prec = (d + "").split("."),
round = (prec.length > 1) ? Math.pow(10, prec[1].length) : 1;
return function(t) {
this.textContent = Math.round(i(t) * round) / round;
//Update node radius based on text length.
var rad = this.textContent.length>3?(this.textContent.length/3)*10:10;
d3.select(this.parentNode).select("circle").attr("r",rad);
};
});
var data =
[{"seq":"1","start":"Account","end":"Order","relation":"Account","rows":"1"},
{"seq":"2","start":"Account","end":"Attachment","relation":"Parent","rows":"10"}
,{"seq":"3","start":"Order","end":"Account","relation":"Account","rows":"15"}
,{"seq":"4","start":"Attachment","end":"Account","relation":"Parent","rows":"55"}
,{"seq":"5","start":"Attachment","end":"Campaign","relation":"Parent","rows":"45"}
,{"seq":"6","start":"Attachment","end":"Lead","relation":"Parent","rows":"47"}
,{"seq":"7","start":"Lead","end":"Attachment","relation":"Parent","rows":"75"}
,{"seq":"8","start":"Campaign","end":"Attachment","relation":"Parent","rows":"34"},
{"seq":"9","start":"Order","end":"Account","relation":"Account","rows":"99"}
,{"seq":"10","start":"Attachment","end":"Account","relation":"Parent","rows":"12"}
,{"seq":"11","start":"Attachment","end":"Campaign","relation":"Parent","rows":"5"}
,{"seq":"12","start":"Attachment","end":"Lead","relation":"Parent","rows":"75"}];
var ellipseSelected, pathSelected, parentNodeX, parentNodeY, relationshipName, indexEdge, fromData, toData, nodeSelected, startNodeSelected;
//flag =1 ,when we have both src and trg
var flag = 1;
var newCount = 0;
var edges = d3.selectAll('.edge');
var path = d3.selectAll('.path')
var allEllipse = d3.selectAll('ellipse');
var allNodes = d3.selectAll('.node');
var theGraph = document.getElementById('graph0') //getContainer
var polygon = document.getElementsByTagName('polygon')[0] //getPolygon to insert after
var allEdgesJS = document.getElementsByClassName("edge"); //select all Edges
for (var i = 0; i < allEdgesJS.length; i++) { //Loop through edges to move
theGraph.insertBefore(allEdgesJS[i], polygon.nextSibling); //insert after polygon
}
function ellipseAdd() {
var circles = d3.select(ellipseSelected.parentNode)
.append("g")
.attr("transform","translate("+parentNodeX+","+parentNodeY+")")
circles.append("circle")
.attr("r", 10)
.attr("stroke-width", 1)
.attr("stroke", "white")
.style('fill', '#CE2029');
d3.select(ellipseSelected.parentNode)
.data([toData]);
circles.data([toData]).append("text")
.style("dominant-baseline","central")
.attr("dx", 0)
.attr('text-anchor',"middle")
.attr("font-size", "8px")
.transition()
.duration(3000)
.tween("text", function(d) {
var i = d3.interpolate(fromData, d),
prec = (d + "").split("."),
round = (prec.length > 1) ? Math.pow(10, prec[1].length) : 1;
return function(t) {
this.textContent = Math.round(i(t) * round) / round;
var rad = this.textContent.length>3?(this.textContent.length/3)*10:10;
d3.select(this.parentNode).select("circle").attr("r",rad);
};
});
}
function blinker() {
if (flag == 0) {
//for adding ellipse and text to it
ellipseAdd();
} else {
//blink 3 things\
//ellipse
ellipseAdd();
d3.select('#' + indexAndEdge[indexEdge].id + ' path').style('opacity', 1)
.transition().style('stroke', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('stroke-width', 1)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('stroke-width', 1)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('stroke-width', 1)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('stroke-width', 1).duration(300).style('opacity', 1)
.transition().style('stroke', "#ff800e").style('stroke-width', 1); //select current id from array //select current id from array
d3.select('#' + indexAndEdge[indexEdge].id + ' polygon')
.transition().style('stroke', 'grey').style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029').style('stroke', '#CE2029').style('stroke-width', 2)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029').style('stroke', '#CE2029').style('stroke-width', 2)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('stroke', '#CE2029').style('fill', '#CE2029').style('stroke-width', 2)
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('stroke', 'grey').style('fill', 'grey').style('stroke-width', 1).duration(300).style('opacity', 1)
.transition().style('stroke', "#ff800e").style('fill', "#ff800e").style('stroke-width', 1); //select current id from array
d3.select('#' + indexAndEdge[indexEdge].id + ' text').style('opacity', 0)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029')
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029')
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', '#CE2029').style('fill', '#CE2029')
.transition().duration(300).duration(300).style('opacity', 1)
.transition().style('fill', 'grey').duration(300).style('opacity', 1)
.transition().style('fill', "#ff800e");
}
}
edges.style('opacity', 1);
allNodes.style('fill', "white");
path.style('fill', "yellow");
var indexAndEdge = [];
var countOnNode = [];
edges.each(function(d, i) {
var thisEdgeCount = this.id.substring(4);
debugger
indexAndEdge.push({ //push index you are at, the edge count worked out above and the id
index: i,
count: thisEdgeCount,
id: this.id,
start: String(this.childNodes[0].childNodes[0].nodeValue).split("->")[0],
destination: String(this.childNodes[0].childNodes[0].nodeValue).split("->")[1],
relation: this.childNodes[6].childNodes[0]
})
d3.select('#' + indexAndEdge[i].id + ' polygon').style('fill', 'grey').style('stroke', 'grey');
d3.select('#' + indexAndEdge[i].id + ' path').style('stroke', 'grey');
});
d3.selectAll('.node').each(function(d, i) {
var thisNodeCount = this.id;
debugger
countOnNode.push({ //push index you are at, the edge count worked out above and the id
id: thisNodeCount,
prevData: 0,
incrementData: 0,
title: this.childNodes[0].childNodes[0].nodeValue,
name: String(this.childNodes[4].childNodes[0].nodeValue)
})
});
function timer() {
setTimeout(function(d) {
if (newCount < data.length) { //if we havent gone through all edges
if (data[newCount].end.length == 0) {
flag = 0;
for (j = 0; j < allNodes[0].length; j++) {
//if sourseName matches
if (String(allNodes[0][j].childNodes[4].childNodes[0].nodeValue) == data[newCount].start) {
ellipseSelected = d3.selectAll('.node')[0][j].childNodes[2];
parentNodeX = ellipseSelected.attributes.cx.value - ellipseSelected.attributes.rx.value + (2 * ellipseSelected.attributes.rx.value);
parentNodeY = ellipseSelected.attributes.cy.value - (ellipseSelected.attributes.ry.value / 2);
//send the data to interpolate
//match id and update prevData ,incrementData
for (var l = 0; l < countOnNode.length; l++) {
if (countOnNode[l].id == d3.selectAll('.node')[0][j].id) {
countOnNode[l].prevData = countOnNode[l].incrementData;
countOnNode[l].incrementData = data[newCount].rows;
fromData = countOnNode[l].prevData;
toData = countOnNode[l].incrementData;
}
}
blinker();
flag = 1;
if (flag == 1) {
break;
}
}
}
} else {
//check relation and targetNode
//check target
flag = 1;
for (var j = 0; j < allNodes[0].length; j++) {
if (String(allNodes[0][j].childNodes[4].childNodes[0].nodeValue) == data[newCount].end) {
ellipseSelected = d3.selectAll('.node')[0][j].childNodes[2];
parentNodeX = ellipseSelected.attributes.cx.value - ellipseSelected.attributes.rx.value + (2 * ellipseSelected.attributes.rx.value);
parentNodeY = ellipseSelected.attributes.cy.value - (ellipseSelected.attributes.ry.value / 2);
for (var l = 0; l < countOnNode.length; l++) {
if (countOnNode[l].id == d3.selectAll('.node')[0][j].id) {
countOnNode[l].prevData = countOnNode[l].incrementData;
countOnNode[l].incrementData = +data[newCount].rows + +countOnNode[l].prevData;
fromData = countOnNode[l].prevData;
toData = countOnNode[l].incrementData;
nodeSelected = l;
// console.log(" j =" + j + "l "+l+ " fromData " + fromData + " toData "+toData);
}
}
debugger
for (var ll = 0; ll < countOnNode.length; ll++) {
if (countOnNode[ll].name == data[newCount].start) {
debugger;
// console.log(data[newCount]);
startNodeSelected = ll;
}
}
debugger
//set the edge by checking relation
for (var k = 0; k < indexAndEdge.length; k++) {
//if(edges[0][k].childNodes[7].childNodes[0] == indexAndEdge)
if ((data[newCount].relation.trim() == (String(indexAndEdge[k].relation.nodeValue).trim()) &&
(((countOnNode[nodeSelected].title == indexAndEdge[k].destination) && (countOnNode[startNodeSelected].title == indexAndEdge[k].start)) || ((countOnNode[nodeSelected].title == indexAndEdge[k].start) && (countOnNode[startNodeSelected].title == indexAndEdge[k].destination))))) {
indexEdge = k;
}
}
blinker();
flag = 0;
if (flag == 0) {
break;
}
}
}
}
//allEllipse
newCount++;
timer();
} else {
// count =0 ;
timer()
// console.log('end') //end
}
}, 3000)
}
timer();
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: graphname Pages: 1 -->
<svg width="308pt" height="131pt"
viewBox="0.00 0.00 308.09 131.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 127)">
<title>graphname</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-127 304.095,-127 304.095,4 -4,4"/>
<!-- 0 -->
<g id="node1" class="node"><title>0</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="51.4971" cy="-105" rx="42.4939" ry="18"/>
<text text-anchor="middle" x="51.4971" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Account</text>
</g>
<!-- 1 -->
<g id="node2" class="node"><title>1</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="177.497" cy="-18" rx="51.9908" ry="18"/>
<text text-anchor="middle" x="177.497" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">Attachment</text>
</g>
<!-- 0->1 -->
<g id="edge1" class="edge"><title>0->1</title>
<path fill="none" stroke="#cd0000" d="M81.6636,-83.6496C104.156,-68.4764 134.397,-48.0758 154.846,-34.2806"/>
<polygon fill="#cd0000" stroke="#cd0000" points="79.4899,-80.894 73.1573,-89.388 83.4047,-86.697 79.4899,-80.894"/>
<text text-anchor="middle" x="143.997" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Parent </text>
</g>
<!-- 2 -->
<g id="node3" class="node"><title>2</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="32.4971" cy="-18" rx="32.4942" ry="18"/>
<text text-anchor="middle" x="32.4971" y="-14.3" font-family="Times New Roman,serif" font-size="14.00">Order</text>
</g>
<!-- 0->2 -->
<g id="edge2" class="edge"><title>0->2</title>
<path fill="none" stroke="#cd0000" d="M36.6269,-78.2339C35.365,-75.1966 34.2755,-72.0805 33.4971,-69 30.8171,-58.3937 30.5329,-46.1155 30.9355,-36.3806"/>
<polygon fill="#cd0000" stroke="#cd0000" points="33.6008,-80.0185 41.0756,-87.527 39.9146,-76.9959 33.6008,-80.0185"/>
<text text-anchor="middle" x="61.4971" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Account </text>
</g>
<!-- 3 -->
<g id="node4" class="node"><title>3</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="177.497" cy="-105" rx="47.3916" ry="18"/>
<text text-anchor="middle" x="177.497" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Campaign</text>
</g>
<!-- 3->1 -->
<g id="edge3" class="edge"><title>3->1</title>
<path fill="none" stroke="#cd0000" d="M177.497,-76.7339C177.497,-63.4194 177.497,-47.806 177.497,-36.1754"/>
<polygon fill="#cd0000" stroke="#cd0000" points="173.997,-76.7989 177.497,-86.799 180.997,-76.799 173.997,-76.7989"/>
<text text-anchor="middle" x="198.997" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Parent </text>
</g>
<!-- 4 -->
<g id="node5" class="node"><title>4</title>
<ellipse fill="#b2dfee" stroke="#b2dfee" cx="271.497" cy="-105" rx="28.6953" ry="18"/>
<text text-anchor="middle" x="271.497" y="-101.3" font-family="Times New Roman,serif" font-size="14.00">Lead</text>
</g>
<!-- 4->1 -->
<g id="edge4" class="edge"><title>4->1</title>
<path fill="none" stroke="#cd0000" d="M251.874,-81.4584C243.735,-72.5603 233.988,-62.4628 224.497,-54 216.904,-47.2291 208.071,-40.4074 200.126,-34.6078"/>
<polygon fill="#cd0000" stroke="#cd0000" points="249.277,-83.8042 258.567,-88.8979 254.481,-79.1226 249.277,-83.8042"/>
<text text-anchor="middle" x="259.997" y="-57.8" font-family="Times New Roman,serif" font-size="14.00"> Parent </text>
</g>
</g>
</svg>
I've two svg elements on which I've applied a mouseover/mouseout event. The goal is to increase the radius of the mask on mouseover to a size specified by a variable (maxMaskRadius) and decrease it on mouseout back to the initial state (initialMaskRadius).
I works perfectly with just one element. But when I've two elements and hover from one element to another, the animation from the previous elements aborts immediately. But I'd like to have it animate back to its initial state. With my current code that's unfortunately not possible.
Any suggestions on how to do that proper?
DEMO
CSS:
.dday.highlight .overlay {
fill: rgba(247,99,62,0.8);
}
.dday.normal {
width: 288px;
height: 288px;
}
HTML:
<svg class="dday highlight normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="car.jpg">
<image height="196" width="250" />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" width="288" height="288" style="mask: url(#mask1)" onmouseover="initAnimation(evt)" onmouseout="initAnimation(evt)" />
</a>
<mask id="mask1">
<rect x="0" y="0" width="288" height="288" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
<svg class="dday highlight normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="nokia.jpg">
<image height="196" width="250" />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" width="288" height="288" style="mask: url(#mask2)" onmouseover="initAnimation(evt)" onmouseout="initAnimation(evt)" />
</a>
<mask id="mask2">
<rect x="0" y="0" width="288" height="288" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
JS:
var maxImageWidth = 250,
maxImageHeight = 196,
ease = 50,
speed = 12,
maxMaskRadius = 100,
svg = null,
svgWidth = null,
svgHeight = null,
mask = null,
maskRadius = null,
initialMaskRadius = null,
imageObj = [],
imageSrcs = [],
imageWidth = null,
imageHeight = null,
mouseEvent = null;
init();
function init(el, index) {
$('.dday').each(function(index){
defineCurrentElement(this, index);
positionMask();
});
}
function defineCurrentElement(el, index) {
// Redefine the current Element
svg = $(el).closest('.dday'),
svgWidth = svg.width(),
svgHeight = svg.height(),
mask = svg.find('circle')[0];
// On page load there is a index provided to load the images for each element
if(typeof index !== 'undefined'){
loadImage(index);
}
}
function loadImage(index) {
// Load images and scale them to fit the predefined area
imageSrcs[index] = svg.data('image');
imageObj[index] = new Image(),
imageObj[index].image = $('image')[index];
imageObj[index].onload = function(){
scale_width = maxImageWidth / this.width;
scale_height = maxImageHeight / this.height;
scale = Math.min(scale_width, scale_height);
imageWidth = this.width * scale;
imageHeight = this.height * scale;
var xCoordinate = (svgWidth - imageWidth) / 2,
yCoordinate = (svgHeight - imageHeight) / 2;
this.image.setAttributeNS('http://www.w3.org/1999/xlink','href', imageSrcs[index]);
this.image.setAttributeNS(null,'width', imageWidth);
this.image.setAttributeNS(null,'height', imageHeight);
this.image.setAttributeNS(null,'x', xCoordinate);
this.image.setAttributeNS(null,'y', yCoordinate);
};
imageObj[index].src = imageSrcs[index];
}
function initAnimation(ev) {
// Triggered on mouseover/-out
// Change current element and init animation
defineCurrentElement(ev.target);
mouseEvent = ev.type;
requestAnimationFrame(animate);
}
function animate() {
if(mouseEvent == 'mouseover') {
// Increase mask radius on mouseover and repeat until target state is reached
maskRadius += Math.round(Math.max(((maxMaskRadius-maskRadius)/ease) * speed, 0.5));
if(maskRadius >= maxMaskRadius) {
// Target radius has been reached
maskRadius = maxMaskRadius;
} else {
// Target radius hasn't been reached yet -> repeat animation
mask.setAttributeNS(null,'r', maskRadius);
requestAnimationFrame(animate);
}
} else {
// Decrease mask radius on mouseover and repeat until initial state is reached
maskRadius -= Math.max(((maskRadius-initialMaskRadius)/ease) * speed, 0.5);
if(maskRadius <= initialMaskRadius) {
// Target radius has been reached
maskRadius = initialMaskRadius;
} else {
// Target radius hasn't been reached yet -> repeat animation
mask.setAttributeNS(null,'r', maskRadius);
requestAnimationFrame(animate);
}
}
}
function positionMask() {
// Center mask inside element
maskRadius = initialMaskRadius = parseInt(mask.getAttributeNS(null, 'r'), 10);
var maskWidth = maskRadius * 2,
xCoordinate = (svgWidth - maskWidth) / 2 + maskRadius,
yCoordinate = (svgHeight - maskWidth) / 2 + maskRadius;
mask.setAttributeNS(null,'cx', xCoordinate);
mask.setAttributeNS(null,'cy', yCoordinate);
}
Okke, I fixed all your code, and that was not an easy task to work with your code. Please declare all variables which you use, and not use in function global variables as your own private variables, because you can re-write existing global variable.
Now about fixed code:
CSS: no changes.
HTML: removed inline handlers (onmouseover and onmouseout)
Javascript:
When document is ready each svg element with class dday is initializing: downloading image for that svg if variable index exists (loading logic didn't changed almost), then centering mask, declaring function for animation, then adding handler for rect element in a element in initializing svg element.
All settings has been exported to variable settings
All private variables for svg is stored in {svgElement}.svgData object.
Demo: jsFiddle
P.S. By the way, this code is also not good enougt, need more time for clean code, but this code works.
HTML:
<svg class="dday sector-sports normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="http://img1.wikia.nocookie.net/__cb20130511205806/epicrapbattlesofhistory/images/9/94/Vaderrotj.jpg">
<image height="196" width="250" />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" width="288" height="288" style="mask: url(#mask1)" />
</a>
<mask id="mask1">
<rect x="0" y="0" width="288" height="288" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
<svg class="dday sector-sports normal" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" data-image="http://static.comicvine.com/uploads/original/11111/111116692/3213841-7948839370-yoda..jpg">
<image height="196" width="250" />
<a class="overlay" xlink:href="/svg/index.html" target="_top">
<rect x="0" y="0" width="288" height="288" style="mask: url(#mask2)" />
</a>
<mask id="mask2">
<rect x="0" y="0" width="288" height="288" fill="#fff" />
<circle cx="125" cy="125" r="25" />
</mask>
</svg>
Javascript: (used jQuery 1.11 library)
$(document).ready(function () {
var settings = {
imageWidthMax: 250,
imageHeightMax: 196,
ease: 50,
speed: 12,
maskRadiusMax: 100
};
var maskElements = [];
$('svg.dday').each(function (index) {
if (maskElements.indexOf(this) < 0) {
maskElements.push(this);
var sd = {};
this.svgData = sd;
sd.svg = $(this);
sd.svgWidth = sd.svg.width();
sd.svgHeight = sd.svg.height();
sd.mask = sd.svg.find('circle')[0];
// On page load there is a index provided to load the images for each element
if (typeof index !== 'undefined') {
var img = new Image();
img.image = $('image')[index];
img.onload = function () {
var m_scale_width = settings.imageWidthMax / this.width;
var m_scale_height = settings.imageHeightMax / this.height;
var m_scale = Math.min(m_scale_width, m_scale_height);
sd.imgWidth = this.width * m_scale;
sd.imgHeight = this.height * m_scale;
var m_x = (sd.svgWidth - sd.imgWidth) / 2;
var m_y = (sd.svgHeight - sd.imgHeight) / 2;
this.image.setAttributeNS('http://www.w3.org/1999/xlink', 'href', sd.svg.data('image'));
this.image.setAttributeNS(null, 'width', sd.imgWidth);
this.image.setAttributeNS(null, 'height', sd.imgHeight);
this.image.setAttributeNS(null, 'x', m_x);
this.image.setAttributeNS(null, 'y', m_y);
};
img.src = sd.svg.data('image');
}
//Center mask inside element
sd.maskRadiusInit = parseInt(sd.mask.getAttributeNS(null, 'r'), 10);
sd.maskRadius = sd.maskRadiusInit;
sd.maskWidth = sd.maskRadius * 2;
sd.maskX = (sd.svgWidth - sd.maskWidth) / 2 + sd.maskRadius;
sd.maskY = (sd.svgHeight - sd.maskWidth) / 2 + sd.maskRadius;
sd.mask.setAttributeNS(null, 'cx', sd.maskX);
sd.mask.setAttributeNS(null, 'cy', sd.maskY);
var animate = function () {
var m_addToRadius = Math.round(Math.max(((settings.maskRadiusMax - sd.maskRadius) / settings.ease) * settings.speed, 0.5));
if (sd.eventType === 'mouseover') {
sd.maskRadius += m_addToRadius;
if (sd.maskRadius > settings.maskRadiusMax) {
sd.maskRadius = settings.maskRadiusMax;
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
} else {
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
requestAnimationFrame(animate);
}
} else {
sd.maskRadius -= Math.round(Math.max(m_addToRadius, 0.5));
if (sd.maskRadius <= sd.maskRadiusInit) {
sd.maskRadius = sd.maskRadiusInit;
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
} else {
sd.mask.setAttributeNS(null, 'r', sd.maskRadius);
requestAnimationFrame(animate);
}
}
};
$('a>rect', this).on('mouseover mouseleave', function (evt) {
sd.eventType = evt.type;
requestAnimationFrame(animate);
});
}
});
});
Demo: jsFiddle
Below is an example of a Javascript animate object, AnimateJS It works cross-browser.
The example shows a quadratic hover effect for either an svg element or elements contained ing a <g>. Give it a try with your app.
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example: Hover Over Element - Quadratic</title>
<script type="text/javascript" src="../bowser.js"></script>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body style='padding:10px;font-family:arial'>
<center>
<h4>Example: Hover Over Element - Quadratic</h4>
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'>
When the cursor moves over the element its size is increased. This works on both transfomed and non-transformed elements, contained in a <g>, or as individual elements. Uses <b>getBBox</b> to determine scale reference point.
The previous hover size increase is reduced to its original size.
</div>
<table><tr>
<td>
<table>
<tr><td colspan=2><b>Animation Settings:</b></td></tr>
<tr><td>1. Smoothness</td><td>100 frames per second</td></tr>
<tr><td>2. Duration</td><td>200 - runtime in ms</td></tr>
<tr><td>3. Range</td><td> increase scale .5</td></tr>
<tr><td>4. Output Equation</td><td><span style=color:blue>function</span> quad(p){return Math.pow(p, 2)}</td></tr>
<tr><td>6. Application Output </td><td>element transform</td></tr>
</table><br />
<i>There are 2 hover functions: <b>hoverOverG(evt)</b> for <g> elements,<br />and <b>hoverOverE(evt)</b> for individual elements.</i>
</td>
<td>
<div id="svgDiv" style='background-color:lightgreen;'>
<svg version="1.1" id="mySVG" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="400" overflow="hidden" >
<g id="CircleStar1" onmouseover="hoverOverG(evt)" transform="translate(100 100)" ><polygon fill="crimson" stroke="none" points="15,-1.37091e-006 14.2658,-4.63527 12.1353,-8.81679 8.81679,-12.1353 4.63524,-14.2659 -1.37091e-006,-15 -4.63527,-14.2659 -8.81679,-12.1353 -12.1353,-8.81679 -14.2659,-4.63527 -15,-1.37091e-006 -14.2659,4.63524 -12.1353,8.81679 -8.81679,12.1353 -4.63527,14.2658 -1.37091e-006,15 4.63524,14.2658 8.81679,12.1353 12.1353,8.81679 14.2658,4.63524" /><polygon fill="dodgerblue" stroke="none" points="6.2319,3.59799 14.392,-1.37091e-006 6.2319,-3.59799 7.19598,-12.4638 -1.37091e-006,-7.19598 -7.19598,-12.4638 -6.2319,-3.59799 -14.392,-1.37091e-006 -6.2319,3.59799 -7.19598,12.4638 -1.37091e-006,7.19598 7.19598,12.4638" /></g>
<g id="CircleStar2" onmouseover="hoverOverG(evt)" transform="translate(200 200)" ><polygon fill="crimson" stroke="none" points="15,-1.37091e-006 14.2658,-4.63527 12.1353,-8.81679 8.81679,-12.1353 4.63524,-14.2659 -1.37091e-006,-15 -4.63527,-14.2659 -8.81679,-12.1353 -12.1353,-8.81679 -14.2659,-4.63527 -15,-1.37091e-006 -14.2659,4.63524 -12.1353,8.81679 -8.81679,12.1353 -4.63527,14.2658 -1.37091e-006,15 4.63524,14.2658 8.81679,12.1353 12.1353,8.81679 14.2658,4.63524" /><polygon fill="dodgerblue" stroke="none" points="6.2319,3.59799 14.392,-1.37091e-006 6.2319,-3.59799 7.19598,-12.4638 -1.37091e-006,-7.19598 -7.19598,-12.4638 -6.2319,-3.59799 -14.392,-1.37091e-006 -6.2319,3.59799 -7.19598,12.4638 -1.37091e-006,7.19598 7.19598,12.4638" /></g>
<g id="CircleStar3" onmouseover="hoverOverG(evt)" transform="translate(300 300)" ><polygon fill="crimson" stroke="none" points="15,-1.37091e-006 14.2658,-4.63527 12.1353,-8.81679 8.81679,-12.1353 4.63524,-14.2659 -1.37091e-006,-15 -4.63527,-14.2659 -8.81679,-12.1353 -12.1353,-8.81679 -14.2659,-4.63527 -15,-1.37091e-006 -14.2659,4.63524 -12.1353,8.81679 -8.81679,12.1353 -4.63527,14.2658 -1.37091e-006,15 4.63524,14.2658 8.81679,12.1353 12.1353,8.81679 14.2658,4.63524" /><polygon fill="dodgerblue" stroke="none" points="6.2319,3.59799 14.392,-1.37091e-006 6.2319,-3.59799 7.19598,-12.4638 -1.37091e-006,-7.19598 -7.19598,-12.4638 -6.2319,-3.59799 -14.392,-1.37091e-006 -6.2319,3.59799 -7.19598,12.4638 -1.37091e-006,7.19598 7.19598,12.4638" /></g>
<g id=rectEllipse transform="translate(330 20)scale(.5)" onmouseover="hoverOverG(evt)">
<rect x=50 y=200 width=60 height=50 fill=orange />
<ellipse cx=80 cy=227 rx=25 ry=15 fill=blue />
</g>
<g id=rectEllipseTransform transform="translate(130 120)scale(.5)" onmouseover="hoverOverG(evt)">
<rect x=50 y=200 width=60 height=50 fill=orange />
<ellipse cx=80 cy=227 rx=25 ry=15 fill=blue />
</g>
<g id=hoverElements >
<circle onmouseover="hoverOverE(evt)" cx=250 cy=150 r=10 fill=blue />
<circle onmouseover="hoverOverE(evt)" cx=150 cy=150 r=10 fill=blue />
<circle onmouseover="hoverOverE(evt)" cx=350 cy=350 r=10 fill=blue />
<circle transform="translate(110 40)" onmouseover="hoverOverE(evt)" cx=150 cy=150 r=10 fill=maroon />
<circle transform="translate(220 80)" onmouseover="hoverOverE(evt)" cx=150 cy=150 r=10 fill=maroon />
<circle transform="translate(220 80)" onmouseover="hoverOverE(evt)" cx=-10 cy=-10 r=10 fill=red />
<circle transform="translate(80 320)scale(.8)" onmouseover="hoverOverE(evt)" cx=-10 cy=-10 r=10 fill=red />
</g>
</svg>
</div>
</td>
</tr> </table>
<br />SVG Source:<br />
<textarea id=svgSourceValue style='font-size:110%;font-family:lucida console;width:90%;height:200px'></textarea>
<br />Javascript:<br />
<textarea id=jsValue style='border-radius:26px;font-size:110%;font-weight:bold;color:midnightblue;padding:16px;background-color:beige;border-width:0px;font-size:100%;font-family:lucida console;width:90%;height:400px'></textarea>
</center>
<div id='browserDiv' style='padding:5px;position:absolute;top:5px;left:5px;background-color:gainsboro;'>OK in:IE11/CH32/FF23<br /></div>
<script id=myScript>
/*---generalized animate core function
Allows progress/output to follow a specific/customized equation(delta)
Inspired by: Ilya Kantor - http://javascript.info/tutorial/animation
*/
var AnimateJS=function(options){
this.options=options
var start = new Date
var iT = setInterval(
function(){
var timePassed = new Date - start
var progress = timePassed / options.duration
if (progress > 1) progress = 1
this.progress=progress
var delta = options.delta(progress)
options.output(delta)
if (progress == 1)clearInterval(iT);
},options.delay)
}
/*
provide options:
1) range(end value)
2) frames per second(delay = 1000/frames per second)
3) duration in ms
4) delta: equation(linear,etc.)
5) output: This application's output function
*/
var HoverSizeIncrease=.5 //---the element's size increased by 50%--
var FinishedOver=true
var StartTrans=null
var PrevTarget=null
//--onmouseover g symbol---
function hoverOverG(evt)
{
if(FinishedOver==true && (evt.target.parentNode!=PrevTarget)) //--allows initial run---
{
if(PrevTarget)
extractHover(PrevTarget)
var target=evt.target.parentNode
PrevTarget=target
FinishedOver=false
var scaleBegin=1
var range=HoverSizeIncrease //---scale increase
var FPS=100 //---frames per second---
var duration=200 //---ms,.2 seconds---
//---quadratic formula in nth degree---
var delta=function quad(p){return Math.pow(p,2)}
if(target.getAttribute("transform"))
{
StartTrans=target.getAttribute("transform")
var myTrans=StartTrans
}
else
{
StartTrans=null
var myTrans=""
}
var bb=target.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----core animation function---
new AnimateJS(
{
delay: 1000/FPS,
duration: duration,
delta: delta, //---quadratic---
output: function(delta)
{
var scale=scaleBegin+delta*range
target.setAttribute("transform",myTrans+"translate("+(cx)+" "+(cy)+")scale("+scale+")translate("+(-cx)+" "+(-cy)+")")
//---finished---
if(progress==1)
onFinish()
}
})
}
}
//--onmouseover element---
function hoverOverE(evt)
{
if(FinishedOver==true && (evt.target!=PrevTarget)) //--allows initial run---
{
if(PrevTarget)
extractHover(PrevTarget)
var target=evt.target
PrevTarget=target
FinishedOver=false
var scaleBegin=1
var range=HoverSizeIncrease //---scale increase
var FPS=100 //---frames per second---
var duration=200 //---ms,.2 seconds---
//---quadratic formula in nth degree---
var delta=function quad(p){return Math.pow(p,2)}
if(target.getAttribute("transform"))
{
StartTrans=target.getAttribute("transform")
var myTrans=StartTrans
}
else
{
StartTrans=null
var myTrans=""
}
var bb=target.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----core animation function---
new AnimateJS(
{
delay: 1000/FPS,
duration: duration,
delta: delta, //---quadratic---
output: function(delta)
{
var scale=scaleBegin+delta*range
target.setAttribute("transform",myTrans+"translate("+(cx)+" "+(cy)+")scale("+scale+")translate("+(-cx)+" "+(-cy)+")")
//---finished---
if(progress==1)
onFinish()
}
})
}
}
var FinishedExtract=true
var ExtractTarget
var ExtractTrans
function extractHover(PrevTarget)
{
if(FinishedExtract==true) //--allows initial run---
{
ExtractTarget=PrevTarget
if(StartTrans)
ExtractTrans=StartTrans
else
ExtractTrans=""
FinishedExtract=false
var scaleBegin=1+HoverSizeIncrease
var range=HoverSizeIncrease //---scale decrease
var FPS=100 //---frames per second---
var duration=200 //---ms,.2 seconds---
//---quadratic formula in nth degree---
var delta=function quad(p){return Math.pow(p,2)}
var bb=ExtractTarget.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
//----core animation function---
new AnimateJS(
{
delay: 1000/FPS,
duration: duration,
delta: delta, //---quadratic---
output: function(delta)
{
var scale=scaleBegin-delta*range
ExtractTarget.setAttribute("transform",ExtractTrans+"translate("+(cx)+" "+(cy)+")scale("+scale+")translate("+(-cx)+" "+(-cy)+")")
if (progress == 1) // --- finished---
extractFinish();
}
})
}
}
//---this example animation: loop finished---
function onFinish()
{
FinishedOver=true
}
//---this example animation: loop finished---
function extractFinish()
{
FinishedExtract=true
if(ExtractTrans!="")
ExtractTarget.setAttribute("transform",ExtractTrans)
else
ExtractTarget.removeAttribute("transform")
}
</script>
<script>
document.addEventListener("onload",init(),false)
function init()
{
jsValue.value=myScript.text
svgSourceValue.value=svgDiv.innerHTML
}
</script>
</body>
</html>
Try to do this using very simple way. I've not applied any animation. I think you'll figure out how to do that.
Remove mouse event from inline
JS
$('svg')
.mouseenter(function(ev) {
console.log($(this).find('circle').attr('r',40));
})
.mouseleave(function(ev) {
console.log($(this).find('circle').attr('r',25));
});
CHECK JSFiddle