JointJS: item added to incorrect position after panning paperScroller - javascript

I'm having trouble to get the correct position to items added to the graph after moving items to a new area on the left side of the paper.
Function called when the item is drop on the canvas.
function drop(ev) {
ev.preventDefault();
// CANVAS ELEMENT DROP
var data = ev.dataTransfer.getData("text");
console.log('ev.pageX: ' + ev.pageX);
console.log('$(paper-container).offset().left: ' + $('#paper-container').offset().left);
console.log('$(.joint-paper).position().left: ' + $('.joint-paper').position().left);
console.log('ev.pageY: ' + ev.pageY);
console.log('$(paper-container).offset().top: ' + $('#paper-container').offset().top);
console.log('$(.joint-paper).position().top: ' + $('.joint-paper').position().top);
var x = ev.pageX - $('#paper-container').offset().left - $('.joint-paper').position().left;
var y = ev.pageY - $('#paper-container').offset().top - $('.joint-paper').position().top;
addCanvasItem(x, y);
}
Then adding item to the canvas:
function addCanvasItem(x, y) {
//x and y are in the screen referential
var zoomRatio = vm.paperScroller.zoom();
var scrollX = vm.paperScroller.el.scrollLeft;
var scrollY = vm.paperScroller.el.scrollTop;
var posX = x / zoomRatio + scrollX / zoomRatio - 50;
var posY = y / zoomRatio + scrollY / zoomRatio - 30;
item.attributes.position = { x: posX, y: posY };
item.addTo(vm.graph);
var cellView = item.findView(vm.paper);
rappidJsConfig.resetSelection([cellView.model]);
vm.selectedView = cellView;
vm.initEditor();
if (vm.isVisibleType('qad.Question')) {
var selectedQuestion = document.getElementById("selected-question");
if (selectedQuestion) {
selectedQuestion.focus();
}
}
};
Any ideas ?

Related

drag SVG element to a new location

if (action == "mousedown") {
startx = x;
starty = y;
}
if (action == "mousemove") {
if (!mouseisdown) {
return;
} else {
//console.log(target);
var transformX = x - startx;
var transformY = y - starty;
// console.log(x, y, startx, starty);
var transformAttr = 'translate(' + transformX + ',' + transformY + ')';
test.setAttribute('transform', transformAttr);
}
}
if (action == "mouseup") {
var transformX = x - startx;
var transformY = y - starty;
// console.log(x, y, startx, starty);
var transformAttr = 'translate(' + transformX + ',' + transformY + ')';
test.setAttribute('transform', transformAttr);
}
Below is my way to get x, y position:
$("svg").on("mousedown", function (event) {
mouseisdown = true;
var offset = $("#center").offset(); //#center is the canvas
var x = event.pageX - offset.left;
var y = event.pageY - offset.top;
current_tool("mousedown", x, y, null);
});
Above is part of my code, my problem is this function works well on the first move, but when I tried to move it second time, the element object will suddenly back to its origin location before this first move, How to fix that problem?
Any help is appreciated.
if (action == "mousedown") {
startx = x - test.transform.animVal["0"].matrix.e; //change
starty = y - test.transform.animVal["0"].matrix.f; // change2
}
if (action == "mousemove") {
if (!mouseisdown) {
return;
} else {
if (test.transform.animVal["0"]) {
console.log(test.transform.animVal["0"].matrix);
var transformX = x - startx;
var transformY = y - starty;
var transformAttr = 'translate(' + transformX + ',' + transformY + ')';
test.setAttribute('transform', transformAttr);
}
}
}
if (action == "mouseup") {
var transformX = x - startx;
var transformY = y - starty;
// console.log(x, y, startx, starty);
var transformAttr = 'translate(' + transformX + ',' + transformY + ')';
test.setAttribute('transform', transformAttr);
}
Problem solved by myself, the reason of the problem is that, when I tries the second move, the start position did not take the first transform into consider, so after the change the code in "mousedown" handler, the problem settled.

Raphael not working on chrome

