I am trying to connect two dynamically created divs using an SVG line. In order to do that I have created an outport for each div from where the SVG path will start. On clicking and dragging the mouse from the outport the SVG path moves and follows the cursor. However, after I am done when I click somewhere else it changes its position. Basically, its path is getting updated after the mouse up event. The line should not move after mouseup no matter where I click. I cannot understand the mistake in my code.
https://codepen.io/iamkrptonite/pen/ExZXjor
$(document).ready(function(){
const boundary = $(".boundary")
const outport = $(".outport")
const bezierWeight = 0.6;
var boxX = boundary[0].offsetLeft;
var boxY = boundary[0].offsetTop;
$(boundary).click(function(){
console.log("boom")
})
$(outport).on("mousedown",function(e){
var imouseX = e.pageX-boxX;
var imouseY = e.pageY-boxY;
$(boundary).on("mousemove",function(e){
var mouseX = e.pageX-boxX;
var mouseY = e.pageY-boxY;
updateBoxesPath(imouseX,imouseY,mouseX,mouseY);
}).mouseup(function (e) {
var mouseX = e.pageX-boxX;
var mouseY = e.pageY-boxY;
updateBoxesPath(imouseX,imouseY,mouseX,mouseY);
$(this).unbind('mousemove');
$(this).unbind('mousedown');
$(this).unbind('mouseclick');
})
}).mouseup(function(){
$(this).unbind('mousemove');
$(this).unbind('mousedown');
$(this).unbind('mousedown');
})
function updateBoxesPath(imouseX,imouseY,mouseX,mouseY) {
var boxPath = document.getElementById("boxPath_0");
var x1 = imouseX;
var y1 = imouseY;
var x4 = mouseX;
var y4 = mouseY;
var dx = Math.abs(x4 - x1) * bezierWeight;
var x2 = x1 + dx;
var x3 = x4 - dx;
var boxData = `M${x1} ${y1} C ${x2} ${y1} ${x3} ${y4} ${x4} ${y4}`;
boxPath.setAttribute("d", boxData);
}
})
you have to unbind mouseup, but you have to precise the same selector too: (i suggest you to use .off, .unbind is deprecated)
$(outport).on("mousedown",function(e){
var imouseX = e.pageX-boxX;
var imouseY = e.pageY-boxY;
$(boundary).on("mousemove",function(e){
var mouseX = e.pageX-boxX;
var mouseY = e.pageY-boxY;
updateBoxesPath(imouseX,imouseY,mouseX,mouseY);
}).mouseup(function (e) {
var mouseX = e.pageX-boxX;
var mouseY = e.pageY-boxY;
updateBoxesPath(imouseX,imouseY,mouseX,mouseY);
$(boundary).off('mousemove mouseup');
$(outport).off('mousedown');
})
});
Of course after to unbind the different events, you have to bind again (.on) to reactivate for another path.
Related
I'v encountered a small problem while trying to create an easy-to-use image-editor, where you can add multiple draggable texts on an image and then save the edited image with texts by original resolution.
Everything else works fine, but I want to be able to edit full-hd images and bigger on a non-full-hd resolution canvas (like 800x600px)
I cant use resolutions like 1920x1080 or bigger on the canvas, since it will be to massive and go out of the borders of browser (scrollbars) and also wont be really so easy to manage.
I tried to use percentage value on canvas, and it looks OK, but the text hitbox wont follow the cursor when dragging around.
Any tips or tricks to handle this problem?
Here is a sample how it looks with 1920x1080 canvas & full-hd image.
I would like to fit the image and functionality to a.. lets say 800x600 canvas but save the output as original full-hd.
<canvas id="canvas" width=1920 height=1080></canvas>
function draw() {
//ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imageObj, 0, 0, 1920, 1080);
for (var i = 0; i < texts.length; i++) {
var text = texts[i];
ctx.fillText(text.text, text.x, text.y);
}
}
https://jsfiddle.net/n0mn7bcg/
// canvas related variables
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// variables used to get mouse position on the canvas
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
var imageObj = new Image();
imageObj.src = 'https://4.bp.blogspot.com/-lQwIDyafEbI/UxNch2499rI/AAAAAAAAogo/FfZxYSCIXxc/s0/Ships+in+from+the+bottle_2_HD.jpg';
// variables to save last mouse position
// used to see how far the user dragged the mouse
// and then move the text by that distance
var startX;
var startY;
// an array to hold text objects
var texts = [];
// this var will hold the index of the hit-selected text
var selectedText = -1;
// clear the canvas & redraw all texts
function draw() {
//ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imageObj, 0, 0, 1920, 1080);
for (var i = 0; i < texts.length; i++) {
var text = texts[i];
ctx.fillText(text.text, text.x, text.y);
}
}
// test if x,y is inside the bounding box of texts[textIndex]
function textHittest(x, y, textIndex) {
var text = texts[textIndex];
return (x >= text.x && x <= text.x + text.width && y >= text.y - text.height && y <= text.y);
}
// handle mousedown events
// iterate through texts[] and see if the user
// mousedown'ed on one of them
// If yes, set the selectedText to the index of that text
function handleMouseDown(e) {
e.preventDefault();
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
// Put your mousedown stuff here
for (var i = 0; i < texts.length; i++) {
if (textHittest(startX, startY, i)) {
selectedText = i;
}
}
}
// done dragging
function handleMouseUp(e) {
e.preventDefault();
selectedText = -1;
}
// also done dragging
function handleMouseOut(e) {
e.preventDefault();
selectedText = -1;
}
// handle mousemove events
// calc how far the mouse has been dragged since
// the last mousemove event and move the selected text
// by that distance
function handleMouseMove(e) {
if (selectedText < 0) {
return;
}
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
var dx = mouseX - startX;
var dy = mouseY - startY;
startX = mouseX;
startY = mouseY;
var text = texts[selectedText];
text.x += dx;
text.y += dy;
draw();
}
// listen for mouse events
$("#canvas").mousedown(function(e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function(e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function(e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function(e) {
handleMouseOut(e);
});
$("#submit").click(function() {
// calc the y coordinate for this text on the canvas
var y = texts.length * 20 + 20;
// get the text from the input element
var text = {
text: $("#theText").val(),
x: 20,
y: y
};
// calc the size of this text for hit-testing purposes
ctx.font = "80px consolas";
text.width = ctx.measureText(text.text).width;
text.height = 80;
// put this new text in the texts array
texts.push(text);
// redraw everything
draw();
});
body {
background: #f3f3f3;
}
#canvas {
border: 1px solid red;
}
#theText {
width: 10em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h4>Add text to canvas and drag it</h4>
<input id="theText" type="text">
<button id="submit">Draw text on canvas</button>
<br>
<canvas id="canvas" width=1920 height=1080></canvas>
It works if you use e.pageX in the mousedown and mousemove event handlers:
https://jsfiddle.net/n0mn7bcg/2/
function handleMouseDown(e) {
e.preventDefault();
startX = parseInt(e.pageX - offsetX);
startY = parseInt(e.pageY - offsetY);
// Put your mousedown stuff here
for (var i = 0; i < texts.length; i++) {
if (textHittest(startX, startY, i)) {
selectedText = i;
}
}
}
function handleMouseMove(e) {
if (selectedText < 0) {
return;
}
e.preventDefault();
mouseX = parseInt(e.pageX - offsetX);
mouseY = parseInt(e.pageY - offsetY);
// Put your mousemove stuff here
var dx = mouseX - startX;
var dy = mouseY - startY;
startX = mouseX;
startY = mouseY;
var text = texts[selectedText];
text.x += dx;
text.y += dy;
draw();
}
More information: What is the difference between screenX/Y, clientX/Y and pageX/Y?
Actually i have tried to drag and drop the canvas text which was created in javascript . But I am not able to drag it properly because selecting the text is not working properly. I kept my code in below snippet.
please help me on this. Thanks in advance.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// variables used to get mouse position on the canvas
var $canvas = $("#canvas");
var canvasOffset = $canvas.offset();
var offsetX = canvasOffset.left;
var offsetY = canvasOffset.top;
var scrollX = $canvas.scrollLeft();
var scrollY = $canvas.scrollTop();
// variables to save last mouse position
// used to see how far the user dragged the mouse
// and then move the text by that distance
var startX;
var startY;
// an array to hold text objects
var texts = [];
// this var will hold the index of the hit-selected text
var selectedText = -1;
// clear the canvas & redraw all texts
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(window.imageSrc, 0, 0, canvas.width, canvas.height);
for (var i = 0; i < texts.length; i++) {
var text = texts[i];
ctx.strokeText(text.text, text.x, text.y);
ctx.fillText(text.text, text.x, text.y);
}
}
// test if x,y is inside the bounding box of texts[textIndex]
function textHittest(x, y, textIndex) {
var text = texts[textIndex];
return (x >= text.x && x <= text.x + text.width && y >= text.y - text.height && y <= text.y);
}
// handle mousedown events
// iterate through texts[] and see if the user
// mousedown'ed on one of them
// If yes, set the selectedText to the index of that text
function handleMouseDown(e) {
e.preventDefault();
startX = parseInt(e.clientX - offsetX);
startY = parseInt(e.clientY - offsetY);
// Put your mousedown stuff here
//alert(texts.length);
for (var i = 0; i < texts.length; i++) {
if (textHittest(startX, startY, i)) {
selectedText = i;
}
}
}
// done dragging
function handleMouseUp(e) {
e.preventDefault();
selectedText = -1;
}
// also done dragging
function handleMouseOut(e) {
e.preventDefault();
selectedText = -1;
}
// handle mousemove events
// calc how far the mouse has been dragged since
// the last mousemove event and move the selected text
// by that distance
function handleMouseMove(e) {
if (selectedText < 0) {
return;
}
e.preventDefault();
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// Put your mousemove stuff here
var dx = mouseX - startX;
var dy = mouseY - startY;
startX = mouseX;
startY = mouseY;
var text = texts[selectedText];
text.x += dx;
text.y += dy;
draw();
}
// listen for mouse events
$("#canvas").mousedown(function (e) {
handleMouseDown(e);
});
$("#canvas").mousemove(function (e) {
handleMouseMove(e);
});
$("#canvas").mouseup(function (e) {
handleMouseUp(e);
});
$("#canvas").mouseout(function (e) {
handleMouseOut(e);
});
$("[id^='memeText']").change(function () {
// calc the y coordinate for this text on the canvas
var y = texts.length * 20 + 100;
var position = $(this).attr('name');
position = position - 1;
// get the text from the input element
var text = {
text: $(this).val(),
x: 250,
y: y
};
// calc the size of this text for hit-testing purposes
ctx.font = "32px verdana";
ctx.textAlign = 'center';
ctx.lineWidth = 3;
ctx.fillStyle = "#fff";
ctx.strokeStyle = "#000";
text.width = ctx.measureText(text.text).width;
text.height = 16;
if(texts[position]==""||texts[position]==null||texts[position]==undefined){
texts.push(text);
} else {
console.log(texts[position].text);
texts[position].text = $(this).val();
}
// put this new text in the texts array
// redraw everything
draw();
});
function handleFileSelect(evt) {
//var canvasWidth = 500;
// var canvasHeight = 500;
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = function(fileObject) {
var data = fileObject.target.result;
var image = new Image();
image.onload = function() {
window.imageSrc = this;
ctx.drawImage(window.imageSrc, 0, 0, canvas.width, canvas.height);
};
document.getElementById('box2').style.display = "block";
image.src = data;
// console.log(fileObject.target.result);
};
reader.readAsDataURL(file);
}
document.getElementById('file').addEventListener('change',
handleFileSelect, false);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box1" style="">
<input id="file" type="file" autocomplete="off">
<br><br>
</div>
<div id="box2" style="display: none;">
<input id="memeText" name="1" autocomplete="off">
<br><br>
<input id="memeText" name="2" autocomplete="off">
<br><br>
<canvas id="canvas" width="500" height="500"></canvas>
<br><br>
<a class="buttonLink" id="downloadLink">Download Meme!</a>
<br><br>
Make Another Meme!
</div>
There is a couple of problems with this.
Firstly, look at how to Get the coordinates of a mouse click on Canvas. Currently your offsets are both 0. Try calling canvas.offsetLeft and canvas.offsetTop - note: canvas, NOT $canvas.
Secondly, text position is set to the middle of the text, not to the top left or bottom left corner. So in your textHittest function, you want this return statement:
return (x >= (text.x - 0.5*text.width) && x <= (text.x + 0.5*text.width) && y >= (text.y - 0.5*text.height) && y <= (text.y + 0.5*text.height));
More on text properties here.
Once that's sorted, double check if the events are registering exactly as you want them to. Hope that's enough to get you started :)
I am trying to draw a circle by clicking and dragging the mouse pointer. The way you would do in PowerPoint or something. The center of the circle is showing up in weird places and I can't explain it.
Here is the jsfiddle: https://jsfiddle.net/h8t3hfa2/2/
This is how I get the start and end position;
$('#c').mousedown(function(event) {
var parentOffset = $(this).offset();
circle = new Circle();
circle.start['x'] = event.pageX - parentOffset.left;
circle.start['y'] = event.pageY - parentOffset.top;
});
$('#c').mouseup(function(event) {
var parentOffset = $(this).offset();
circle.end['x'] = event.pageX - parentOffset.left;
circle.end['y'] = event.pageY - parentOffset.top;
circle.draw(canvas[0]);
});
When I console log the midpoint it looks correct but the circle shows up somewhere else. Any ideas?
It happens because you're scaling your canvas using CSS. Remember, canvas dimensions are different from canvas CSS (style) dimensions.
A quick fix is to equalize them:
canvas.get(0).width = canvas.width();
canvas.get(0).height = canvas.height();
https://jsfiddle.net/h8t3hfa2/3/
var Circle = function() {
this.start = [];
this.end = [];
}
Circle.prototype.draw = function(canvas) {
var me = this;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
var mid = me.getcenter();
var rad = me.getradius();
console.log(mid, rad);
ctx.beginPath();
console.log(mid['x'], mid['y']);
ctx.arc(mid['x'], mid['y'], rad, 0, 360);
ctx.stroke();
}
};
Circle.prototype.getcenter = function() {
var me = this;
//Check the start and end are set
var centerX = (me.start['x'] + me.end['x']) / 2;
var centerY = (me.start['y'] + me.end['y']) / 2;
return {
'x': centerX,
'y': centerY
};
};
Circle.prototype.getradius = function() {
var me = this;
var distX = Math.abs(me.start['x'] - me.end['x']);
var distY = Math.abs(me.start['y'] - me.end['y']);
return distX / 2;
};
var circle;
var canvas = $('#c');
// added only these two lines
canvas.get(0).width = canvas.width();
canvas.get(0).height = canvas.height();
$('#c').mousedown(function(event) {
var parentOffset = $(this).offset();
circle = new Circle();
circle.start['x'] = event.pageX - parentOffset.left;
circle.start['y'] = event.pageY - parentOffset.top;
});
$('#c').mouseup(function(event) {
var parentOffset = $(this).offset();
circle.end['x'] = event.pageX - parentOffset.left;
circle.end['y'] = event.pageY - parentOffset.top;
circle.draw(canvas[0]);
});
canvas {background-color: white;height: 100%;width: 100%;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id='c'></canvas>
You should probably also add an onresize event handler to reset the canvas dimensions when the window size changes because you're using a fluid layout. Be careful, though, modifying either of the canvas dimensions (even to the original size) will cause the canvas to clear.
Removing height: 100%; width: 100%; from styles fixes avoids the problem.
Off-topic, but I suggest editing getradius to use the min (or max) of distX and distY (instead of hardcoding distX) like the applications you mentioned.
Circle.prototype.getradius = function() {
var me = this;
var distX = Math.abs(me.start['x'] - me.end['x'])/2;
var distY = Math.abs(me.start['y'] - me.end['y'])/2;
return Math.min(distX, distY);
};
I have this code for DnD an element. It works for an especific element. However I wanna make it work for all divs. I can't reference "this.id" to the move function. I don't what is missing to make it work.
window.addEventListener('load', init, false);
function init(){
//just one element
/* box = document.getElementById('div1');
box.addEventListener('mousedown', startMoving, false);
box.addEventListener('mouseup', stopMoving, false);*/
//all element?
box = document.getElementsByTagName('div');
for (i=0;i<box.length;i++) {
box[i].addEventListener('mousedown', startMoving, false);
box[i].addEventListener('mouseup', stopMoving, false);
}
}
function startMoving(evt){
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
a = document.getElementById(this.id);
divTop = parseInt(a.style.top),
divLeft = parseInt(a.style.left);
var diffX = posX - divLeft,
diffY = posY - divTop;
document.onmousemove = function(evt){
evt = evt || window.event;
var posX = evt.clientX,
posY = evt.clientY,
aX = posX - diffX,
aY = posY - diffY;
move(this.id,aX,aY);
}
}
function stopMoving(){
document.onmousemove = function(){}
}
function move(divid,newX,newY){
var a = document.getElementById(divid);
a.style.left = newX + 'px';
a.style.top = newY + 'px';
}
Is there a better way to make this?
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