I draw a Polygon using D3 mouse events as shown in this fiddle.
Below is the method that get's the polygon's bounding box and sets the polygon's bounding box properties.
function completePolygon() {
d3.select('g.outline').remove();
gPoly = svgCanvas.append('g')
.classed("polygon", true);
polyPoints.splice(polyPoints.length - 1);
polyEl = gPoly.append("polygon")
.attr("points", polyPoints);
for (var i = 0; i < polyPoints.length; i++) {
gPoly.append('circle')
.attr("cx", polyPoints[i][0])
.attr("cy", polyPoints[i][1])
.attr("r", 4)
.call(dragBehavior);
}
isDrawing = false;
isDragging = true;
bbox = polyEl._groups[0][0].getBBox();
var bbox2 = gPoly._groups[0][0].getBBox();
//Altering the bounding box's attribute of polygon
bbox.x = 0;
bbox.y = 0;
bbox.width = 50;
bbox.height = 50;
gPoly.attr("transform", "translate(" + 0 + "," + 0 + ")");
// polyEL.attr("transform", "translate(" + 0 + "," + 0 + ")");
//
// gPoly.call(d3.drag().on("drag", movePolygon(bbox)));
}
I want to make the entire polygon draggable. I tried getting the Bounding Box of the drawn Polygon and setting the X and Y coordinates to 0 then translating it on drag like I did for the circle and rectangle elements in this fiddle but changing any of the polygon's bounding box properties don't seem to have an affect on the polygon element. However translating for the polygon works.
Is there any other way other than looping through the polygon's 2 dimensional array of coordinates and updating all the coordinate points on to implement a draggable polygon?
I'm really not following all this getBBox()
stuff. Why don't you drag the element using the traditional way?
gPoly.call(d3.drag().on("drag", function(d) {
d3.select(this).attr("transform", "translate(" +
(d.x = d3.event.x) + "," + (d.y = d3.event.y) + ")")
}));
Here is your code with that change:
d3.select('#poly').on('click', function() {
new Polygon();
});
var w = 600,
h = 500;
var svgCanvas = d3.select('body').append('svg').attr("width", w).attr("height", h);
function Polygon() {
var polyPoints = [];
var gContainer = svgCanvas.append('g').classed("outline", true);
var isDrawing = false;
var isDragging = false;
var linePoint1, linePoint2;
var startPoint;
var bbox;
var boundingRect;
var shape;
var gPoly;
var polyDraw = svgCanvas.on("mousedown", setPoints)
.on("mousemove", drawline)
.on("mouseup", decidePoly);
var dragBehavior = d3.drag().on("drag", alterPolygon);
// var dragPolygon = d3.drag().on("drag", movePolygon(bbox));
//On mousedown - setting points for the polygon
function setPoints() {
if (isDragging) return;
isDrawing = true;
var plod = d3.mouse(this);
linePoint1 = {
x: plod[0],
y: plod[1]
};
polyPoints.push(plod);
var circlePoint = gContainer.append("circle")
.attr("cx", linePoint1.x)
.attr("cy", linePoint1.y)
.attr("r", 4)
.attr("start-point", true)
.classed("handle", true)
.style("cursor", "pointer");
// on setting points if mousedown on a handle
if (d3.event.target.hasAttribute("handle")) {
completePolygon()
}
}
//on mousemove - appending SVG line elements to the points
function drawline() {
if (isDrawing) {
linePoint2 = d3.mouse(this);
gContainer.select('line').remove();
gContainer.append('line')
.attr("x1", linePoint1.x)
.attr("y1", linePoint1.y)
.attr("x2", linePoint2[0] - 2) //arbitary value must be substracted due to circle cursor hover not working
.attr("y2", linePoint2[1] - 2); // arbitary values must be tested
}
}
//On mouseup - Removing the placeholder SVG lines and adding polyline
function decidePoly() {
gContainer.select('line').remove();
gContainer.select('polyline').remove();
var polyline = gContainer.append('polyline').attr('points', polyPoints);
gContainer.selectAll('circle').remove();
for (var i = 0; i < polyPoints.length; i++) {
var circlePoint = gContainer.append('circle')
.attr('cx', polyPoints[i][0])
.attr('cy', polyPoints[i][1])
.attr('r', 4)
.attr("handle", true)
.classed("handle", true);
}
}
//Called on mousedown if mousedown point if a polygon handle
function completePolygon() {
d3.select('g.outline').remove();
gPoly = svgCanvas.append('g')
.classed("polygon", true);
polyPoints.splice(polyPoints.length - 1);
//console.log(polyPoints);
polyEl = gPoly.append("polygon")
.attr("points", polyPoints);
for (var i = 0; i < polyPoints.length; i++) {
gPoly.append('circle')
.attr("cx", polyPoints[i][0])
.attr("cy", polyPoints[i][1])
.attr("r", 4)
.call(dragBehavior);
}
isDrawing = false;
isDragging = true;
bbox = polyEl._groups[0][0].getBBox();
var bbox2 = gPoly._groups[0][0].getBBox();
bbox.x = 0;
bbox.y = 0;
bbox.width = 50;
bbox.height = 50;
// debugger;
gPoly.datum({
x: 0,
y: 0
})
//console.log(bbox);
gPoly.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"
});
// polyEL.attr("transform", "translate(" + 0 + "," + 0 + ")");
//
gPoly.call(d3.drag().on("drag", function(d) {
d3.select(this).attr("transform", "translate(" + (d.x = d3.event.x) + "," + (d.y = d3.event.y) + ")")
}));
}
//Altering polygon coordinates based on handle drag
function alterPolygon() {
if (isDrawing === true) return;
var alteredPoints = [];
var selectedP = d3.select(this);
var parentNode = d3.select(this.parentNode);
//select only the elements belonging to the parent <g> of the selected circle
var circles = d3.select(this.parentNode).selectAll('circle');
var polygon = d3.select(this.parentNode).select('polygon');
var pointCX = d3.event.x;
var pointCY = d3.event.y;
//rendering selected circle on drag
selectedP.attr("cx", pointCX).attr("cy", pointCY);
//loop through the group of circle handles attatched to the polygon and push to new array
for (var i = 0; i < polyPoints.length; i++) {
var circleCoord = d3.select(circles._groups[0][i]);
var pointCoord = [circleCoord.attr("cx"), circleCoord.attr("cy")];
alteredPoints[i] = pointCoord;
}
//re-rendering polygon attributes to fit the handles
polygon.attr("points", alteredPoints);
bbox = parentNode._groups[0][0].getBBox();
console.log(bbox);
}
function movePolygon() {
}
function prepareTransform(bboxVal) {
var originalPosition = {
x: bboxVal.x,
y: bboxVal.y
};
console.log(bboxVal);
console.log(bbox);
bbox.x = 0;
bbox.y = 0;
// //render a bounding box
// shape.rectEl.attr("x", bbox.x).attr("y", bbox.y).attr("height", bbox.height).attr("width", bbox.width);
//
// //drag points
// shape.pointEl1.attr("cx", bbox.x).attr("cy", bbox.y).attr("r", 4);
// shape.pointEl2.attr("cx", (bbox.x + bbox.width)).attr("cy", (bbox.y + bbox.height)).attr("r", 4);
// shape.pointEl3.attr("cx", bbox.x + bbox.width).attr("cy", bbox.y).attr("r", 4);
// shape.pointEl4.attr("cx", bbox.x).attr("cy", bbox.y + bbox.height).attr("r", 4);
return originalPosition;
}
}
h1 {
text-align: center;
}
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 60%;
/* depends on svg ratio, for my zebra height/width = 1.2 so padding-bottom = 50% * 1.2 = 60% */
vertical-align: middle;
/* top | middle | bottom ... do what you want */
}
.my-svg {
/* svg into : object, img or inline */
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%;
/* only required for <img /> */
z-index: 0;
}
svg {
border: solid 1px rgba(221, 61, 16, 0.71);
}
.rectangle {
fill: lightblue;
stroke: blue;
stroke-width: 2px;
fill-opacity: 0.5;
}
.rectangle-bind {
fill: none;
stroke: #081c4e;
stroke-width: 1px;
stroke-dasharray: 5;
}
circle {
fill: lightgreen;
stroke: green;
stroke-width: 2px;
fill-opacity: 0.5;
}
.rect-active {
fill: #1578db;
}
.pointC-active {
fill: #FF8F00;
}
.circle-active {
fill: #4CAF50;
}
path {
fill: #ffb7b3;
stroke: #ff4736;
stroke-width: 5px;
}
polygon {
fill: #b6eeff;
fill-opacity: 0.5;
stroke: #0067ff;
stroke-width: 2px;
}
line {
fill: none;
stroke: #cd08ff;
stroke-width: 2px;
}
polyline {
fill: none;
stroke: #563aff;
stroke-width: 2px;
}
circle.handle {
fill: yellow;
stroke: #cb9c0f;
stroke-width: 2px;
cursor: pointer;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<button id='poly'>Poly</button>
PS: Don't use _groups[0][0]
. Use node()
instead.