Dragging linked images (without firing the link) - javascript

I need to just drag/move around some images. Each image is linked to some other page. When I use drag functions I can drag the image but when I release it the link of the image fires up. I would need to drag around, release, and then click the image if I want to open the link. What can I do?
The JSFiddle: http://jsfiddle.net/grundum/wp0zhjbn/21/
I found How to HTML5-drag an element that has a link in it but is not clear and doesn't have a concrete answer. Any ideas?
Thanks in advance.
HTML
<div class="dragme" draggable="true">
<a draggable="false" href="https://placeholder.com/"><img class="dragme" draggable="false" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/microformat.svg"></a>
</div>
CSS
.dragme {
position: relative;
width: 270px;
height: 203px;
cursor: move;
}
#draggable {
background-color: #ccc;
border: 1px solid #000;
}
JS
function startDrag(e) {
// determine event object
if (!e) {
var e = window.event;
}
// IE uses srcElement, others use target
var targ = e.target ? e.target : e.srcElement;
if (targ.className != 'dragme') {
return
};
// calculate event X, Y coordinates
offsetX = e.clientX;
offsetY = e.clientY;
// assign default values for top and left properties
if (!targ.style.left) {
targ.style.left = '0px'
};
if (!targ.style.top) {
targ.style.top = '0px'
};
// calculate integer values for top and left
// properties
coordX = parseInt(targ.style.left);
coordY = parseInt(targ.style.top);
drag = true;
// move div element
document.onmousemove = dragDiv;
}
function dragDiv(e) {
if (!drag) {
return
};
if (!e) {
var e = window.event
};
var targ = e.target ? e.target : e.srcElement;
// move div element
targ.style.left = coordX + e.clientX - offsetX + 'px';
targ.style.top = coordY + e.clientY - offsetY + 'px';
return false;
}
function stopDrag() {
drag = false;
}
window.onload = function() {
document.onmousedown = startDrag;
document.onmouseup = stopDrag;
}

When I use drag functions I can drag the image but when I release it
the link of the image fires up. I would need to drag around, release,
and then click the image if I want to open the link. What can I do?
Then how can you recognize if element is being dragged or clicked? Time interval between onmousedown, onmouseup calls? Mouse movement?
One thing for sure you have to get rid of anchor tag or call in its click event handler preventDefault() as its getting in the way.
Ugly example of how that might work.
let state = {
startDrag: false,
drag: false,
clicked: false,
offsetX: -1,
offsetY: -1,
target: null,
anchor: null
}
let href = "https://placeholder.com/";
window.addEventListener("load", (event) => {
state.anchor = document.querySelector("#anchor");
state.anchor.addEventListener('click', (event) => {
if(!state.clicked) {
event.preventDefault()
}
});
document.addEventListener('mousedown', startDrag);
document.addEventListener('mouseup', stopDrag);
})
function startDrag(event) {
const target = event.target ? event.target : event.srcElement;
state.target = target;
const { clientX, clientY } = event;
state.offsetX = clientX;
state.offsetY = clientY;
state.startDrag = true;
if (!target.style.left) {
target.style.left = '0px'
};
if (!target.style.top) {
target.style.top = '0px'
};
state.coordX = parseInt(target.style.left);
state.coordY = parseInt(target.style.top);
document.onmousemove = dragDiv;
}
function dragDiv({clientX, clientY}) {
if(!state.startDrag) {
return;
}
const { target, coordX, coordY, offsetX, offsetY } = state;
state.drag = state.startDrag;
target.style.left = coordX + clientX - offsetX + 'px';
target.style.top = coordY + clientY - offsetY + 'px';
return false;
}
function stopDrag() {
document.onmousemove = null;
if(state.startDrag && state.drag) {
// handle stop dragging;
}
else {
// handle click;
state.clicked = true;
state.anchor.click();
// or location.href = href;
}
state.clicked = false;
state.startDrag = false;
state.drag = false;
}
.dragme {
position: relative;
width: 270px;
height: 203px;
cursor: move;
}
#draggable {
background-color: #ccc;
border: 1px solid #000;
}
<div class="dragme" draggable="true">
<a id="anchor" draggable="false" href="https://placeholder.com/"><img class="dragme" draggable="false" src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/microformat.svg"></a>
</div>