I can use my raphael test page (just playing with it at the moment) in Firefox and IE 11 (eurgh). It doesn't, however, work in Chrome. I've checked the console and no errors have been reported, just the Raphael features won't work (adding a rect, image).
So, firstly:
1. Is chromes support for Raphael different to others?
Secondly, I'll post my code in case I've made a huge error somewhere (left out HTML as it's a basic DIV).
//Get the background image
var myImg = new Image();
myImg.src = 'comp1.bmp';
var width = myImg.width;
var height = myImg.height;
var allShapes = [];
var paper = new Raphael("paper", width, height);
//Add a background image
var img = paper.image("comp1.bmp", 0, 0, width, height);
var cw = width / 2;
var ch = height / 2;
var allShapes = [];
var activeRect; //This will store the rect that was used last.
//drag and drop code
function addNewShape(){
var rectDetails = {};
rectDetails.rect = paper.rect(cw - 50, ch - 50, 50, 50);
rectDetails.rect.attr({
'stroke': 'orange',
'fill': 'white',
'fill-opacity': 0,
'stroke-width': 2
});
//Set the resizer for this rectangle
var resize = paper.rect(cw - 20, ch - 20, 20, 20);
resize.attr({
fill: "orange",
stroke: "none",
opacity: .5
});
//Assign to rect
resize.box = rectDetails.rect;
resize.drag(move_resizer, start_resizer);
rectDetails.rect.sizer = resize;
rectDetails.rect.drag(drag_move, drag_start, drag_up);
//Add co-ordinate text box
var div = document.getElementById('coordContainer');
div.innerHTML = div.innerHTML + "Shape " + rectDetails.rect.id + ": <input type='text' id='pos" + rectDetails.rect.id + "'>";
//Add the new shape to the shape coords array
allShapes[rectDetails.rect.id] = rectDetails.rect;
}
function getCoords(){
var coordDiv = document.getElementById('resultsContainer');
for(var i = 1; i <= allShapes.length; i++) {
var bbox = allShapes[i].getBBox();
var temp = "Shape " + allShapes[i].id + ": X(" + bbox.x + "), Y(" + bbox.y + ")";
coordDiv.innerHTML = coordDiv.innerHTML + temp;
}
}
var ox = null;
var oy = null;
function drag_start(e) {
//Set the active rect
activeRect = this;
//Assign attrs
this.ow = this.attr('width');
this.oh = this.attr('height');
this.ox = this.attr('x');
this.oy = this.attr('y');
this.sizer.ox = this.sizer.attr("x");
this.sizer.oy = this.sizer.attr("y");
this.sizer.attr({opacity: 1});
};
//Moving the shape
function drag_move(dx, dy, posx, posy) {
document.getElementById("pos" + this.id).value = "X: " + dx + " | Y: " + dy; //co-ordinates of the shape!
this.attr({
transform: "...T" + (dx - ox) + "," + (dy - oy)
});
this.sizer.attr({x: this.sizer.ox + dx, y: this.sizer.oy + dy});
// store the previous versions of dx,dy for use in the next move call
ox = dx;
oy = dy;
}
function drag_up(e) {
//reset the original positions
ox = 0;
oy = 0;
}
function start_resizer(){
// storing original coordinates
this.ox = this.attr("x");
this.oy = this.attr("y");
this.box.ow = this.box.attr("width");
this.box.oh = this.box.attr("height");
}
function move_resizer(dx, dy){
// move will be called with dx and dy
this.attr({x: this.ox + dx, y: this.oy + dy});
this.box.attr({width: this.box.ow + dx, height: this.box.oh + dy});
}
NB:
The addShape() function is called from a button.
Chrome shows the div, just it's content is empty (white).
I have no idea what the issue is with this code. Any help greatly received.

Why does my menu jump the second time I try and drag it to a new item?

I've made a rotating menu.
To select an item you rotate the menu by clicking and dragging.
http://codepen.io/PaulBunker/pen/ZGgxvY
var dragging = false;
var links = $('.menu a');
var radius = 520;
var degree = 0.262;
var angle = 0.79;
var orgX;
var orgY;
var offset = $('.menu').offset();
var newangle;
var origradians;
function setItems(angle) {
var internalangle = angle;
links.each(function() {
var y = Math.round(radius * Math.cos(internalangle));
var x = Math.round(radius * Math.sin(internalangle));
$(this).css({
left: x + 'px',
top: 0-y + 'px',
display:'block',
});
$(this).addClass(y);
if (y < 10 & x > 0) {
$(this).addClass('active');
}
if ( y < -10 || y > 0) {
$(this).removeClass('active');
}
internalangle += degree;
});
}
$(function() {
$(document).mousedown(function(evt) {
orgX = evt.pageX - offset.left;
orgY = evt.pageY - offset.top;
orgradians = Math.atan2(orgX, orgY);
dragging = true;
});
$(document).mouseup(function() {
dragging = false;
angle = newangle;
});
$(document).mousemove(function(evt) {
if (dragging) {
var x = evt.pageX - offset.left;
var y = evt.pageY - offset.top;
var radians = Math.atan2(y, x);
newangle = (orgradians + radians) - angle;
console.log (orgradians , radians, angle, newangle);
setItems(newangle);
console.log(newangle);
}
});
setItems(angle);
});
My problem is after the first drag to select an item.
At the beginning of the second drag the menu jumps into the wrong position.
on 'mouseup' I save the 'newangle' variable and override the 'angle variable'.
I suspect there is an error somewhere in the line
newangle = (orgradians + radians) - angle;
I've been tearing my hair out to try and get this to work!
Thanks in advance for any guidance!
-Paul
If you don't use trigonometry on mouse move and just use it once when the radius of the circle is defined it'll fix the issue:
Work out the rotation speed based on the radius of the circle:
var rotationSpeed = Math.atan(1/radius);
Use the rotation speed and the difference in the Y position of the cursor on mouse move:
var newangle = lastangle - (difY*rotationSpeed);
http://codepen.io/stevenarcher/pen/yNmRyP
var dragging = false;
var links = $('.menu a');
var radius = 520;
var degree = 0.262;
var lastY;
var offset = $('.menu').offset();
var lastangle = 0;
var rotationSpeed = Math.atan(1/radius);
function setItems(internalangle) {
links.each(function() {
var y = Math.round(radius * Math.cos(internalangle));
var x = Math.round(radius * Math.sin(internalangle));
$(this).css({
left: x + 'px',
top: 0 - y + 'px',
display:'block',
});
$(this).addClass(y);
if (y < 10) {
$(this).addClass('active');
}
if ( y < -10 || y > 0) {
$(this).removeClass('active');
}
internalangle += degree;
});
}
$(document).mousedown(function(evt) {
lastY = evt.pageY - offset.top;
dragging = true;
});
$(document).mouseup(function(evt) {
dragging = false;
});
$(document).mousemove(function(evt) {
if (dragging) {
var y = evt.pageY - offset.top;
var difY = lastY - y;
lastY = y;
var newangle = lastangle - (difY*rotationSpeed);
setItems(newangle);
lastangle = newangle;
}
});
setItems(0);

Pinch to zoom with Hammer.js

I want a pinch to Zoom function for an image. I want it to zoom in the area where the fingers are.
My index is only
<div id="wrapper" style="-webkit-transform: translate3d(0,0,0);overflow: hidden;">
</div>
And my script for zooming and scrolling is similar with this example. I have made a few changes to fit with my project
My problem is in
case 'transform':
rotation = last_rotation + ev.gesture.rotation;
scale = Math.max(1, Math.min(last_scale * ev.gesture.scale, 10));
break;
How can I change it so that it doesn't zoom into the center of the picture but at place where the first finger have touch the display?
Sorry for my bad english :)
This is an example with hammer.js and tap. As you tap it will zoom in at the point where you tapped. The event data is common for all gestures so switching from tap to pinch should work. It is a good example to work on. You may need to increase the scale step as you pinch. It has been tested on chrome(v30) and firefox (v24).
It is based on the solution mentioned at the thread,
Zoom in on a point (using scale and translate)
as you will see an alternative could also be to use canvas.
HTML
<div style="-webkit-transform: translate3d(0,0,0);overflow: hidden;" class="zoomable">
<img src="http://i.telegraph.co.uk/multimedia/archive/01842/landscape-rainbow_1842437i.jpg" />
</div>
JS
(function ($) {
$(document).ready(function () {
var scale = 1; // scale of the image
var xLast = 0; // last x location on the screen
var yLast = 0; // last y location on the screen
var xImage = 0; // last x location on the image
var yImage = 0; // last y location on the image
Hammer($('.zoomable img').get(0)).on("tap", function (event) {
var posX = event.gesture.center.pageX;
var posY = event.gesture.center.pageY;
// find current location on screen
var xScreen = posX; //- $(this).offset().left;
var yScreen = posY; //- $(this).offset().top;
// find current location on the image at the current scale
xImage = xImage + ((xScreen - xLast) / scale);
yImage = yImage + ((yScreen - yLast) / scale);
scale++;
// determine the location on the screen at the new scale
var xNew = (xScreen - xImage) / scale;
var yNew = (yScreen - yImage) / scale;
// save the current screen location
xLast = xScreen;
yLast = yScreen;
// redraw
$(this).css('-webkit-transform', 'scale(' + scale + ')' + 'translate(' + xNew + 'px, ' + yNew + 'px' + ')')
.css('-webkit-transform-origin', xImage + 'px ' + yImage + 'px').css('-moz-transform', 'scale(' + scale + ') translate(' + xNew + 'px, ' + yNew + 'px)').css('-moz-transform-origin', xImage + 'px ' + yImage + 'px')
.css('-o-transform', 'scale(' + scale + ') translate(' + xNew + 'px, ' + yNew + 'px)').css('-o-transform-origin', xImage + 'px ' + yImage + 'px').css('transform', 'scale(' + scale + ') translate(' + xNew + 'px, ' + yNew + 'px)');
});
});
})(jQuery);
http://jsfiddle.net/SySZL/
Check out the Pinch Zoom and Pan with HammerJS demo. This example has been tested on Android, iOS and Windows Phone.
You can find the source code at Pinch Zoom and Pan with HammerJS.
For your convenience, here is the source code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport"
content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">
<title>Pinch Zoom</title>
</head>
<body>
<div>
<div style="height:150px;background-color:#eeeeee">
Ignore this area. Space is needed to test on the iPhone simulator as pinch simulation on the
iPhone simulator requires the target to be near the middle of the screen and we only respect
touch events in the image area. This space is not needed in production.
</div>
<style>
.pinch-zoom-container {
overflow: hidden;
height: 300px;
}
.pinch-zoom-image {
width: 100%;
}
</style>
<script src="https://hammerjs.github.io/dist/hammer.js"></script>
<script>
var MIN_SCALE = 1; // 1=scaling when first loaded
var MAX_SCALE = 64;
// HammerJS fires "pinch" and "pan" events that are cumulative in nature and not
// deltas. Therefore, we need to store the "last" values of scale, x and y so that we can
// adjust the UI accordingly. It isn't until the "pinchend" and "panend" events are received
// that we can set the "last" values.
// Our "raw" coordinates are not scaled. This allows us to only have to modify our stored
// coordinates when the UI is updated. It also simplifies our calculations as these
// coordinates are without respect to the current scale.
var imgWidth = null;
var imgHeight = null;
var viewportWidth = null;
var viewportHeight = null;
var scale = null;
var lastScale = null;
var container = null;
var img = null;
var x = 0;
var lastX = 0;
var y = 0;
var lastY = 0;
var pinchCenter = null;
// We need to disable the following event handlers so that the browser doesn't try to
// automatically handle our image drag gestures.
var disableImgEventHandlers = function () {
var events = ['onclick', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover',
'onmouseup', 'ondblclick', 'onfocus', 'onblur'];
events.forEach(function (event) {
img[event] = function () {
return false;
};
});
};
// Traverse the DOM to calculate the absolute position of an element
var absolutePosition = function (el) {
var x = 0,
y = 0;
while (el !== null) {
x += el.offsetLeft;
y += el.offsetTop;
el = el.offsetParent;
}
return { x: x, y: y };
};
var restrictScale = function (scale) {
if (scale < MIN_SCALE) {
scale = MIN_SCALE;
} else if (scale > MAX_SCALE) {
scale = MAX_SCALE;
}
return scale;
};
var restrictRawPos = function (pos, viewportDim, imgDim) {
if (pos < viewportDim/scale - imgDim) { // too far left/up?
pos = viewportDim/scale - imgDim;
} else if (pos > 0) { // too far right/down?
pos = 0;
}
return pos;
};
var updateLastPos = function (deltaX, deltaY) {
lastX = x;
lastY = y;
};
var translate = function (deltaX, deltaY) {
// We restrict to the min of the viewport width/height or current width/height as the
// current width/height may be smaller than the viewport width/height
var newX = restrictRawPos(lastX + deltaX/scale,
Math.min(viewportWidth, curWidth), imgWidth);
x = newX;
img.style.marginLeft = Math.ceil(newX*scale) + 'px';
var newY = restrictRawPos(lastY + deltaY/scale,
Math.min(viewportHeight, curHeight), imgHeight);
y = newY;
img.style.marginTop = Math.ceil(newY*scale) + 'px';
};
var zoom = function (scaleBy) {
scale = restrictScale(lastScale*scaleBy);
curWidth = imgWidth*scale;
curHeight = imgHeight*scale;
img.style.width = Math.ceil(curWidth) + 'px';
img.style.height = Math.ceil(curHeight) + 'px';
// Adjust margins to make sure that we aren't out of bounds
translate(0, 0);
};
var rawCenter = function (e) {
var pos = absolutePosition(container);
// We need to account for the scroll position
var scrollLeft = window.pageXOffset ? window.pageXOffset : document.body.scrollLeft;
var scrollTop = window.pageYOffset ? window.pageYOffset : document.body.scrollTop;
var zoomX = -x + (e.center.x - pos.x + scrollLeft)/scale;
var zoomY = -y + (e.center.y - pos.y + scrollTop)/scale;
return { x: zoomX, y: zoomY };
};
var updateLastScale = function () {
lastScale = scale;
};
var zoomAround = function (scaleBy, rawZoomX, rawZoomY, doNotUpdateLast) {
// Zoom
zoom(scaleBy);
// New raw center of viewport
var rawCenterX = -x + Math.min(viewportWidth, curWidth)/2/scale;
var rawCenterY = -y + Math.min(viewportHeight, curHeight)/2/scale;
// Delta
var deltaX = (rawCenterX - rawZoomX)*scale;
var deltaY = (rawCenterY - rawZoomY)*scale;
// Translate back to zoom center
translate(deltaX, deltaY);
if (!doNotUpdateLast) {
updateLastScale();
updateLastPos();
}
};
var zoomCenter = function (scaleBy) {
// Center of viewport
var zoomX = -x + Math.min(viewportWidth, curWidth)/2/scale;
var zoomY = -y + Math.min(viewportHeight, curHeight)/2/scale;
zoomAround(scaleBy, zoomX, zoomY);
};
var zoomIn = function () {
zoomCenter(2);
};
var zoomOut = function () {
zoomCenter(1/2);
};
var onLoad = function () {
img = document.getElementById('pinch-zoom-image-id');
container = img.parentElement;
disableImgEventHandlers();
imgWidth = img.width;
imgHeight = img.height;
viewportWidth = img.offsetWidth;
scale = viewportWidth/imgWidth;
lastScale = scale;
viewportHeight = img.parentElement.offsetHeight;
curWidth = imgWidth*scale;
curHeight = imgHeight*scale;
var hammer = new Hammer(container, {
domEvents: true
});
hammer.get('pinch').set({
enable: true
});
hammer.on('pan', function (e) {
translate(e.deltaX, e.deltaY);
});
hammer.on('panend', function (e) {
updateLastPos();
});
hammer.on('pinch', function (e) {
// We only calculate the pinch center on the first pinch event as we want the center to
// stay consistent during the entire pinch
if (pinchCenter === null) {
pinchCenter = rawCenter(e);
var offsetX = pinchCenter.x*scale - (-x*scale + Math.min(viewportWidth, curWidth)/2);
var offsetY = pinchCenter.y*scale - (-y*scale + Math.min(viewportHeight, curHeight)/2);
pinchCenterOffset = { x: offsetX, y: offsetY };
}
// When the user pinch zooms, she/he expects the pinch center to remain in the same
// relative location of the screen. To achieve this, the raw zoom center is calculated by
// first storing the pinch center and the scaled offset to the current center of the
// image. The new scale is then used to calculate the zoom center. This has the effect of
// actually translating the zoom center on each pinch zoom event.
var newScale = restrictScale(scale*e.scale);
var zoomX = pinchCenter.x*newScale - pinchCenterOffset.x;
var zoomY = pinchCenter.y*newScale - pinchCenterOffset.y;
var zoomCenter = { x: zoomX/newScale, y: zoomY/newScale };
zoomAround(e.scale, zoomCenter.x, zoomCenter.y, true);
});
hammer.on('pinchend', function (e) {
updateLastScale();
updateLastPos();
pinchCenter = null;
});
hammer.on('doubletap', function (e) {
var c = rawCenter(e);
zoomAround(2, c.x, c.y);
});
};
</script>
<button onclick="zoomIn()">Zoom In</button>
<button onclick="zoomOut()">Zoom Out</button>
<div class="pinch-zoom-container">
<img id="pinch-zoom-image-id" class="pinch-zoom-image" onload="onLoad()"
src="https://hammerjs.github.io/assets/img/pano-1.jpg">
</div>
</div>
</body>
</html>

