fabricjs how i can make an angle measurement? - fabricjs

i want to use fabricjs for create an Object and can measure some images sections .
Update:
I follow sample of http://fabricjs.com/stickman/ and seems I have something but need to improve more
here is what i have https://jsfiddle.net/mavirroco/gtfw58st/
(function () {
var canvas = this.__canvas = new fabric.Canvas('c', {
selection: false
});
var text1 = new fabric.Text('0 Deg', {
fontSize: 20,
fontFamily: 'Georgia',
top: 10,
left: 100
});
canvas.add(text1);
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
function makeCircle(left, top, line1, line2, line3, line4) {
var c = new fabric.Triangle({
left: left,
top: top,
strokeWidth: 5,
fill: '#fff',
stroke: '#666',
angle: -180,
width: 10,
height: 10
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
c.line3 = line3;
c.line4 = line4;
return c;
}
function makeLine(coords) {
return new fabric.Line(coords, {
fill: 'red',
stroke: 'red',
strokeWidth: 5,
selectable: false
});
}
var line2 = makeLine([250, 175, 250, 250]),
line3 = makeLine([250, 250, 300, 350]),
line4 = makeLine([250, 250, 200, 350]);
canvas.add(line3, line4);
canvas.add(
makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
makeCircle(line3.get('x2'), line3.get('y2'), line3),
makeCircle(line4.get('x2'), line4.get('y2'), line4));
canvas.on('object:moving', function (e) {
var p = e.target;
p.line1 && p.line1.set({
'x2': p.left,
'y2': p.top
});
p.line2 && p.line2.set({
'x1': p.left,
'y1': p.top
});
p.line3 && p.line3.set({
'x1': p.left,
'y1': p.top
});
p.line4 && p.line4.set({
'x1': p.left,
'y1': p.top
});
canvas.renderAll();
dy = line3.get('y2') - line4.get('y2');
dx = line3.get('x2') - line4.get('x2');
theta = Math.atan2(dy, dx);
theta *= 180 / Math.PI // rads to degs
text1.setText(parseFloat(theta).toFixed(2));
});
})();

y11 = line3.get('y1');
y12 = line3.get('y2');
y21 = line4.get('y1');
y22 = line4.get('y2');
x11 = line3.get('x1');
x12 = line3.get('x2');
x21 = line4.get('x1');
x22 = line4.get('x2');
angle1 = Math.atan2(y11 - y12, x11 - x12);
angle2 = Math.atan2(y21 - y22, x21 - x22);
angle = angle1 - angle2;
angle = angle*180/Math.PI;
if(angle < 0) angle = -angle;
if(360 - angle < angle) angle = 360 - angle;
text1.setText(angle.toString());
https://jsfiddle.net/gtfw58st/5/

Related

I want to do infinite resize and infinite repeat backgroundImage on canvas with fabric.js

Here is my code where it is working zoom but the background image is not repeat on zoom with fabric.
Here I tried repeat with pattern but still not repeat the background. how can I achieve this when zoom perform with infinite on canvas and then repeat on image of background set.
<canvas
width="2000"
height="1000"
id="canvas"
style="border: 1px solid #ccc;"
></canvas>
</div>
function init() {
let canvas = new fabric.Canvas('canvas')
var bg = new fabric.Rect({ width: 2000, height: 1000, stroke: 'white', strokeWidth: 0, fill: '', evented: false, selectable: false });
bg.fill = new fabric.Pattern({ source: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAASElEQVQ4y2NkYGD4z0A6+M3AwMBKrGJWBgYGZiibEQ0zIInDaCaoelYyHYcX/GeitomjBo4aOGrgQBj4b7RwGFwGsjAwMDAAAD2/BjgezgsZAAAAAElFTkSuQmCC', repeat: 'repeat' },
function () { bg.dirty = false; canvas.requestRenderAll() });
bg.canvas = canvas;
canvas.backgroundImage = bg;
canvas.on('mouse:wheel', function (opt) {
var delta = opt.e.deltaY;
var zoom = canvas.getZoom();
zoom *= 0.999 ** delta;
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
canvas.zoomToPoint({ x: opt.e.offsetX, y: opt.e.offsetY }, zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
var vpt = this.viewportTransform;
if (zoom < 0.4) {
vpt[4] = 200 - 1000 * zoom / 2;
vpt[5] = 200 - 1000 * zoom / 2;
} else {
if (vpt[4] >= 0) {
vpt[4] = 0;
} else if (vpt[4] < canvas.getWidth() - 1000 * zoom) {
vpt[4] = canvas.getWidth() - 1000 * zoom;
}
if (vpt[5] >= 0) {
vpt[5] = 0;
} else if (vpt[5] < canvas.getHeight() - 1000 * zoom) {
vpt[5] = canvas.getHeight() - 1000 * zoom;
}
}
});
}
onMounted(() => {
init()
})

Fabricjs mask object with transformation

I'm trying to mask an object using Fabric.js free drawing brush. It works fine if the object is in its default position and without any transformations. But once I add transformations to the object, the mask is placed in the wrong position. I'm not sure how to solve this. Can someone take a look?
I want to be able to apply any transformations, before or after the mask, without messing up the mask.
let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;
canvas.on("path:created", function(options) {
clip(options.path);
});
function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);
let mask = new fabric.Path(path.path, {
top: object.top,
left: object.left,
objectCaching: false,
strokeWidth: 0,
pathOffset: {
x: 0,
y: 0
}
});
let originalObjLeft = object.left,
originalObjTop = object.top;
object.set({
clipTo: function(ctx) {
mask.set({
left: -object.width / 2 - mask.width / 2 - originalObjLeft,
top: -object.height / 2 - mask.height / 2 - originalObjTop,
objectCaching: false
});
mask.render(ctx);
}
});
canvas.requestRenderAll();
}
// image
let image = new Image();
let object;
image.onload = function() {
object = new fabric.Image(image, {
width: 500,
height: 500,
//scaleX: 0.8,
//scaleY: 0.8,
//angle: 45,
top: 50,
left: 300
});
canvas.add(object);
};
image.src = "http://i.imgur.com/8rmMZI3.jpg";
I implement an exemple with some transformations (scaleX,scaleY,left,top).
I'm strugle to find a solution when the inital object have an angle different than 0. For the current solution I need it to divide the maskscale with the object scale and also adjust the positions.
let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;
canvas.on("path:created", function(options) {
clip(options.path);
});
function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);
let mask = new fabric.Path(path.path, {
top: object.top,
left: object.left,
objectCaching: false,
strokeWidth: 0,
scaleX : 1/object.scaleX,
scaleY : 1/object.scaleY,
pathOffset: {
x: 0,
y: 0
}
});
let originalObjLeft = object.left,
originalObjTop = object.top,
originalMaskScaleX = mask.scaleX,
originalMaskScaleY = mask.scaleY,
originalObjScaleX = object.scaleX,
originalObjScaleY = object.scaleY;
object.set({
clipTo: function(ctx) {
mask.set({
left: -object.width / 2 -( mask.width / 2 * originalMaskScaleX) - originalObjLeft/originalObjScaleX ,
top: -object.height / 2 -( mask.height / 2 * originalMaskScaleY) - originalObjTop/originalObjScaleY ,
objectCaching: false
});
mask.render(ctx);
}
});
canvas.requestRenderAll();
}
// image
let image = new Image();
image.onload = function() {
object = new fabric.Image(image, {
width: 500,
height: 500,
scaleX: 0.8,
scaleY: 0.8,
// angle: 45,
top: 50,
left: 100
});
canvas.add(object);
};
image.src = "http://i.imgur.com/8rmMZI3.jpg";
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<div class="canvas__wrapper">
<canvas id="canvas" width="1280" height="720"></canvas>
</div>
You can check here for loadFromJSON support.
The only problem remains is when the object is rotated.
Basically whenever you set an angle, your context matrix has been transformed. In order to mask properly you need to return to initial state of the Transformation Matrices. Fabricjs handles first matrix with center point of an object (calculates center of an object with or without an angle). Second matrix is rotating matrix, and third - scaling.
To display image with all options which are set to an object, you need to multiply all Matrices:
(First Matrix * Second Matrix) * Third Matrix
So the idea of clipping will be reverse engineering of rotating context and multiplications of matrices:
difference between center points of regular object without rotation and center point of the same object but with rotation. After that take result of subtractions and divide by original object scale value.
let canvas = new fabric.Canvas("canvas", {
backgroundColor: "lightgray",
width: 1280,
height: 720,
preserveObjectStacking: true,
selection: false,
stateful: true
});
const angle = 45;
let objectHasBeenRotated = false;
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = "black";
canvas.freeDrawingBrush.width = 2;
canvas.on("path:created", function (options) {
clip(options.path);
});
function clip(path) {
canvas.isDrawingMode = false;
canvas.remove(path);
let mask = new fabric.Path(path.path, {
top: 0,
left: 0,
objectCaching: false,
strokeWidth: 0,
scaleX: 1 / object.scaleX,
scaleY: 1 / object.scaleY,
pathOffset: {
x: 0,
y: 0,
}
});
let originalObjLeft = object.left,
originalObjTop = object.top,
originalMaskScaleX = mask.scaleX,
originalMaskScaleY = mask.scaleY,
originalObjScaleX = object.scaleX,
originalObjScaleY = object.scaleY,
transformedTranslate = object.translateToGivenOrigin({
x: object.left,
y: object.top
}, object.originX, object.originY, 'center', 'center'),
originalTransformLeft = transformedTranslate.x - object.getCenterPoint().x,
originalTransformTop = transformedTranslate.y - object.getCenterPoint().y;
object.set({
clipTo: function (ctx) {
ctx.save();
ctx.rotate(-angle * Math.PI / 180);
ctx.translate(originalTransformLeft / originalObjScaleX, originalTransformTop / originalObjScaleY)
mask.set({
left: -object.width / 2 - (mask.width / 2 * originalMaskScaleX) - originalObjLeft / originalObjScaleX,
top: -object.height / 2 - (mask.height / 2 * originalMaskScaleY) - originalObjTop / originalObjScaleY,
objectCaching: false
});
mask.render(ctx);
ctx.restore();
}
});
canvas.requestRenderAll();
}
// image
let image = new Image();
image.onload = function () {
object = new fabric.Image(image, {
width: 500,
height: 500,
scaleX: 0.8,
scaleY: 0.8,
angle: angle,
top: 50,
left: 300,
id: 'pug'
});
canvas.add(object);
};
image.src = "http://i.imgur.com/8rmMZI3.jpg";
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.3.6/fabric.js"></script>
<div class="canvas__wrapper">
<canvas id="canvas" width="1280" height="720"></canvas>
</div>