Related

Detect mouse drag and direction using pure javascript

I am trying to detect the mouse direction when dragging the mouse. When the mouse button is down and the user drags the mouse, I want the text to change to left or right depending on the mouse drag direction.
Here's my code.
var divOverlay = document.getElementById ("div");
var dragged = false
window.addEventListener('mousedown', function () { dragged = false })
document.addEventListener('mousemove', function () { dragged = true })
window.addEventListener('mouseup', function(e) {
if (dragged == true && e.pageX <= 0) {
direction = "left"
} else if (dragged == true && e.pageX >= 0) {
direction = "right"
}
divOverlay.innerHTML = direction;
oldx = e.pageX;
})
#div {
width: 100px;
height: 100px;
background: red;
}
<div id="div"></div>
I don't think I'm too far off but I can't workout what I am doing wrong so I need some help.
Here you go. It just needed a minor tweak.
var divOverlay = document.getElementById ("div");
var dragged = false
var oldX = 0;
window.addEventListener('mousedown', function (e) { oldX = e.pageX; dragged = false })
document.addEventListener('mousemove', function () { dragged = true })
window.addEventListener('mouseup', function(e) {
if (dragged == true && e.pageX < oldX) {
direction = "left"
} else if (dragged == true && e.pageX > oldX) {
direction = "right"
}
divOverlay.innerHTML = direction;
})
#div {
width: 100px;
height: 100px;
background: red;
}
<div id="div"></div>
You should add oldX to the position checking. You also need to initialize it.
var divOverlay = document.getElementById("div");
var dragged = false
var oldX = 0;
window.addEventListener('mousedown', function (e) {
dragged = false;
// set start position of drag
oldX = e.pageX;
});
document.addEventListener('mousemove', function () {
dragged = true
});
window.addEventListener('mouseup', function (e) {
// compare drag end position with start position
if (dragged == true && e.pageX <= oldX) {
direction = "left"
} else if (dragged == true && e.pageX > oldX) {
direction = "right"
}
divOverlay.innerHTML = direction;
});
#div {
width: 100px;
height: 100px;
background: red;
}
<div id="div"></div>
Having a separate function to handle the relative direction of the mouse on the X axis is more likely something suitable for what you would like to do. Instead of trying to update variables, remove the eventListener once the mouse is released.
See below as an example:
const body = document.body;
const container = document.getElementById('container');
const directionTag = document.getElementById("direction");
const moveFunc = function (e) {
let direction;
if (e.movementX < 0) {
container.style.backgroundColor = "red";
direction = "left";
} else if (e.movementX > 0) {
container.style.backgroundColor = "blue";
direction = "right";
}
if (typeof direction !== "undefined") {
directionTag.innerHTML = direction;
}
};
body.onmousedown = function () {
body.addEventListener("mousemove", moveFunc);
};
body.onmouseup = function () {
body.removeEventListener("mousemove", moveFunc);
};
#container {
height: 150px;
margin: 20px;
padding: 20px;
width: 300px;
border: 1px solid #888;
}
<body>
<div id="container">This will change when you hold down</div>
<p id="direction">Unset</p>
</body>

Change opacity then hide on swipe up (CSS / JS)

