d3js transforming nested group images - javascript

I am working on [this][1] d3 project. Basically I am trying to create a SQL like query builder. I can drop boxes to the drawing area & other operators inside the box. Then I should be able to connect them all. I am trying to translate 2 images which are nested in groups. I want to move the small items inside the big box. I can transform the big box and small operators separately. Problem happens When I try to move the small operators first. I want to move the small operators, then big boxes. meanwhile I want to keep the relative position of small operators and big box same. But when I try to move the large box after moving one of the small box it resets its location. Here is a demo of my work in jsfiddle
<g id="draw">
<rect class="container" height="400" width="400" x="0" y="0" style="fill:gray"></rect>
<g class="qbox" id="qbox">
<line id="dummyLine" x1="0" x2="0" y1="0" y2="0" visibility="hidden"
style='stroke:red; stroke-width:4px'></line>
<image x="10" y="10" class="container" initial-x="10" initial-y="10" xlink:href="http://i60.tinypic.com/20ic9e.png"
width="110"
height="110"></image>
<circle class="left" id="qbox-left" initial-cx="10" initial-cy="65" cx="10" cy="65" r="5"
style="fill:red"></circle>
<circle class="right" id="qbox-right" initial-cx="120" initial-cy="65" cx="120" cy="65" r="5"
style="fill:red"></circle>
<g id="op1" class="op">
<image class="opim" x="10" y="10" class="container" initial-x="10" initial-y="10"
xlink:href="http://i58.tinypic.com/imlzs9.png" width="50"
height="50"></image>
<circle id="op1-left" class="left" initial-cx="10" initial-cy="35" cx="10" cy="35" r="5"
style="fill:red"></circle>
<circle id="op1-right" class="right" initial-cx="60" initial-cy="35" cx="60" cy="35" r="5"
style="fill:red"></circle>
</g>
<g id="op2" class="op">
<image class="opim" x="60" y="60" initial-x="60" initial-y="60"
xlink:href="http://i58.tinypic.com/imlzs9.png" width="50"
height="50"></image>
<circle id="op2-left" class="left" initial-cx="60" initial-cy="85" cx="60" cy="85" r="5"
style="fill:red"></circle>
<circle id="op2-right" class="right" initial-cx="110" initial-cy="85" cx="110" cy="85" r="5"
style="fill:red"></circle>
</g>
</g>
<g class="qbox" id="qbox2" >
<line id="dummyLine" x1="0" x2="0" y1="0" y2="0" visibility="hidden"
style='stroke:red; stroke-width:4px'></line>
<image x="110" y="110" class="container" initial-x="110" initial-y="110" xlink:href="http://i60.tinypic.com/20ic9e.png"
width="110"
height="110"></image>
<circle class="left" id="qbox-left" initial-cx="110" initial-cy="165" cx="110" cy="165" r="5"
style="fill:red"></circle>
<circle class="right" id="qbox-right" initial-cx="220" initial-cy="265" cx="220" cy="165" r="5"
style="fill:red"></circle>
<g id="op3" class="op">
<image class="opim" x="110" y="110" class="container" initial-x="110" initial-y="110"
xlink:href="http://i58.tinypic.com/imlzs9.png" width="50"
height="50"></image>
<circle id="op1-left" class="left" initial-cx="110" initial-cy="135" cx="110" cy="135" r="5"
style="fill:red"></circle>
<circle id="op1-right" class="right" initial-cx="160" initial-cy="135" cx="160" cy="135" r="5"
style="fill:red"></circle>
</g>
<g id="op4" class="op">
<image class="opim" x="160" y="160" initial-x="160" initial-y="160"
xlink:href="http://i58.tinypic.com/imlzs9.png" width="50"
height="50"></image>
<circle id="op2-left" class="left" initial-cx="160" initial-cy="185" cx="160" cy="185" r="5"
style="fill:red"></circle>
<circle id="op2-right" class="right" initial-cx="210" initial-cy="185" cx="210" cy="185" r="5"
style="fill:red"></circle>
</g>
</g>
</g>
var qBox = d3.selectAll('.qbox')
.on('dblclick', function () {
var g = d3.select(this);
var scale = 'scale(1.2,1.2)';
g.attr('transform', g.attr('transform') + ' ' + scale);
});
var opBox = d3.selectAll('.op');
var circles = d3.selectAll('circle');
var cDrag = d3.behavior.drag()
.on('dragstart', function () {
d3.event.sourceEvent.stopPropagation();
})
.on('drag', function () {
var dummyLine = d3.select('#dummyLine');
var me = d3.select(this);
var transForm = me.node().getCTM();
var t2 = me.select(function () {
return this.parentNode;
}).select(function () {
return this.parentNode;
}).select('circle').node().getCTM();
var tC = d3.transform(d3.select(this).attr('transform')).translate;
var tP = d3.transform(d3.select(this).select(function () {
return this.parentNode;
}).attr('transform')).translate;
console.log(transForm);
var meX = t2['e'];
var meY = t2['f'];
dummyLine
.style('visibility', 'visible')
.attr('tx1', Number(me.attr('cx')))
.attr('x1', Number(me.attr('cx')) + (Number(transForm['e'] - Number(meX))))
.attr('ty1', Number(me.attr('cy')))
.attr('y1', Number(me.attr('cy')) + (Number(transForm['f'] - Number(meY))))
.attr('x2', Number(d3.event.x) )
.attr('tx2', Number(d3.event.x) + Number(tP[0]) - Number(tC[0]))
.attr('y2', Number(d3.event.y) )
.attr('ty2', Number(d3.event.y) + Number(tP[1]) - Number(tC[0]))
.attr('start', me.attr('id'))
;
})
.on('dragend', function () {
var g = d3.select(this).select(function () {
return this.parentNode;
}).select(function () {
return this.parentNode;
});
var dummyLine = d3.select('#dummyLine');
dummyLine.style('visibility', 'hidden');
d3.select('.qbox')
.append('line')
.attr('id', function () {
return dummyLine.attr('start') + '__' + circleID;
})
.attr('x1', dummyLine.attr('x1'))
.attr('ix1', dummyLine.attr('tx1'))
.attr('x2', dummyLine.attr('x2'))
.attr('ix2', d3.select('#' + circleID).attr('cx'))
.attr('y1', dummyLine.attr('y1'))
.attr('iy1', dummyLine.attr('ty1'))
.attr('y2', dummyLine.attr('y2'))
.attr('iy2', d3.select('#' + circleID).attr('cy'))
.attr('start', dummyLine.attr('start'))
.attr('end', circleID)
.style('stroke', 'green')
.style('stroke-width', '2px')
;
})
;
var svg = d3.select('svg').node();
var drag = d3.behavior.drag()
.origin(function () {
var t = d3.transform(d3.select(this).attr("transform")).translate;
return {x: t[0], y: t[1]};
}).on('dragstart', function () {
d3.event.sourceEvent.stopPropagation();
}).on('drag', function () {
var g = d3.select(this);
var mouse = {dx: d3.event.x, dy: d3.event.y};
var currentObj = {
x: g.select('image').attr('x'),
y: g.select('image').attr('y'),
width: g.select('image').attr('width'),
height: g.select('image').attr('height')
};
var parentObj = {
x: (Number(g.select(function () {
return this.parentNode;
}).select('.container').attr('x'))), // + Number(d3.transform(parent.attr('transform')).translate[0])),
y: (Number(g.select(function () {
return this.parentNode;
}).select('.container').attr('y'))), // + Number(d3.transform(parent.attr('transform')).translate[1])),
width: g.select(function () {
return this.parentNode;
}).select('.container').attr('width'),
height: g.select(function () {
return this.parentNode;
}).select('.container').attr('height')
};
var loc = getXY(mouse, currentObj, parentObj);
d3.select(this).attr('transform', 'translate(' + loc.x + ',' + loc.y + ')');
// d3.select(this).attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')');
var groupId = d3.select(this).attr('id');
var groupClass = d3.select(this).attr('class');
d3.selectAll('line')[0].forEach(function (e1) {
var line = d3.select(e1);
// console.log('groupId: ', groupId);
if (line.attr('id') != 'dummyLine' && groupClass != 'qbox') {
// console.log('--------------');
// console.log('lineId: ', line.attr('id'));
var lineStart = line.attr('start').split('-')[0];
var lineEnd = line.attr('end').split('-')[0];
// console.log('lineStatr : ', lineStart);
// console.log('lineEnd : ', lineEnd);
var t = d3.transform(d3.select('#' + groupId).attr('transform')).translate;
var t2 = d3.transform(d3.select('#' + groupId).select(function () {
return this.parentNode;
}).attr('transform')).translate;
console.log('groupID ', groupId);
if (lineStart == groupId) {
var t = d3.transform(d3.select('#' + lineStart).attr('transform')).translate;
line.attr('x1', Number(line.attr('ix1')) + (Number(t[0])));
line.attr('y1', Number(line.attr('iy1')) + Number(t[1]));
// line.attr('x1', Number(line.attr('ix1')) - (-Number(t[0])+Number(t2[0])));
// line.attr('y1', Number(line.attr('iy1')) - (-Number(t[1]+Number(t2[1]))));
}
if (lineEnd == groupId) {
var t = d3.transform(d3.select('#' + lineEnd).attr('transform')).translate;
line.attr('x2', Number(line.attr('ix2')) + Number(t[0]));
line.attr('y2', Number(line.attr('iy2')) + Number(t[1]));
// line.attr('x2', Number(line.attr('ix2')) - Number(t[0]));
// line.attr('y2', Number(line.attr('iy2')) - Number(t[1]));
// line.attr('x2', Number(line.attr('ix2')) - (Number(t[0]+Number(t2[0]))));
// line.attr('y2', Number(line.attr('iy2')) - (Number(t[1]+Number(t2[1]))));
}
}
});
})
;
opBox.call(drag);
qBox.call(drag);
circles.call(cDrag);
var circleID;
circles.on('mouseover', function () {
circleID = d3.select(this).attr('id');
}).on('mouseout', function () {
circleID = null;
})
PS : I connect two elements by dragging the circles and dropping into another circle.
Can anyone point out my mistake?