JQuery Listener on Canvas Child doesn't work

i have a problem i'm implementing a crop custom rectangle on a canvas i made a function in Javascript that when the crop function is called the create on existing canvas a child and then with the JQuery listener i draw the rectangle.The childNode is create correctly while the listener don't work they don't get the events. This is my code:
var dragging = false;
var xstart = 0;
var ystart = 0;
var width = 0;
var height = 0;
var ctxotmp = null;
var ctxtmp = null;
var canvastmp = null;
var mycanvas = null;
function draw() {
ctxtmp.fillRect(xstart, ystart, width, height);
}
function init() {
mycanvas = $('#mycanvas')[0];
// create temp canvas
canvastmp = document.createElement('canvas');
canvastmp.id = "mycanvastmp";
canvastmp.width = mycanvas.width;
canvastmp.height = mycanvas.height;
mycanvas.parentNode.appendChild(canvastmp);
$("#mycanvastmp").css({position:"absolute",top:$("#mycanvas").css("top"),left:$("#mycanvas").css("left")});
canvastmp = $('#mycanvastmp')[0];
ctxtmp = canvastmp.getContext('2d');
ctxtmp.lineWidth = 1;
ctxtmp.fillStyle = "rgba(0, 0, 0, 0.5)";
}
//listener
$('#mycanvastmp').mousedown(function(e) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
xstart = e.pageX - xoffs;
ystart = e.pageY - yoffs;
dragging = true;
});
$('#mycanvastmp').mousemove(function(e) {
if(dragging) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
width = e.pageX - xoffs - xstart;
height = e.pageY - yoffs - ystart;
ctxtmp.clearRect(0, 0, $(this).width(), $(this).height());
draw();
}
});
$('#mycanvastmp').mouseup(function() {
dragging=false;
alert('The rectangle for crop (x, y, width, height): ' + xstart + ', ' + ystart + ', ' + width + ', ' + height);
});
Someone can help me?
Looks like you're attaching the events before the temp canvas is created.
Either attach the events in the init() function after adding the temp canvas to the DOM or use .delegate() or .on()
$("#mycanvas").on("mouseup", "#mycanvastmp", function() {
//...
});
You need to use .on on method in order to bind events to dynamically created objects. When your page initially loads and the dom fires, it doesn't see tempCanvas so it doesn't attach them initially.
//listener
$('body').on('mousedown' ,'#mycanvastmp', function(e) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
xstart = e.pageX - xoffs;
ystart = e.pageY - yoffs;
dragging = true;
});
$('body').on('mousemove' ,'#mycanvastmp', function(e) {
if(dragging) {
var xoffs = $(this).offset().left;
var yoffs = $(this).offset().top;
width = e.pageX - xoffs - xstart;
height = e.pageY - yoffs - ystart;
ctxtmp.clearRect(0, 0, $(this).width(), $(this).height());
draw();
}
});
$('body').on('mouseup' ,'#mycanvastmp', function(e) {
dragging=false;
alert('The rectangle for crop (x, y, width, height): ' + xstart + ', ' + ystart + ', ' + width + ', ' + height);
});
init();
Live Demo

Categories

Resources