I'm looking to change opacity and then completely hide div on swipe up on certain threshold, like in the video below or in Photoswipe:
https://www.loom.com/share/29741bdadc7846bfbc747d3870815340
Unfortunately most off libraries only allow to register actual event start end, but not the amount of swiped pixels. How would I get the actual swiped distance and connect it to the swipe event?
Note: You can apply the animations used in this example on other elements like an overlay instead. The technique is the same.
Here is some code to move up an element, fade it out and remove it from display. Note that I only implemented the PointerEvent-api. You should also implement a fallback.
A summary about what is going on:
Detect a pointerdown on the element and allow the pointer to be used outside the element with setPointerCapture().
Detect a pointermove on the element. If the mouse/touch is moved up, also move up the element. ( I also restricted movement to the left, right, bottom, but you don't have to)
Detect a pointerup. After releasePointerCapture() the pointer will once more only be available in the default element and not outside it. Depending on the amount the element has moved up, the element is returned to its original position or animated out.
class SwipeOutBehaviour {
constructor(element) {
this.element = element;
this.dy = null;
this.initial_y = null;
this.animation_frame_state = 'completed';
if( window.PointerEvent ) {
this.element.addEventListener('pointerdown', this.start_drag.bind(this), true);
this.element.addEventListener('pointermove', this.drag.bind(this), true);
this.element.addEventListener('pointerup', this.drag_end.bind(this), true);
} else {
//should use composition instead if you re serious, for this example I only implemented PointerEvent some browsers will use Tpuchevent and MouseEvent
}
}
start_drag( event ){
event.preventDefault();
// only respond to a single touch
if( event.touches && event.touches.length > 1 ) return;
// allow pointerevents outside the target
event.target.setPointerCapture(event.pointerId);
// set initial pos
this.initial_y = ( event.targetTouches ) ? event.targetTouches[0].clientY : event.clientY;
}
drag( event ){
event.preventDefault();
if( this.initial_y === null ) return;
if( this.animation_frame_state === 'pending' ) return;
this.dy = ( event.targetTouches ) ? Math.floor( event.targetTouches[0].clientY - this.initial_y ) : Math.floor( event.clientY - this.initial_y );
if( this.dy > 0 ) return;
this.animation_frame_state = 'pending'
window.requestAnimationFrame( () => {
this.element.style.transform = `translateY(${this.dy}px)`
this.animation_frame_state = 'completed';
});
}
drag_end(event) {
event.preventDefault();
if(event.touches && event.touches.length > 0) return;
event.target.releasePointerCapture(event.pointerId);
if( this.dy < -100 ) {
window.requestAnimationFrame( () => {
this.element.style.transition = 'opacity 500ms, translateY 200ms';
this.element.style.transform = `translateY(-175px)`;
this.element.style.opacity = `0`;
this.animation_frame_state = 'completed';
window.setTimeout( () => {
// set display to none, you could remove it from the DOM instead
this.element.style.display = 'none';
}, 500)
});
} else {
window.requestAnimationFrame( () => {
this.element.style.transform = `translateY(0px)`
this.animation_frame_state = 'completed';
});
}
this.initial_y = null;
}
}
let element = document.getElementById('container');
new SwipeOutBehaviour( element );
#container {
margin: auto;
width: 150px;
height: 150px;
border: 1px solid black;
}
#box-of-doom {
margin: auto;
width: 200px;
height: 200px;
border: 1px solid red;
background: orange;
}
p {
text-align: center;
}
<p>Drag the item in the box of doom<p>
<div id='box-of-doom'>
<p>The box of doom<p>
</div>
<div id='container'>
<img alt='a placeholder' src='https://via.placeholder.com/150' />
</div>
Note: This answer is inspired by this documentation/article from Google about touch events, so you may want to read more there.
With a lot of event listeners and computed properties; I made a quick code pen using W3's draggable function, but added the opacity change myself:
// Make the DIV element draggable:
dragElement(document.getElementById("mydiv"));
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
//change background opacity:
const background = document.getElementById("background");
const bgHeight = background.offsetHeight;
const elmntHeight = elmnt.offsetHeight;
const adjustedBottom = bgHeight - elmntHeight;
const percentage = 1 - elmnt.offsetTop / adjustedBottom;
console.log(percentage)
background.style.opacity = percentage;
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}
body {
margin: 0;
}
#background {
background: black;
width: 100vw;
height: 100vh;
position: absolute;
}
#mydiv {
position: absolute;
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
}
#mydivheader {
padding: 10px;
cursor: move;
z-index: 10;
background-color: #2196F3;
color: #fff;
}
<div id="background"></div>
<!-- Draggable DIV -->
<div id="mydiv">
<!-- Include a header DIV with the same name as the draggable DIV, followed by "header" -->
<div id="mydivheader">Click here to move</div>
<p>Move</p>
<p>this</p>
<p>DIV</p>
</div>
</div>
Far from perfect, but hopefully demonstrates an idea to expand on.