After all sort of troubles, I found my answer. Actually it is all about the coordinate system and where to put stuff and how to organize it. Once I fugured that out, Answer is pretty obvious.
<svg width="500" height="500" style="background-color: blue">
<g id="draw">
<rect class="container" height="500" width="500" x="0" y="0" style="fill:yellow"></rect>
<line class="dummyLineOutsideQbox"></line>
<g class="qbox" id="qbox">
<line class="dummyLineInsideQbox"></line>
<image x="10" y="10" class="container" initial-x="10" initial-y="10" xlink:href="images/query.png"
width="110"
height="110"></image>
<circle class="left" id="qbox-left" initial-cx="10" initial-cy="65" cx="10" cy="65" r="5"
style="fill:red"></circle>
<circle class="right" id="qbox-right" initial-cx="120" initial-cy="65" cx="120" cy="65" r="5"
style="fill:red"></circle>
<g id="op1" class="op">
<image class="opim" x="10" y="10" class="container" initial-x="10" initial-y="10"
xlink:href="images/filter.png" width="50"
height="50"></image>
<circle id="op1-left" class="left" initial-cx="10" initial-cy="35" cx="10" cy="35" r="5"
style="fill:red"></circle>
<circle id="op1-right" class="right" initial-cx="60" initial-cy="35" cx="60" cy="35" r="5"
style="fill:red"></circle>
</g>
<g id="op2" class="op">
<image class="opim" x="60" y="60" initial-x="60" initial-y="60"
xlink:href="images/filter.png" width="50"
height="50"></image>
<circle id="op2-left" class="left" initial-cx="60" initial-cy="85" cx="60" cy="85" r="5"
style="fill:red"></circle>
<circle id="op2-right" class="right" initial-cx="110" initial-cy="85" cx="110" cy="85" r="5"
style="fill:red"></circle>
</g>
</g>
<g class="qbox" id="qbox2">
<line class="dummyLineInsideQbox"></line>
<image x="110" y="110" class="container" initial-x="110" initial-y="110" xlink:href="images/query.png"
width="110"
height="110"></image>
<circle class="left" id="qbox2-left" initial-cx="110" initial-cy="165" cx="110" cy="165" r="5"
style="fill:red"></circle>
<circle class="right" id="qbox2-right" initial-cx="220" initial-cy="265" cx="220" cy="165" r="5"
style="fill:red"></circle>
<g id="op3" class="op">
<image class="opim" x="110" y="110" class="container" initial-x="110" initial-y="110"
xlink:href="images/filter.png" width="50"
height="50"></image>
<circle id="op3-left" class="left" initial-cx="110" initial-cy="135" cx="110" cy="135" r="5"
style="fill:red"></circle>
<circle id="op3-right" class="right" initial-cx="160" initial-cy="135" cx="160" cy="135" r="5"
style="fill:red"></circle>
</g>
<g id="op4" class="op">
<image class="opim" x="160" y="160" initial-x="160" initial-y="160"
xlink:href="images/filter.png" width="50"
height="50"></image>
<circle id="op4-left" class="left" initial-cx="160" initial-cy="185" cx="160" cy="185" r="5"
style="fill:red"></circle>
<circle id="op4-right" class="right" initial-cx="210" initial-cy="185" cx="210" cy="185" r="5"
style="fill:red"></circle>
</g>
</g>
</g>
</svg>
<script>
var qBox = d3.selectAll('.qbox')
.on('dblclick', function () {
var g = d3.select(this);
var scale = 'scale(1.2,1.2)';
g.attr('transform', g.attr('transform') + ' ' + scale);
});
var opBox = d3.selectAll('.op');
var circles = d3.selectAll('circle');
var cDrag = d3.behavior.drag()
.on('dragstart', function () {
d3.event.sourceEvent.stopPropagation();
})
.on('drag', function () {
var thisCircle = d3.select(this);
var thisGroup = thisCircle.select(function () {
return this.parentNode;
});
var thisGroupTransform = d3.transform(thisGroup.attr('transform')).translate;
var thisGroupParent = d3.select(this).select(function () {
return this.parentNode;
}).select(function () {
return this.parentNode;
});
var thisGroupParentId = thisGroupParent.attr('id');
var thisGroupParentClass = thisGroupParent.attr('class');
if (thisGroupParentClass == 'qbox') {
dummyLine = d3.select('#' + thisGroupParentId).select('.dummyLineInsideQbox');
}
else {
dummyLine = d3.select('#' + thisGroupParentId).select('.dummyLineOutsideQbox');
}
console.log('dummyLine ', dummyLine.attr('class'));
dummyLine
.style('visibility', 'visible')
.style('stroke', 'red')
.style('stroke-width', '3px')
.attr('x1', Number(thisCircle.attr('cx')) + thisGroupTransform[0])
.attr('real-x1', thisCircle.attr('cx'))
.attr('y1', Number(thisCircle.attr('cy')) + thisGroupTransform[1])
.attr('real-y1', thisCircle.attr('cy'))
.attr('x2', d3.mouse(this)[0] + thisGroupTransform[0])
.attr('y2', d3.mouse(this)[1] + thisGroupTransform[1])
.attr('startGroup', thisGroup.attr('id'))
.attr('startCircleClass', thisCircle.attr('class'))
;
})
.on('dragend', function () {
var thisCircle = d3.select('#' + circleID);
var thisCircleClass = thisCircle.attr('class');
var thisGroup = thisCircle.select(function () {
return this.parentNode;
});
var thisGroupTransform = d3.transform(thisGroup.attr('transform')).translate;
var thisGroupParent = d3.select(this).select(function () {
return this.parentNode;
}).select(function () {
return this.parentNode;
});
var thisGroupParentId = thisGroupParent.attr('id');
var thisGroupParentClass = thisGroupParent.attr('class');
var sourceCircleClass = dummyLine.attr('startCircleClass');
var sourceGroup = d3.select('#' + dummyLine.attr('startGroup'));
console.log('SOURCE GROUP :', sourceGroup.attr('id'));
var targetLineGroup;
var lineClass;
var t = [0, 0];
var x1, x2, y1, y2;
if (( sourceGroup.attr('class') == 'op') || (thisGroup.attr('class') == 'op')) {
if (sourceGroup.attr('class') == 'op') {
targetLineGroup = sourceGroup.select(function () {
return this.parentNode;
});
// console.log('I am on line 185');
} else {
targetLineGroup = thisGroup.select(function () {
return this.parentNode;
});
// console.log('I am on line 190');
}
lineClass = 'in';
x1 = startCircle.attr('cx');
y1 = startCircle.attr('cy');
x2 = thisCircle.attr('cx');
y2 = thisCircle.attr('cy');
}
if ((sourceGroup.attr('class') == 'qbox') && (thisGroup.attr('class') == 'qbox')) {
targetLineGroup = sourceGroup.select(function () {
return this.parentNode;
});
lineClass = 'out';
x1 = dummyLine.attr('x1');
x2 = dummyLine.attr('x2');
y1 = dummyLine.attr('y1');
y2 = dummyLine.attr('y2');
}
targetLineGroup
.append('line')
.attr('class', lineClass)
.attr('id', function () {
return sourceCircleClass + '--' + sourceGroup.attr('id') + '__' + thisCircleClass + '--' + thisGroup.attr('id');
})
.attr('x1', dummyLine.attr('x1'))
.attr('real-x1', dummyLine.attr('x1'))
.attr('y1', dummyLine.attr('y1'))
.attr('real-y1', dummyLine.attr('y1'))
.attr('x2', dummyLine.attr('x2'))
.attr('real-x2', dummyLine.attr('x2'))
.attr('y2', dummyLine.attr('y2'))
.attr('real-y2', dummyLine.attr('y2'))
.attr('startGroup', dummyLine.attr('startGroup'))
.attr('endGroup', thisGroup.attr('id'))
.style('stroke', 'green')
.style('stroke-width', '3px')
;
dummyLine.style('visibility', 'hidden');
console.log('DRAWING LINE ON : ', targetLineGroup.attr('id'))
})
;
var svg = d3.select('svg').node();
var drag = d3.behavior.drag()
.origin(function () {
var t = d3.transform(d3.select(this).attr("transform")).translate;
return {x: t[0], y: t[1]};
}).on('dragstart', function () {
d3.event.sourceEvent.stopPropagation();
}).on('drag', function () {
var g = d3.select(this);
var mouse = {dx: d3.event.x, dy: d3.event.y};
var currentObj = {
x: g.select('image').attr('x'),
y: g.select('image').attr('y'),
width: g.select('image').attr('width'),
height: g.select('image').attr('height')
};
var parentObj = {
x: (Number(g.select(function () {
return this.parentNode;
}).select('.container').attr('x'))), // + Number(d3.transform(parent.attr('transform')).translate[0])),
y: (Number(g.select(function () {
return this.parentNode;
}).select('.container').attr('y'))), // + Number(d3.transform(parent.attr('transform')).translate[1])),
width: g.select(function () {
return this.parentNode;
}).select('.container').attr('width'),
height: g.select(function () {
return this.parentNode;
}).select('.container').attr('height')
};
var loc = getXY(mouse, currentObj, parentObj);
d3.select(this).attr('transform', 'translate(' + loc.x + ',' + loc.y + ')');
// d3.select(this).attr('transform', 'translate(' + d3.event.x + ',' + d3.event.y + ')');
var thisGroupId = d3.select(this).attr('id');
var groupClass = d3.select(this).attr('class');
var thisGroupClass = d3.select(this).attr('class');
var tLineClass;
if (thisGroupClass == 'qbox') {
tLineClass = 'out'
} else {
tLineClass = 'in'
}
d3.selectAll('line.' + tLineClass)[0].forEach(function (e1) {
var line = d3.select(e1);
var lineId = line.attr('id');
var lineStartGroup = lineId.split('__')[0].split('--')[1];
var lineStartSide = lineId.split('__')[0].split('--')[0];
var lineEndGroup = lineId.split('__')[1].split('--')[1];
var lineEndSide = lineId.split('__')[1].split('--')[0];
console.log('-------------------');
console.log(lineId);
console.log(lineStartGroup);
console.log(lineEndGroup);
console.log('-------------------');
var c;
var ctm;
if (thisGroupId == lineStartGroup) {
var t = d3.transform(d3.select('#' + thisGroupId).attr('transform')).translate;
if (lineStartSide == 'left') {
c = d3.select('#' + lineStartGroup).select('.left');
ctm = c.node().getCTM();
} else {
c = d3.select('#' + lineStartGroup).select('.right');
ctm = c.node().getCTM();
}
var x = d3.transform(c.select(function () {
return this.parentNode;
}).attr('transform')).translate;
console.log('START e'+ctm['e']);
console.log('START ex'+x[0]);
console.log('START f'+ctm['f']);
console.log('START fx'+x[0]);
line.attr('x1', Number(c.attr('cx')) + Number(x[0]));
line.attr('y1', Number(c.attr('cy')) + Number(x[1]));
}
if (thisGroupId == lineEndGroup) {
// var t = d3.transform(d3.select('#' + thisGroupId).attr('transform')).translate;
//
// line.attr('x2', Number(line.attr('real-x2')) + Number(t[0]));
// line.attr('y2', Number(line.attr('real-y2')) + Number(t[1]));
if (lineEndSide == 'left') {
c = d3.select('#' + lineEndGroup).select('.left');
} else {
c = d3.select('#' + lineEndGroup).select('.right');
}
var x = d3.transform(c.select(function () {
return this.parentNode;
}).attr('transform')).translate;
ctm = c.node().getCTM();
console.log('END circleId'+c.attr('id'));
line.attr('x2', Number(c.attr('cx')) + Number(x[0]));
line.attr('y2', Number(c.attr('cy')) + Number(x[1]));
}
});
})
;
opBox.call(drag);
qBox.call(drag);
circles.call(cDrag);
var circleID;
var dummyLine;
circles.on('mouseover', function () {
circleID = d3.select(this).attr('id');
}).on('mouseout', function () {
circleID = null;
})
function getXY(mouse, current, parent) {
var obj = {
x: 0,
y: 0
};
var dx = mouse.dx;
var dy = mouse.dy;
obj.x = dx;
obj.y = dy;
var xGap = current.x - parent.x;
var yGap = current.y - parent.y;
if (dx < 0) {
if ((dx + xGap) < 0) {
obj.x = -1 * xGap + 10;
}
} else {
if ((dx + xGap) > parent.width) {
obj.x = parent.width - xGap - current.width - 10;
}
}
if (dy < 0) {
if ((dy + yGap) < 0) {
obj.y = -1 * yGap + 10;
}
} else {
if ((dy + yGap) > parent.height) {
obj.y = parent.height - yGap - current.height - 10;
}
}
return obj;
}
</script>