Create a SVG object with a arrow line Highcharts

I need to draw a arrow line pointing to a particular point in my highcharts.
I was able to get the line drawn but not sure how to have a arrow before that being displayed.
Here is my fiddle and code.
http://jsfiddle.net/48kyq3wq/1/
$.getJSON('https://www.highcharts.com/samples/data/jsonp.php?filename=usdeur.json&callback=?', function(data) {
var callout = function(chart) {
$('.cO').remove();
var xAxis = chart.xAxis[0],
yAxis = chart.yAxis[0],
point = chart.series[0].data[100],
lineXLength = 100,
lineYLength = -50;
chart.renderer.path(['M', xAxis.toPixels(point.x), yAxis.toPixels(point.y), 'L', xAxis.toPixels(point.x) + lineXLength, yAxis.toPixels(point.y) + lineYLength,]).attr({
'stroke-width': 5,
stroke: 'red',
zIndex: 0
}).addClass('C0').add();
chart.renderer.label('Custom label', xAxis.toPixels(point.x) + lineXLength, yAxis.toPixels(point.y) + lineYLength - 22, 'rect')
.css({
color: '#FFFFFF'
})
.attr({
fill: 'rgba(0, 0, 0, 0.75)',
padding: 8,
r: 5,
width: 100,
height: 30,
zIndex: 6
}).addClass('cO')
.add();
};
Highcharts.chart('container', {
chart: {
events: {
load: function() {
callout(this);
},
redraw: function() {
callout(this);
}
}
},
xAxis: {
type: 'datetime'
},
legend: {
enabled: false
},
series: [{
data: data
}]
});
});
You need to create an arrow and rotate it depending on the position of point and the label.
Create a path which connects the label and the point
const x0 = label.x;
const y0 = label.y + label.height / 2;
const x1 = x;
const y1 = y;
const path = chart.renderer.path([
'M', label.x, label.y + label.height / 2,
'L', x, y,
]).attr({
'stroke-width': 1,
stroke: 'black',
zIndex: 6
}).add()
Create a method for rotating points. It will rotate the point around the top point of the arrow
function rotatePoint(c, angle, p) {
const sin = Math.sin(angle);
const cos = Math.cos(angle);
const x = p[0] - c[0];
const y = p[1] - c[1];
const nx = x * cos - y * sin;
const ny = x * sin + y * cos;
return [nx + c[0], ny + c[1]];
}
Create an arrow, find the angle between the label and the point and rotate the arrow according to the angle.
const array = [[x1 - 5, y1 - 15], [x1 + 5, y1 - 15], [x1, y1], [x1 - 5, y1- 15]]
const angle = Math.atan2(y0 - y1, x0 - x1) + 90 * Math.PI / 180;
const ma = array.map(point => rotatePoint(array[2], angle, point));
Render the arrow
const arrow = chart.renderer.path({
fill: 'black',
zIndex: 6,
d: ['M', ma[0].join(' '), 'L', ma[1].join(' '), ma[2].join(' '), ma[3].join(' ')].join(' ')
}).add()
You should also consider cases when the point is on the right of the label and the top of the label - rotation and calculation of the angle stay the same but connecting the point with the label needs to be adjusted.
Live example and output
http://jsfiddle.net/savpscya/