Drop position problem when dragging DIV onto Canvas

The javascript below allows me to drag a DIV onto a canvas. I am loading a multi-page PDF (actually image files) in a popup window and placing canvas layers over them and loading shapes in each canvas at certain coordinates.
The code below is mostly working except for when the div is "dropped" onto the canvas, it does not drop where the cursor is. The div drops up and to the right of wherever my cursor is on the canvas.
The page width (918) and height (1188) are static and never change.
I'm assuming this has something to do with the div's offset as it's relative to the page's offset but I cannot seem to get it right.
Any suggestions are appreciated.
$('.canvas-container').each(function (index, item) {
var canvasContainer = $(this)[index];
var canvasObject = $("canvas", this)[index];
var divOffsetX, divOffsetY;
var sigDivs = $(".sigFields").last();
divOffsetX = sigDivs.offset().left;
divOffsetY = sigDivs.offset().top;
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleDragEnter(e) {
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over');
}
function handleDrop(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
}
if (e.stopPropagation) {
e.stopPropagation();
}
var div = document.querySelector('.sigFields div.div_dragging');
console.log('event: ', e);
var offset = $(this).offset();
var y = e.clientY - (offset.top + divOffsetY);
var x = e.clientX - (offset.left + divOffsetX);
var newSigDiv = new fabric.Rect({
width: 234,
height: 24,
left: x,
top: y,
lockRotation: true,
hasRotatingPoint: false
});
fabricCanvas.add(newSigDiv);
return false;
}
function handleDragEnd(e) {
[].forEach.call(divs, function (div) {
div.classList.remove('div_dragging');
});
}
var divs = document.querySelectorAll('.sigFields div');
[].forEach.call(divs, function (div) {
div.addEventListener('dragstart', handleDragStart, false);
div.addEventListener('dragend', handleDragEnd, false);
});
canvasContainer.addEventListener('dragenter', handleDragEnter, false);
canvasContainer.addEventListener('dragover', handleDragOver, false);
canvasContainer.addEventListener('dragleave', handleDragLeave, false);
canvasContainer.addEventListener('drop', handleDrop, false);
});
The fix was simple.
Rather than attempt some weird math with the offsets, I changed
left: x,
left: y
to
left: e.layerX,
top: e.layerY

Style drag ghost element

I am facing an issue, where I am dragging a div.
While the ghost element looks good on MacOs(yes both on Chrome and FireFox),it appears to be too transparent,in Windows (yes both on Chrome and FireFox.
I tried multiple approaches but nothing seems to work.
So is it possible to style the ghost element?
Also, I tried to make an image of that element on the go, and use it as ghost dragging image, but the transparency issue still remains.
You can do this by implementing dragging of the element yourself in JavaScript. That way, you can apply a CSS class to the element while it is being dragged, which styles it in any way you wish.
const d = 'dragging';
const container = document.getElementById('container');
const el = document.getElementById('drag');
var cOffX = 0;
var cOffY = 0;
el.addEventListener('mousedown', dragStart);
function dragStart(e) {
e = e || window.event;
e.preventDefault();
cOffX = e.clientX - el.offsetLeft;
cOffY = e.clientY - el.offsetTop;
document.addEventListener('mousemove', dragMove);
document.addEventListener('mouseup', dragEnd);
el.classList.add(d);
container.style.cursor = 'move';
};
function dragMove(e) {
e = e || window.event;
e.preventDefault();
el.style.top = (e.clientY - cOffY).toString() + 'px';
el.style.left = (e.clientX - cOffX).toString() + 'px';
};
function dragEnd(e) {
e = e || window.event;
e.preventDefault();
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
el.classList.remove(d);
container.style.cursor = null;
};
#container {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
#drag {
position: absolute;
height: 100px;
width: 100px;
background-color: lime;
border-radius: 0;
transition: background-color 0.25s, border-radius 0.25s;
cursor: move;
}
#drag.dragging {
background-color: navy;
border-radius: 50%;
}
<div id="container">
<div id="drag"></div>
</div>
As others have said, the ghosting is browser-based and can't be changed, so it seems you need your own solution if you want to get around that.

