How to delete svg when I click on it? I am using the lib snapsvg. I have plunkered my issue.
The use case is as follows : when user clicks on the svg, I create a circle at the position the user clicked, and if the user clicks on a circle that has been created, I want to delete the circle. I have a strange behavior because the circle is moved but not deleted.
(function() {
var s = Snap("#svg");
s.rect(10, 10, 400, 400);
s.click(handleClick);
function handleClick(event) {
var e = event.target;
var dim = e.getBoundingClientRect();
var x = event.clientX - dim.left;
var y = event.clientY - dim.top;
var c = this.circle(x, y, 10);
c.attr({
fill: '#FFF'
})
c.click(function() {
console.log('click circle');
this.remove();
});
}
})();
I managed to find the solution, the element was not moving, actually it was removed but an other was created. To solve my issue I have just added event.stopPropagation(); inside c.click function :
c.click(function(event) {
console.log('click circle');
this.remove();
event.stopPropagation();
});
Related
I'm using InteractJs library in a Rails 6 app. I've implemented it's drag and drop events.
The drag and drop functionality is working fine. The problem that I'm facing is if I drop an element out of the dropzone and place it to it's original(starting) position, it get's positioned there, but if I try to drag that element again it appears on the position where the previous dragging ended and not where it was restored to, this is the problem that I'm looking to solve
Below is my code for the events
var dragMoveListener = function (event) {
var target, x, y;
target = event.target;
x = (parseFloat(target.getAttribute("data-x")) || 0) + event.dx;
y = (parseFloat(target.getAttribute("data-y")) || 0) + event.dy;
target.style.webkitTransform = target.style.transform =
"translate(" + x + "px, " + y + "px)";
target.setAttribute("data-x", x);
return target.setAttribute("data-y", y);
};
window.dragMoveListener = dragMoveListener;
interact('*[data-draggable="true"]').draggable({
inertia: false,
autoScroll: true,
onmove: dragMoveListener,
});
const restoreToOriginalPosition = (event) => {
// this restores the element to it's original position but the next time I attempt to drag it, it appears on the position where the previous dragging ended
event.relatedTarget.style.transform = "translate(0px, 0px)";
};
$(document).on("turbolinks:load", function () {
interact("#dropzone").dropzone({
accept: '*[data-draggable="true"]',
overlap: 0.75,
ondropactivate: function (event) {
// ...
},
ondrop: function (event) {
const product_id = event.relatedTarget.attributes["data-product-id"].value;
// my use case is to remove the element from DOM if it's dropped into the dropzone, otherwise restore to it's starting position
$(event.relatedTarget).remove();
addToCart(product_id); //it's definition is not relevant here
},
ondropdeactivate: function (event) {
restoreToOriginalPosition(event);
},
});
});
In the ondrop event listener I've attempted to restore the element to it's original position on the page. See the function restoreToOriginalPosition which successfully puts the element to it's original position.
I had to remove/reset the data-x and data-y attributes of the draggable element.
const restoreToOriginalPosition = (event) => {
event.relatedTarget.removeAttribute("data-x");
event.relatedTarget.removeAttribute("data-y");
event.relatedTarget.style.transform = "translate(0px, 0px)";
};
This solved the problem.
I am building a drag and drop application purely in Javascript. I have coded the drag part where the element can be dragged and dropped randomly in the page. Now, I have built a drop zone that contains 9 boxes(divs) wherein 9 divs must be dropped. I can't think of an approach that will help me accomplish this task. I am thinking of making those divs 'absolute' and re-build them use top & left attributes. But how should I proceed further? How will the div that I drag i.e onmousedown will come to know that onmouseup it should drop at the specified location. Example: If I select a div numbered 1, it should drop at target numbered 1.
Here's the Javascript I am using for selecting and dragging:
window.onload = function() {
var el = document.getElementById('block1');
var mover = false, x, y, posx, posy, first = true;
el.onmousedown = function() {
mover = true;
};
el.onmouseup = function() {
mover = false;
first = true;
};
el.onmousemove = function(e) {
if (mover) {
if (first) {
x = e.offsetX;
y = e.offsetY;
first = false;
}
posx = e.pageX - x;
posy = e.pageY - y;
this.style.left = posx + 'px';
this.style.top = posy + 'px';
}
};
};
I would have the targets recognize a "onmouseenter" and set a Boolean to be true for that target, then set it to false "onmouseleave". then "onmouseup" check all of the booleans and if div1 has been dropped and target1 is true then div1 position = target position.
I know this is a pseudo code answer but its just my thought process on ho to solve the problem.
I'm working on a project based on a nice little sample canvas drawing app someone else on the project downloaded and modified. We need to allow the user to click a button elsewhere on the page (not part of the canvas), and have it run a function that came with the sample app. However, the function is inside of a closure. Since I can't call the function directly (right? the closure prevents this? I don't often work with closures), I thought I'd be able to accomplish this by triggering a mouse event at the location the user would click to accomplish the same thing. It's not working, and I don't know why not.
I posted a greatly simplified version at this fiddle. Simple HTML code:
<div id="canvasDiv"></div>
<div id="buttonDiv">
<button>why can't I send a click to the canvas?</button>
</div>
And the simplified version of the downloaded sample app, plus my attempt to use jQuery's .trigger method to trigger the event:
var WM = {};
WM.drawingApp = function(options) {
"use strict";
var canvas, context,
// Add mouse and touch event listeners to the canvas
createUserEvents = function() {
var getElementPos = function(element) {
// straight-forward stuff removed for brevity's sake
return pos;
};
var press = function(e) {
// Mouse down location
var sizeHotspotStartX, toolIndex,
mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX),
mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY);
var elementPos = getElementPos(document.getElementById(options.canvasElementId || 'canvasDiv'));
mouseX -= elementPos.x;
mouseY -= elementPos.y;
announce(mouseX, mouseY);
};
var announce = function(x,y) { alert('press at: ' + x + ', ' + y); }
// Add mouse event listeners to canvas element
canvas.addEventListener("mousedown", press, false);
},
// Creates a canvas element, etc
init = function() {
// Create the canvas
canvas = document.createElement('canvas');
canvas.setAttribute('width', 100);
canvas.setAttribute('height', 100);
canvas.setAttribute('id', 'canvas');
document.getElementById(options.canvasElementId || 'canvasDiv').appendChild(canvas);
context = canvas.getContext("2d"); // Grab the 2d canvas context
createUserEvents();
};
init();
return {};
};
jQuery(document).ready(function() {
jQuery('#buttonDiv').on('click', 'button', function() {
var down = jQuery.Event("mousedown", {
pageX: 50,
pageY: 50
});
jQuery('#canvasDiv canvas').trigger(down);
});
});
As you can see by running the fiddle, if you click inside the box, you get an alert announcing where you clicked. But if you click the button, you don't get an alert. While writing this question, it occurred to me that maybe jQuery's .trigger method isn't a sufficient way to send the click. Its documentation page specifically says that .trigger "does not perfectly replicate a naturally-occurring event". We're open to solutions that don't involve jQuery.
You can define a variable var press; outside of WM, inside of WM, remove var before press and set press = function() {}. You should then be able to call press(down) at click of button
var press;
press = function(e) {
console.log(e);
// Mouse down location
var sizeHotspotStartX, toolIndex,
mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX),
mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY);
var elementPos = getElementPos(
document.getElementById(options.canvasElementId
|| 'canvasDiv')
);
mouseX -= elementPos.x;
mouseY -= elementPos.y;
announce(mouseX, mouseY);
};
jQuery(document).ready(function() {
jQuery('#buttonDiv').on('click', 'button', function() {
var down = jQuery.Event("mousedown", {
pageX: 50,
pageY: 50
});
press(down); // call `press` at `button` click
//jQuery('#canvasDiv canvas').trigger(down);
});
});
// based on http://www.williammalone.com/projects/html5-canvas-javascript-drawing-app-with-bucket-tool/
var press;
var WM = {};
WM.drawingApp = function(options) {
"use strict";
var canvas, context,
// Add mouse and touch event listeners to the canvas
createUserEvents = function() {
var getElementPos = function(element) {
var parentOffset, pos;
if (!element) {
pos = {
x: 0,
y: 0
};
} else {
pos = {
x: element.offsetLeft,
y: element.offsetTop
};
if (element.offsetParent) {
parentOffset = getElementPos(element.offsetParent);
pos.x += parentOffset.x;
pos.y += parentOffset.y;
}
}
return pos;
};
press = function(e) {
console.log(e)
// Mouse down location
var sizeHotspotStartX, toolIndex,
mouseX = (e.changedTouches ? e.changedTouches[0].pageX : e.pageX),
mouseY = (e.changedTouches ? e.changedTouches[0].pageY : e.pageY);
var elementPos = getElementPos(document.getElementById(options.canvasElementId || 'canvasDiv'));
mouseX -= elementPos.x;
mouseY -= elementPos.y;
announce(mouseX, mouseY);
};
var announce = function(x,y) { alert('press at: ' + x + ', ' + y); }
// Add mouse event listeners to canvas element
canvas.addEventListener("mousedown", press, false);
},
// Creates a canvas element, loads images, adds events, and draws the canvas for the first time.
init = function() {
// Create the canvas (Neccessary for IE because it doesn't know what a canvas element is)
canvas = document.createElement('canvas');
canvas.setAttribute('width', 100);
canvas.setAttribute('height', 100);
canvas.setAttribute('id', 'canvas');
document.getElementById(options.canvasElementId || 'canvasDiv').appendChild(canvas);
context = canvas.getContext("2d"); // Grab the 2d canvas context
createUserEvents();
};
init();
return {};
};
jQuery(document).ready(function() {
jQuery('#buttonDiv').on('click', 'button', function() {
var down = jQuery.Event("mousedown", {
pageX: 50,
pageY: 50
});
press(down)
//jQuery('#canvasDiv canvas').trigger(down);
});
});
var drawingApp = WM.drawingApp({
canvasElementId: "canvasDiv"
});
#canvasDiv canvas {
border: solid black 1px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<div id="canvasDiv"></div>
<div id="buttonDiv">
<button>why can't I send a click to the canvas?</button>
</div>
jsfiddle https://jsfiddle.net/gkvdha3h/5/
Question is about the onstart event handler for Element.drag in the newly announced Snap.svg.
The intention of the code below is to register event handlers for the start and stop of a drag (onstart/onstop) on an svg object.
var s = Snap(800,600);
var bigCircle = s.circle(300,150,100);
bigCircle.drag(null,
function(){
console.log("Move started");
},
function(){
console.log("Move stopped");
}
);
The console messages work fine on drag start and stop, but the null overrides the default onmove function - resulting in no actual drag happening. How do I pass something that says "I don't want to mess with the default onmove"?
(Note: I'd prefer to register an event handler by means of assignment, like the familiar onClick, but that's a different matter.)
Note added after few hours:
The Raphael.js documentation and examples provide some clues. At least now I know how to pass in a proper function for onmove that provides the default move behavior:
var s = Snap(800,600);
var bigCircle = s.circle(300,150,100);
start = function() {
this.ox = parseInt(this.attr("cx"));
this.oy = parseInt(this.attr("cy"));
console.log("Start move, ox=" + this.ox + ", oy=" + this.oy);
}
move = function(dx, dy) {
this.attr({"cx": this.ox + dx, "cy": this.oy + dy});
}
stop = function() {
this.ox = parseInt(this.attr("cx"));
this.oy = parseInt(this.attr("cy"));
console.log("Stop move, ox=" + this.ox + ", oy=" + this.oy);
}
bigCircle.drag(move, start, stop);
I'm not sure if I'm misunderstanding what you exactly want...don't you want to implement the drag ?
So for example...
var s = Snap(400,400);
var bigCircle = s.circle(150, 150, 100);
var moveFunc = function (dx, dy, posx, posy) {
this.attr( { cx: posx , cy: posy } ); // basic drag, you would want to adjust to take care of where you grab etc.
};
bigCircle.drag( moveFunc,
function(){
console.log("Move started");
},
function(){
console.log("Move stopped");
}
);
JSBin here http://jsbin.com/akoCAkA/1/edit?html,js,output
There is an example how to drag with SnapSVG here: http://svg.dabbles.info/snaptut-drag.html
var s = Snap("#svgout");
var rect = s.rect(20,20,40,40);
var circle = s.circle(60,150,50);
var move = function(dx,dy) {
this.attr({
transform: this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy]
});
}
var start = function() {
this.data('origTransform', this.transform().local );
}
var stop = function() {
console.log('finished dragging');
}
rect.drag(move, start, stop );
circle.drag(move, start, stop );
After struggling for some hours to do this with snap.js, I finally discovered svg.js and its draggable plugin, with which it is so much easier:
var draw = SVG('svg');
var circle = draw.circle(10).attr({cx:30,cy:30,fill:'#f06'});
circle.dragend = function(delta, event) {
alert(this.attr('cx'))
}
circle.draggable();
So, I switched to svg.js ...
The eve.on method wasn't working for me, so I did some poking around and managed to recreate the onmove function. The other two (onstart and onend) require no specific code to work apparently:
var S = Snap(300,300);
var bigCircle = S.circle(150, 150, 100);
bigCircle.drag(onDragMove, onDragStart, onDragEnd);
var ddx = 0;
var ddy = 0;
var dxDone = 0;
var dyDone = 0;
function onDragMove (dx, dy, posx, posy) {
dx = dx + dxDone; // dx and dy reset to 0 for some reason when this function begins
dy = dy + dyDone; // retain the last move's position as the starting point
this.attr( { transform: 't'+dx+','+dy } );
ddx = dx;
ddy = dy;
console.log('moving...');
};
function onDragStart(x,y,e) {
console.log('start!');
};
function onDragEnd(e) {
dxDone = ddx;
dyDone = ddy;
console.log('end!');
};
Please note however that this should only be used for one dragged object at a time. If you need a custom drag for another object, you'll have to rename the functions (ie onDragStart2) and the four variables declared outside of them (ie ddx2) after duplicating it.
Also, the 'transform' string format I passed (tx,y) came from what I found after doing console.log( this.attr('transform') ). I'm not familiar with matrix() just yet, so this way seemed easier.
Hope this helps!
I can't drag group elements with custom handlers, s.drag() makes it possible. So i searched further found its possible.
Documentation:
Additionaly following drag events are triggered: drag.start. on start, drag.end. on > end and drag.move. on every move. When element is dragged over another element > drag.over. fires as well.
Solution:
s.drag();
eve.on("snap.drag.start." + s.id, function () {
console.log('cool');
});
eve.on("snap.drag.move." + s.id, function () {
console.log('cooler');
});
eve.on("snap.drag.end." + s.id, function () {
console.log('way cool');
});
eve is not documented on snapsvg it is available on raphael. i don't know this is proper way or hack.
Try this
var paper = Snap("#main");
var object = paper.circle(300,150,100)
object .attr({
stroke: "#000",
strokeWidth: 10,
strokeLinecap:"round"
});
var move1 = function(dx,dy, posx, posy) {
this.transform(this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy])
};
var start = function() {
this.data('origTransform', this.transform().local );
}
var stop = function() {
console.log('dragging done');
}
object.drag(move1, start, stop );
I'm trying to move a div with the movement of the mouse cursor, but can't understand how to get the newly updated mouse position within my timeout. Maybe there is a simpler way.
var t;
$(document).ready(function(){
$("body").on("mousedown", ".heading", function (e) {
$("body").data("header_click", true);
if ($("body").data("header_click")) {
var container = $("#dialog");
container.css("position", "absolute");
t = setInterval(function(){
//some way to get mouse position
var pos = container.position();
container.css({
top: "",//set based on mouse position
left: "",//set based on mouse position
});
}, 100);
}else{
document.clearInterval(t);
}
});
});
$("body").on("mousedown", ".heading", function (e) {
$("body").data("header_click", false);
});
The solution found here did not work for me.
You will need to bind to the mouse move event and update a document variable.
var currentMousePos = { x: -1, y: -1 };
$(document).on('mousemove', function(event) {
currentMousePos.x = event.pageX;
currentMousePos.y = event.pageY;
});
Then use those positions relative to the absolute positions of the element you are wanting to drag to calculate and update the elements new position.
$(document).ready(function(){
$("body").on("mousedown", ".heading", function (e) {
$("body").data("header_click", true);
if ($("body").data("header_click")) {
var container = $("#dialog");
container.css("position", "absolute");
var containerPos = container.pos();
var mouseTopOffset = containerPos.top - currentMousePos.y;
var mouseLeftOffset = containerPos.left - currentMousePos.x;
container.css("left", mouseTopOffset +"px");
container.css("top", mouseLeftOffset +"px");
}
}
}
I havent really tested this but in theory should do what you need.