From my experience developing visual editors, I can say that relative positions (such as x,y in Operator) can and shoud be managed as data.
Try changing that data (and not directly the x,y attributes of the svg element), and binding the data in a d3js way. It will be much more idiomatic and fast.
PS: I know about separation of model and view, and I know about the bad side of storing visual properties along the model, but if it will be only a view for that model, it is for sure the best approach.

Related

JQuery draggable element controlling a SVG object jumping for the first time

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>

why subrect of rect can not show in d3.js

what graph i want to generate is nested rect,but the sub rect cant show.
My partial code is :
_chart.renderFeatureAxisInner = function(svg=_svg){
feature_name = svg.select('.Feat_0_name').text();
nest_data = d3.nest()
.key(function(d){
return d[feature_name];
}).key(function(d){
return d['Label'];
}).entries(_chart._l1_inner_data);
var updateRect = svg.selectAll('#Feature_0')
.data(nest_data);
var enterRect = updateRect.enter();
var exitRect = updateRect.exit();
updateRect.append('g')
.attr('id',function(d_l0,i){
return 'Feature0_value_'+i;
})
.selectAll('rect')
.data(function(d_l0,i){
//console.log(i);
return d_l0.values;
}).enter() //nest_data[0].values
.append('rect');
var i_l0 = 0;
var offsety = 0;
var offsety_arr=[0];
updateRect.selectAll('rect')
.data(function(d_l0,i_l0){
//i_l0=i_l0;
offsety=0;
return d_l0.values;
}) //nest_data[0].values
//.append('rect')
.attr('id',function(d_l1,i_l1){
if(d_l1.values[0]['Sepal Length'] == '(4.296, 6.1]')
i_l0 = 0;
else i_l0 = 1;
return 'Feature0_v'+i_l0+'_'+i_l1;
})
.attr('stroke','black')
.attr('fill',function(d_l1,i_l1){
//nest_data[0].values[0]
return _colors[d_l1.key];
})
//.attr('stroke','blue')
.attr('width',function(d_l1,i_l1){
if(d_l1.values[0]['Sepal Length'] == '(4.296, 6.1]')
i_l0 = 0;
else i_l0 = 1;
return svg.select('.Feature0_value_'+i_l0)
.attr('width');
}).attr('height',function(d_l1,i_l1){
var h=_base_height * d_l1.values[0]['1'];
offsety_arr.push(h);
return h;
})
.attr('x',function(d_l1,i_l1){
//cls = d.values[0]['Sepal Length'];
if(d_l1.values[0]['Sepal Length'] == '(4.296, 6.1]')
i_l0 = 0;
else i_l0 = 1;
//console.log(i_l0);
return svg.select('.Feature0_value_'+i_l0)
.attr('x');
}).attr('y',function(d_l1,i_l1){
if(d_l1.values[0]['Sepal Length'] == '(4.296, 6.1]')
i_l0 = 0;
else i_l0 = 1;
offsety=offsety + offsety_arr.splice(0,1)[0];
console.log(offsety);
var basey=parseFloat(svg.select('.Feature0_value_0')
.attr('y'));
y=offsety+basey;
return y+'';
});
offsety_arr=[0];
}
I think the generated rects element is not wrong.
And the result html is :
<!DOCTYPE html>
<html>
<body>
<svg width="1200" height="500">
<path stroke-width="3px" stroke="red" class="baseline1" d="M187.5,100L187.5,400"></path>
<text dy="-1em" y="100" x="187.5" text-anchor="middle" font-size="12px" fill="white" class="Feat_0_name">Sepal Length</text>
<g class="Feature0_frame">
<rect height="60" width="94.999999995" y="190" x="140.0000000025" stroke="blue" fill-opacity="0.3" fill="none" class="Feature0_value_0" id="Feature_0">
<g id="Feature0_value_0">
<rect y="190" x="140.0000000025" height="31.57894737" width="94.999999995" fill="yellow" stroke="black" id="Feature0_v0_0"></rect>
<rect y="221.57894737" x="140.0000000025" height="21.473684208" width="94.999999995" fill="blue" stroke="black" id="Feature0_v0_1"></rect>
<rect y="243.052631578" x="140.0000000025" height="6.947368422" width="94.999999995" fill="red" stroke="black" id="Feature0_v0_2"></rect></g></rect>
<rect height="60" width="55.000000005" y="250" x="159.9999999975" stroke="blue" fill-opacity="0.3" fill="none" class="Feature0_value_1" id="Feature_0">
<g id="Feature0_value_1">
<rect y="250" x="159.9999999975" height="17.454545454" width="55.000000005" fill="blue" stroke="black" id="Feature0_v1_0"></rect>
<rect y="267.454545454" x="159.9999999975" height="42.545454546" width="55.000000005" fill="red" stroke="black" id="Feature0_v1_1"></rect></g></rect>
</g>
</svg>
</body>
</html>
and the result is result image
However,Run it in Browser,just show 2 rects ranther than 7 rects,in other words, the sub rects are not shown. What is wrong? can somebody help ?
what i want to realize is the figure 1 figure 1 and 2
and any suggestion on figure 2 realization, for example ,what layout should i use(maybe Treemap?) or just the same method on figure1 ?