Dragging an image rendered on Canvas with a mobile device causes the image to disappear

I'm making an app that draws images on canvas with Fabric.js.
For tablets and smartphones, if you pan the drawn image by dragging, the image disappears.
I will paste a GIF image that shows the current situation.
demo GIF
I want to prevent images from disappearing by dragging panning even in browsers other than PC.
Please let me know if there is any other option to do this way.
The code is as follows:
By pressing the “Run code snippet”, you could see how it works.
When you upload an image, a "pan" button will appear on the canvas and below the canvas.
Press the "pan" button to pan the image.
var canvas = new fabric.Canvas("c");
$("input").on("change", function (e) {
var fr = new FileReader(e);
fr.onload = (e) => {
input(e.target.result);
};
fr.readAsDataURL(e.target.files[0]);
});
var input = function (url) {
fabric.Image.fromURL(url, function (oImg) {
canvas.setWidth(450);
var resizeScale = canvas.width / oImg.width;
oImg.scale(resizeScale);
canvas.setHeight(oImg.height * resizeScale);
canvas.clear();
canvas.add(oImg).renderAll();
canvas.selection = oImg.selectable = false;
$("#show").show();
canvas.hoverCursor = "default"
});
};
$("#pan").click(function () {
canvas.hoverCursor = "crosshair"
});
canvas.on("mouse:down", () => {
var panning;
if (canvas.hoverCursor === "crosshair") {
panning = true;
};
canvas.on("mouse:move", e => {
if (panning) {
const delta = new fabric.Point(e.e.movementX, e.e.movementY);
canvas.relativePan(delta);
};
});
canvas.on("mouse:up", () => {
panning = false;
});
});
html {
text-align: center;
margin: auto;
max-width: 550px;
}
input {
margin: 5%;
}
#show {
display: none;
}
canvas {
border: 2px solid black;
}
<input type='file' accept='image/*' />
<div id="show">
<canvas id="c"></canvas>
<button id="pan">pan</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/3.4.0/fabric.min.js"></script>
On a mobile device, the event passed into mouse:... listeners is not a MouseEvent but rather a TouchEvent. It has a different interface than that of a MouseEvent and therefore you cannot access a movementX property. You can calculate the delta x/y yourself via something like this:
let panning
let prevX
let prevY
canvas.on("mouse:down", (e) => {
panning = true
if (e.e instanceof TouchEvent) {
const {clientX, clientY} = e.e.touches[0]
prevX = clientX
prevY = clientY
}
})
canvas.on("mouse:move", e => {
if (panning) {
let delta
if (e.e instanceof TouchEvent) {
// we're on mobile
const {clientX, clientY} = e.e.touches[0]
delta = new fabric.Point(clientX - prevX, clientY - prevY)
prevX = clientX
prevY = clientY
} else {
// we're on desktop
delta = new fabric.Point(e.e.movementX, e.e.movementY)
}
console.log(delta.x, delta.y)
canvas.relativePan(delta)
}
})
canvas.on("mouse:up", () => {
panning = false
})
Here, I'm checking what kind of event was passed (via e.e instanceof TouchEvent) and calculate the delta accordingly. You might not need this if you only work with mobile devices.

Categories

Resources