Unable to resize Rect with mouse events - javascript

I am trying to resize the rectangle inside the SVG by using mouse events. Currently, I am working on the right bottom edge, for that, I created another circle shape and I am applying events on that shape to resize the rectangle. I don't know why when I start resizing it first it gets small and then starts resizing.
const min = 54;
let initialWidth = 0, initialHeight = 0, mouseX = 0, mouseY = 0;
rightBottomCorner.addEventListener('mousedown', function(evt: any) {
getMousePositions(evt);
getElementDimensions();
evt.stopPropagation();
window.addEventListener('mousemove', scale);
window.addEventListener('mouseup', removeScale);
});
function getMousePositions(evt: any) {
mouseX = evt.pageX;
mouseY = evt.pageY;
}
function getElementDimensions() {
selectedRect = rect;
initialWidth = selectedRect.clientWidth;
initialHeight = selectedRect.clientHeight;
}
function scale(evt: any) {
selectedRect = rect;
const width = initialWidth + (evt.pageX - mouseX);
const height = initialHeight + (evt.pageY - mouseY);
if (width > min) {
selectedRect.setAttributeNS(null, 'width', width as unknown as string);
}
if (height > min) {
selectedRect.setAttributeNS(null, 'height', height as unknown as string);
}
}
function removeScale() {
window.removeEventListener('mousemove', scale);
}

You are passing the functions to addEventlistener before they are defined.You have to move the rightBottomCorner.addEventListener function below your removeScale function. Then the resizing works.

Related

Can't update my mouse position, because of my horizontal scroll (LocomotiveScroll)

I'm trying to make a custom cursor with the Ink Cursor by Ricardo Mendieta. https://codepen.io/mendieta/pen/WgvENJ
The cursor is working, but the problem I have is that I use a horizontal scroll with Locomotive Scroll. When I scroll, the mouse position doesn't get updated. I tried to fix this with a mousewheel function. I can console log the mousewheel event, but it doesn't update my mouse position.
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mousewheel', onMouseScroll);
const onMouseMove = (event) => {
mousePosition.x = event.clientX - width / 2;
mousePosition.y = event.clientY - width / 2;
resetIdleTimer();
};
const onMouseScroll = (event) => {
console.log(event);
mousePosition.x = event.clientX - width / 2;
mousePosition.y = event.clientY - width / 2;
resetIdleTimer();
};
const render = (timestamp) => {
const delta = timestamp - lastFrame;
positionCursor(delta);
lastFrame = timestamp;
requestAnimationFrame(render);
};
const positionCursor = (delta) => {
let x = mousePosition.x;
let y = mousePosition.y;
dots.forEach((dot, index, dots) => {
let nextDot = dots[index + 1] || dots[0];
dot.x = x;
dot.y = y;
dot.draw(delta);
if (!idle || index <= sineDots) {
const dx = (nextDot.x - dot.x) * 0.35;
const dy = (nextDot.y - dot.y) * 0.35;
x += dx;
y += dy;
}
});
};
Is there a way I can update the mouse position when I scroll when the scroll direction is horizontal.
I just figured this out for my situation, which I think is similar to yours. Not sure if this will help or not - hopefully it does.
scroll.on('scroll', (instance) => {
let customCursor = document.querySelector(".customCursor");
let scrollPx = instance.scroll.x + "px";
customCursor.style.left = scrollPx;
});
So instead of trying to reconfigure where the mouse position is, I'm simply updating the "left" attribute of the custom cursor to be in sync with how much the horizontally laid out Locomotive scroll container is being scrolled.

How to use event listeners to make click, drag, and drop function? [duplicate]