fabricJS Not persisting Floodfill

I have an algorithm for Floodfilling a canvas. Im trying to incorporate this with fabricJS. So here is the dilemna.... I create a fabric.Canvas(). Which creates a wrapper canvas and also an upper-canvas canvas. I click on the canvas to apply my Floodfill(). This works fine and applies my color. But as soon as i go to drag my canvas objects around, or add additional objects to the canvas, the color disappears and looks like it resets of sort.
Any idea why this is?
This happen because fabricjs wipe out all canvas every frame and redraw from its internal data.
I made a JSfiddle that implements Flood Fill for Fabric JS. Check it here: https://jsfiddle.net/av01d/dfvp9j2u/
/*
* FloodFill for fabric.js
* #author Arjan Haverkamp (av01d)
* #date October 2018
*/
var FloodFill = {
// Compare subsection of array1's values to array2's values, with an optional tolerance
withinTolerance: function(array1, offset, array2, tolerance)
{
var length = array2.length,
start = offset + length;
tolerance = tolerance || 0;
// Iterate (in reverse) the items being compared in each array, checking their values are
// within tolerance of each other
while(start-- && length--) {
if(Math.abs(array1[start] - array2[length]) > tolerance) {
return false;
}
}
return true;
},
// The actual flood fill implementation
fill: function(imageData, getPointOffsetFn, point, color, target, tolerance, width, height)
{
var directions = [[1, 0], [0, 1], [0, -1], [-1, 0]],
coords = [],
points = [point],
seen = {},
key,
x,
y,
offset,
i,
x2,
y2,
minX = -1,
maxX = -1,
minY = -1,
maxY = -1;
// Keep going while we have points to walk
while (!!(point = points.pop())) {
x = point.x;
y = point.y;
offset = getPointOffsetFn(x, y);
// Move to next point if this pixel isn't within tolerance of the color being filled
if (!FloodFill.withinTolerance(imageData, offset, target, tolerance)) {
continue;
}
if (x > maxX) { maxX = x; }
if (y > maxY) { maxY = y; }
if (x < minX || minX == -1) { minX = x; }
if (y < minY || minY == -1) { minY = y; }
// Update the pixel to the fill color and add neighbours onto stack to traverse
// the fill area
i = directions.length;
while (i--) {
// Use the same loop for setting RGBA as for checking the neighbouring pixels
if (i < 4) {
imageData[offset + i] = color[i];
coords[offset+i] = color[i];
}
// Get the new coordinate by adjusting x and y based on current step
x2 = x + directions[i][0];
y2 = y + directions[i][1];
key = x2 + ',' + y2;
// If new coordinate is out of bounds, or we've already added it, then skip to
// trying the next neighbour without adding this one
if (x2 < 0 || y2 < 0 || x2 >= width || y2 >= height || seen[key]) {
continue;
}
// Push neighbour onto points array to be processed, and tag as seen
points.push({ x: x2, y: y2 });
seen[key] = true;
}
}
return {
x: minX,
y: minY,
width: maxX-minX,
height: maxY-minY,
coords: coords
}
}
}; // End FloodFill
var fcanvas; // Fabric Canvas
var fillColor = '#f00';
var fillTolerance = 2;
function hexToRgb(hex, opacity) {
opacity = Math.round(opacity * 255) || 255;
hex = hex.replace('#', '');
var rgb = [], re = new RegExp('(.{' + hex.length/3 + '})', 'g');
hex.match(re).map(function(l) {
rgb.push(parseInt(hex.length % 2 ? l+l : l, 16));
});
return rgb.concat(opacity);
}
function floodFill(enable) {
if (!enable) {
fcanvas.off('mouse:down');
fcanvas.selection = true;
fcanvas.forEachObject(function(object){
object.selectable = true;
});
return;
}
fcanvas.deactivateAll().renderAll(); // Hide object handles!
fcanvas.selection = false;
fcanvas.forEachObject(function(object){
object.selectable = false;
});
fcanvas.on({
'mouse:down': function(e) {
var mouse = fcanvas.getPointer(e.e),
mouseX = Math.round(mouse.x), mouseY = Math.round(mouse.y),
canvas = fcanvas.lowerCanvasEl,
context = canvas.getContext('2d'),
parsedColor = hexToRgb(fillColor),
imageData = context.getImageData(0, 0, canvas.width, canvas.height),
getPointOffset = function(x,y) {
return 4 * (y * imageData.width + x)
},
targetOffset = getPointOffset(mouseX, mouseY),
target = imageData.data.slice(targetOffset, targetOffset + 4);
if (FloodFill.withinTolerance(target, 0, parsedColor, fillTolerance)) {
// Trying to fill something which is (essentially) the fill color
console.log('Ignore... same color')
return;
}
// Perform flood fill
var data = FloodFill.fill(
imageData.data,
getPointOffset,
{ x: mouseX, y: mouseY },
parsedColor,
target,
fillTolerance,
imageData.width,
imageData.height
);
if (0 == data.width || 0 == data.height) {
return;
}
var tmpCanvas = document.createElement('canvas'), tmpCtx = tmpCanvas.getContext('2d');
tmpCanvas.width = canvas.width;
tmpCanvas.height = canvas.height;
var palette = tmpCtx.getImageData(0, 0, tmpCanvas.width, tmpCanvas.height); // x, y, w, h
palette.data.set(new Uint8ClampedArray(data.coords)); // Assuming values 0..255, RGBA
tmpCtx.putImageData(palette, 0, 0); // Repost the data.
var imgData = tmpCtx.getImageData(data.x, data.y, data.width, data.height); // Get cropped image
tmpCanvas.width = data.width;
tmpCanvas.height = data.height;
tmpCtx.putImageData(imgData,0,0);
fcanvas.add(new fabric.Image(tmpCanvas, {
left: data.x,
top: data.y,
selectable: false
}))
}
});
}
$(function() {
// Init Fabric Canvas:
fcanvas = new fabric.Canvas('c', {
backgroundColor:'#fff',
enableRetinaScaling: false
});
// Add some demo-shapes:
fcanvas.add(new fabric.Circle({
radius: 80,
fill: false,
left: 100,
top: 100,
stroke: '#000',
strokeWidth: 2
}));
fcanvas.add(new fabric.Triangle({
width: 120,
height: 160,
left: 50,
top: 50,
stroke: '#000',
fill: '#00f',
strokeWidth: 2
}));
fcanvas.add(new fabric.Rect({
width: 120,
height: 160,
left: 150,
top: 50,
fill: 'red',
stroke: '#000',
strokeWidth: 2
}));
fcanvas.add(new fabric.Rect({
width: 200,
height: 120,
left: 200,
top: 120,
fill: 'green',
stroke: '#000',
strokeWidth: 2
}));
/* Images work very well too. Make sure they're CORS
enabled though! */
var img = new Image();
img.crossOrigin = 'anonymous';
img.onload = function() {
fcanvas.add(new fabric.Image(img, {
left: 300,
top: 100,
angle: 30,
}));
}
img.src = 'http://misc.avoid.org/chip.png';
});