Text Alignment in d3

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>

d3 dragging elements individually & by group

I have couple of rectangles inside a group, I want them to move in a group when the outer rectangle is dragged, But I should also be able to move the inner rectangles individually, I can do this separately, but I cannot make them work in same time. Here is the fiddle. Can anyone help me on this?
<svg width="500" height="500" style="background-color: red">
<g class="big">
<rect class="sqr" height="200" width="400" x="40" y="40" style="fill:blue"></rect>
<rect class="sqr" height="50" width="50" x="100" y="100" initial-x="100" initial-y="100" style="fill:green"></rect>
<rect class="sqr" height="50" width="50" x="250" y="100" initial-x="100" initial-y="100" style="fill:green"></rect>
</g>
</svg>
<script>
var drag = d3.behavior.drag().on('drag', function(d){
d3.select(this).attr('transform', 'translate('+(d3.event.x)+','+(d3.event.y)+')');
});
var sqDrag = d3.behavior.drag().on('drag', function (d) {
console.log('sqr dragging..');
d3.select(this).attr('x', d3.mouse(this)[0]).attr('y',d3.mouse(this)[1]);
});
var g = d3.select('.big').call(drag);
var r = d3.selectAll('.sqr').on('mousedown', function () {
console.log('sqr clicked');
d3.event.stopPropagation();
}).call(sqDrag);
</script>
Just modify your sqDrag function
var sqDrag = d3.behavior.drag().on('drag', function (d) {
console.log('sqr dragging..');
var id = d3.select(this).attr('id');
if (id === null)
d3.select(this).attr('x', d3.mouse(this)[0]).attr('y',d3.mouse(this)[1]);
else {
var parent = d3.select(this);
var dx = d3.mouse(this)[0] - parent.attr('x');
var dy = d3.mouse(this)[1] - parent.attr('y');
parent.attr('x', d3.mouse(this)[0]).attr('y',d3.mouse(this)[1]);
d3.selectAll("." + id).attr('x', function() {
return Number(d3.select(this).attr('x')) + dx;
}).attr('y', function() {
return Number(d3.select(this).attr('y')) + dy;
});
}
});
Fiddle - https://jsfiddle.net/46ag2x2z/

SVG hover with multiple elements

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

Categories