Im struggling with seemingly a simple javascript exercise, writing a vanilla drag and drop. I think Im making a mistake with my 'addeventlisteners', here is the code:
var ele = document.getElementsByClassName ("target")[0];
var stateMouseDown = false;
//ele.onmousedown = eleMouseDown;
ele.addEventListener ("onmousedown" , eleMouseDown , false);
function eleMouseDown () {
stateMouseDown = true;
document.addEventListener ("onmousemove" , eleMouseMove , false);
}
function eleMouseMove (ev) {
do {
var pX = ev.pageX;
var pY = ev.pageY;
ele.style.left = pX + "px";
ele.style.top = pY + "px";
document.addEventListener ("onmouseup" , eleMouseUp , false);
} while (stateMouseDown === true);
}
function eleMouseUp () {
stateMouseDown = false;
document.removeEventListener ("onmousemove" , eleMouseMove , false);
document.removeEventListener ("onmouseup" , eleMouseUp , false);
}
Here's a jsfiddle with it working: http://jsfiddle.net/fpb7j/1/
There were 2 main issues, first being the use of onmousedown, onmousemove and onmouseup. I believe those are only to be used with attached events:
document.body.attachEvent('onmousemove',drag);
while mousedown, mousemove and mouseup are for event listeners:
document.body.addEventListener('mousemove',drag);
The second issue was the do-while loop in the move event function. That function's being called every time the mouse moves a pixel, so the loop isn't needed:
var ele = document.getElementsByClassName ("target")[0];
ele.addEventListener ("mousedown" , eleMouseDown , false);
function eleMouseDown () {
stateMouseDown = true;
document.addEventListener ("mousemove" , eleMouseMove , false);
}
function eleMouseMove (ev) {
var pX = ev.pageX;
var pY = ev.pageY;
ele.style.left = pX + "px";
ele.style.top = pY + "px";
document.addEventListener ("mouseup" , eleMouseUp , false);
}
function eleMouseUp () {
document.removeEventListener ("mousemove" , eleMouseMove , false);
document.removeEventListener ("mouseup" , eleMouseUp , false);
}
By the way, I had to make the target's position absolute for it to work.
you can try this fiddle too, http://jsfiddle.net/dennisbot/4AH5Z/
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>titulo de mi pagina</title>
<style>
#target {
width: 100px;
height: 100px;
background-color: #ffc;
border: 2px solid blue;
position: absolute;
}
</style>
<script>
window.onload = function() {
var el = document.getElementById('target');
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';
}
};
}
</script>
</head>
<body>
<div id="target" style="left: 10px; top:20px"></div>
</body>
</html>
I've just made a simple drag.
It's a one liner usage, and it handles things like the offset of the mouse to the top left corner of the element, onDrag/onStop callbacks, and SVG elements dragging
Here is the code.
// simple drag
function sdrag(onDrag, onStop) {
var startX = 0;
var startY = 0;
var el = this;
var dragging = false;
function move(e) {
el.style.left = (e.pageX - startX ) + 'px';
el.style.top = (e.pageY - startY ) + 'px';
onDrag && onDrag(el, e.pageX, startX, e.pageY, startY);
}
function startDragging(e) {
if (e.currentTarget instanceof HTMLElement || e.currentTarget instanceof SVGElement) {
dragging = true;
var left = el.style.left ? parseInt(el.style.left) : 0;
var top = el.style.top ? parseInt(el.style.top) : 0;
startX = e.pageX - left;
startY = e.pageY - top;
window.addEventListener('mousemove', move);
}
else {
throw new Error("Your target must be an html element");
}
}
this.addEventListener('mousedown', startDragging);
window.addEventListener('mouseup', function (e) {
if (true === dragging) {
dragging = false;
window.removeEventListener('mousemove', move);
onStop && onStop(el, e.pageX, startX, e.pageY, startY);
}
});
}
Element.prototype.sdrag = sdrag;
and to use it:
document.getElementById('my_target').sdrag();
You can also use onDrag and onStop callbacks:
document.getElementById('my_target').sdrag(onDrag, onStop);
Check my repo here for more details:
https://github.com/lingtalfi/simpledrag
this is how I do it
var MOVE = {
startX: undefined,
startY: undefined,
item: null
};
function contentDiv(color, width, height) {
var result = document.createElement('div');
result.style.width = width + 'px';
result.style.height = height + 'px';
result.style.backgroundColor = color;
return result;
}
function movable(content) {
var outer = document.createElement('div');
var inner = document.createElement('div');
outer.style.position = 'relative';
inner.style.position = 'relative';
inner.style.cursor = 'move';
inner.style.zIndex = 1000;
outer.appendChild(inner);
inner.appendChild(content);
inner.addEventListener('mousedown', function(evt) {
MOVE.item = this;
MOVE.startX = evt.pageX;
MOVE.startY = evt.pageY;
})
return outer;
}
function bodyOnload() {
document.getElementById('td1').appendChild(movable(contentDiv('blue', 100, 100)));
document.getElementById('td2').appendChild(movable(contentDiv('red', 100, 100)));
document.addEventListener('mousemove', function(evt) {
if (!MOVE.item) return;
if (evt.which!==1){ return; }
var dx = evt.pageX - MOVE.startX;
var dy = evt.pageY - MOVE.startY;
MOVE.item.parentElement.style.left = dx + 'px';
MOVE.item.parentElement.style.top = dy + 'px';
});
document.addEventListener('mouseup', function(evt) {
if (!MOVE.item) return;
var dx = evt.pageX - MOVE.startX;
var dy = evt.pageY - MOVE.startY;
var sty = MOVE.item.style;
sty.left = (parseFloat(sty.left) || 0) + dx + 'px';
sty.top = (parseFloat(sty.top) || 0) + dy + 'px';
MOVE.item.parentElement.style.left = '';
MOVE.item.parentElement.style.top = '';
MOVE.item = null;
MOVE.startX = undefined;
MOVE.startY = undefined;
});
}
bodyOnload();
table {
user-select: none
}
<table>
<tr>
<td id='td1'></td>
<td id='td2'></td>
</tr>
</table>
While dragging, the left and right of the style of the parentElement of the dragged element are continuously updated. Then, on mouseup (='drop'), "the changes are committed", so to speak; we add the (horizontal and vertical) position changes (i.e., left and top) of the parent to the position of the element itself, and we clear left/top of the parent again. This way, we only need JavaScript variables for pageX, pageY (mouse position at drag start), while concerning the element position at drag start, we don't need JavaScript variables for that (just keeping that information in the DOM).
If you're dealing with SVG elements, you can use the same parent/child/commit technique. Just use two nested g, and use transform=translate(dx,dy) instead of style.left=dx, style.top=dy