Problems when interactively resizing and moving svg rectangles with D3

I am trying to create panel components that will hold some visualizations.
I am making the panel component with svgs. They look ok, but I am getting some weird behavior when resizing and moving the panels.
var groups = ["uno", "dos", "tres", "cuatro"];
var w = 350;
var x = 0;
var y = 0;
var width = 800;
var h = 200;
var height = 800;
var val = [];
var drag = d3.behavior.drag()
.origin(Object)
.on("drag", move);
var resize = d3.behavior.drag()
.origin(Object)
.on("drag", dragResize);
svg = d3.select("body").append("div").append("svg");
charts = svg.selectAll("g.chart")
.data(groups); //(dims);
box = charts.enter()
.append("g").classed("chart", true)
.attr("id", function(d,i) { return "box"+i})
//.data([{x: 95, y: 0}]);
box.append("rect").classed("box", true)
var t = box.append("rect").classed("titleBox", true)
t.call(drag);
box.append("text").classed("title", true).data(groups)
box.append("text").classed("legend", true).data(groups)
box.append("rect").classed("icon", true)
.call(resize);
box.selectAll("rect.box")
.data([{x: 95, y: 0}])
.attr({
x: function(d) { return d.x; },
y: function(d) { return d.y; },
width: w,
height: function(d) { return 200}//d.length*30 + 60}
})
box.selectAll("rect.titleBox")
.classed("drag", true)
.data([{x: 95, y: 0}])
.attr({
x: function(d) { return d.x; },
y: function(d) { return d.y; },
width: w,
height: 25,
fill: "#000000"
})
box.selectAll("text.title")
.attr({
x: 105,
y: 20,
width: 350,
height: 25,
fill: "#ffffff"
})
.text(function(d) {
console.log("i from title "+ d);
return d;
})
box.selectAll("text.legend")
.attr({
x: 105,
y: 45,
width: 200,
height: 25,
fill: "#999999"
})
.text(function(d) {
return d;
})
box.selectAll("rect.icon")
.data([{x: 429, y: 184}])
.attr({
x: function(d) { return d.x; },
y: function(d) { return d.y; },
width: 16,
height: 16,
fill: "#999999"
})
var dx = 429;
var dy = 184;
function move(){
var dragTarget = d3.select(this);
var dragObject = d3.select(this.parentNode);
console.log("move x:"+x+" y:"+y);
//console.log("d3.event.x:"+d3.event.x+" d3.event.y:"+d3.event.y);
x += d3.event.x - parseInt(dragTarget.attr('x'));
y += d3.event.y - parseInt(dragTarget.attr("y"));
console.log("x:"+x+" y:"+y);
dragObject
.attr("transform", "translate(" + x + "," + y + ")")
};
function dragResize(){
var dragx = Math.max(dx + (16/2), Math.min(w, dx + width + d3.event.dx));
var dragy = Math.max(dy + (16/2), Math.min(h, dy + height + d3.event.dy));
//console.log("resize x:"+x+" y:"+y);
console.log("d3.event.x:"+d3.event.dx+" d3.event.y:"+d3.event.dy);
var dragTarget = d3.select(this);
var dragObject = d3.select(this.parentNode);
var o = dragObject.select("rect.box");
var o1 = dragObject.select("rect.titleBox");
var oldx = dx;
var oldy = dy;
dx = Math.max(0, Math.min(dx + width - (16 / 2), d3.event.x));
dy = Math.max(0, Math.min(dy + height - (16 ), d3.event.y));
w = w - (oldx - dx);
h = h - (oldy - dy);
dragTarget
.attr("x", function(d) { return dragx - (16/2) })
.attr("y", function(d) { return dragy - (16) })
o.attr("width", w)
.attr("height", h);
o1.attr("width", w);
};
I have posted the code at http://jsfiddle.net/dtqY5/
The problem is the following: I can move each panel, by dragging the title area, with no problem. Howvwer, after I resize any of the panels, I cannot move them anymore. They jump to their original position. The x and y becones NaN, but I cannot understand why.
ANy ideas and suggestions will be welcomed.
D3 uses the drag.origin accessor you provide to calculate an offset. Since the access you provide is just an empty object, this offset is NaN which results in x and y on the event also being NaN.
If you remove drag.origin altogether it uses the current mouse position as the origin which makes the panels jump when you start dragging. If you specify the origin to be the position of the shape being dragged it looks better:
.origin(function() {
var current = d3.select(this);
return {x: current.attr("x"), y: current.attr("y") };
})
Here's an updated fiddle: http://jsfiddle.net/4nvhc/

Resources