unwanted behaviour after javascript mousedown event

I have made a fiddle here and I am using Chrome.
I want to drag the red dashed/dotted line on the left to the right. A new flex column is added on mouseup OR when you exceed a part of the container, depending on the number of columns already added. For now I just try to add max 5 columns.
The first "drag" works as expected
mouse down
while holding mousedown drag to right
column is added on mouseup or when exceeding some width
Now I want to repeat these steps and add another one. But now the behaviour is different:
mouse down
drag it to the right but it gets stuck
I have to release the mouse button and move to the right and get out of the function
Here is some code, I've tried some stuff with bubble true or false on the eventlisteners but no luck. Should I use other events?
var container = document.querySelector('.js-flex-container'),
containerRow = container.querySelector('.js-flex-row'),
oldX = 0,
oldY = 0,
rect = container.getBoundingClientRect(),
mouseupEvent = new MouseEvent('mouseup'),
newDiv,
colCount,
captureMouseMove = function captureMouseMove(event){
var directionX = 0,
directionY = 0;
if ((event.clientX - rect.left) > oldX) {
// "right"
newDiv.style.width = oldX + 'px';
if (oldX >= Math.round(rect.right / colCount)) {
container.dispatchEvent(mouseupEvent);
}
}
oldX = (event.clientX - rect.left);
};
container.querySelector('.js-flex-column').addEventListener('mousedown', function(event){
var colWidth = event.clientX - rect.left,
columns = container.querySelectorAll('.col');
colCount = columns.length + 1;
newDiv = document.createElement('div');
newDiv.className = 'col-x';
columns[0].parentNode.insertBefore(newDiv, columns[0]);
container.addEventListener('mousemove', captureMouseMove);
});
container.addEventListener('mouseup', function(){
console.log('mouseup');
if (typeof newDiv !== 'undefined') {
newDiv.style.width = '';
newDiv.className = 'col col-' + colCount;
container.removeEventListener('mousemove', captureMouseMove);
}
});
Chrome is doing something (funky) with the event after move.
Adding event.preventDefault() should do the trick
captureMouseMove = function captureMouseMove(event){
var directionX = 0,
directionY = 0;
if ((event.clientX - rect.left) > oldX) {
// "right"
newDiv.style.width = oldX + 'px';
if (oldX >= Math.round(rect.right / colCount)) {
container.dispatchEvent(mouseupEvent);
}
}
oldX = (event.clientX - rect.left);
event.preventDefault(); // <---
};
I would also recommend that you don't use container for mouseup events. Instead use window so releasing outside of the container doesn't cause issues. You could do the same for mousemove.

Canvas Zoom-in, Zoom-Out using mousewheel

I'm trying make my <canvas> zoomable (zoom-in, zoom-out).
I'm trying the following code to take care of the zooming functions, however, it does not work as expected because as soon as I try to zoom-in/out, the canvas goes empty/blank.
This leads me to believe the mousewheel function is working but something is off as it "deletes" the canvas drawing.
function drawZoom() {
var startScale = 1;
var scale = startScale;
var floor = $("#floorplan")[0].getContext("2d")
var width = floor.canvas.width;
var height = floor.canvas.height;
var intervalId;
var imageData = floor.getImageData(0, 0, width, height);
var canvas = $("<canvas>").attr("width", width).attr("height", height)[0];
canvas.getContext("2d").putImageData(imageData, 0, 0);
function drawContents(){
var newWidth = width * scale;
var newHeight = height * scale;
floor.save();
floor.translate(-((newWidth-width)/2), -((newHeight-height)/2));
floor.scale(scale, scale);
floor.clearRect(0, 0, width, height);
floor.drawImage(canvas, 0, 0);
floor.restore();
}
$("#test").on('DOMMouseScroll mousewheel',function(e) {
var e = e || window.event; // old IE support
var theEvent = e.originalEvent.wheelDelta || e.originalEvent.detail*-1;
if(theEvent / 120 > 0) {
zoomin();
} else {
zoomout();
}
if (e.preventDefault)
e.preventDefault();
});
function zoomin()
{
scale = scale + 0.01;
drawContents();
}
function zoomout()
{
scale = scale - 0.01;
drawContents();
}
}
CodePen Live Example
What am I doing wrong here? How can I solve this?
I can add the wheel hanlder in your drawSettings like this:
//Drag settings start
function drawSettings() {
//snip....
if (canvas.addEventListener) {
// IE9, Chrome, Safari, Opera
canvas.addEventListener("mousewheel", MouseWheelHandler, false);
// Firefox
canvas.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
}
// IE 6/7/8
else canvas.attachEvent("onmousewheel", MouseWheelHandler);
//the rest of your code...
The function to zoom in and out needs to be tailored for your example by using the value of delta. Add this function I found from here: https://www.sitepoint.com/html5-javascript-mouse-wheel/ and alter the canvas size as needed.
function MouseWheelHandler(e) {
// cross-browser wheel delta
var e = window.event || e; // old IE support
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
canvas.style.width = Math.max(50, Math.min(800, canvas.width + (30 * delta))) + "px";
canvas.style.height = Math.max(50, Math.min(800, canvas.width + (30 * delta))) + "px";
return false;
}

Pan Svg While dragging elements

I want to achieve 'Panning' in svg while 'dragging' an element in particular direction.
Let say i select an element and start 'dragging' it upward untill it reached top of screen, now my svg should pan upwards automatically, without causing any problem with dragging. how i can achieve this.?
i have made a small mockup of this, where user can select and drag elements. it also contain two button, which cause svg to pan upward and downward. I am achiveing 'Panning' by changing 'ViewBox' of svg. ( i have to use this logic, i cannot use any other solution);
here is the fiddle link : http://jsfiddle.net/9J25r/
Complete Code:-
addEventListener('mousedown', mousedown, false);
var mx, my;
var dx, dy;
var mainsvg = document.getElementById('svg');
var selectedElement;
var eleTx, eleTy;
function getSvgCordinates(event) {
var m = mainsvg.getScreenCTM();
var p = mainsvg.createSVGPoint();
var x, y;
x = event.pageX;
y = event.pageY;
p.x = x;
p.y = y;
p = p.matrixTransform(m.inverse());
x = p.x;
y = p.y;
x = parseFloat(x.toFixed(3));
y = parseFloat(y.toFixed(3));
return {x: x, y: y};
}
function mousedown(event) {
if (event.target.id === 'arrow_t') {
panning('up');
}
else if (event.target.id === 'arrow_b') {
panning('down');
}
else if (event.target.id.split('_')[0] === 'rect') {
selectedElement = event.target;
var translatexy = selectedElement.getAttribute('transform');
translatexy = translatexy.split('(');
translatexy = translatexy[1].split(',');
eleTx = translatexy[0];
translatexy = translatexy[1].split(')');
eleTy = translatexy[0];
eleTx = parseFloat(eleTx);
eleTy = parseFloat(eleTy);
var xy = getSvgCordinates(event);
mx = xy.x;
my = xy.y;
mx = parseFloat(mx);
my = parseFloat(my);
addEventListener('mousemove', drag, false);
addEventListener('mouseup', mouseup, false);
}
}
function drag(event) {
var xy = getSvgCordinates(event);
dx = xy.x - mx;
dy = xy.y - my;
selectedElement.setAttribute('transform', 'translate(' + (eleTx + dx) + ',' + (eleTy + dy) + ')');
}
function mouseup(event) {
removeEventListener('mousemove', drag, false);
removeEventListener('mouseup', mouseup, false);
}
function panning(direction) {
var viewBox = svg.getAttribute('viewBox');
viewBox = viewBox.split(' ');
var y = parseFloat(viewBox[1]);
if (direction === 'up')
{
y+=5;
}
else if (direction === 'down')
{
y-=5;
}
viewBox=viewBox[0]+' '+y+' '+viewBox[2]+' '+viewBox[3];
svg.setAttribute('viewBox',viewBox);
}
here is the fiddle link : http://jsfiddle.net/9J25r/
EDIT:- (UPDATE)
I use the solution of Ian , it works well on the sample, but when i applied it to my original application, it did not work. check the below gif. You can see the 'gap' between mouse pointer and element. how i can remove that? .
This is one way, I've just done it with the Y/vertical for the moment...
You may want to adjust it, so that if the cursor is off the screen it adjusts the viewBox automatically as well, depends how you want it to drag (otherwise you will need to keep wiggling it to kick the drag func in).
var viewBox = svg.getAttribute('viewBox');
viewBoxSplit = viewBox.split(' ');
if( ely < viewBoxSplit[1] ) {
panning('down');
} else if( ely + +event.target.getAttribute('height')> +viewBoxSplit[1] + 300 ) {
panning('up');
}
jsfiddle here

Categories