Issues with making images draggable - javascript

I am currently creating my own website. I need code to allow users to change the position of some images by dragging them. I have written this HTML, CSS and JS in separate files.
The whole click and drag think works, when I click on the image and drag it on any side, it moves.
When I release the mouse button nothing happens. I have tried break and return, but they don't stop the function. Do you have any suggestions?
PS: I'm only a beginner in JS, so if you have any other advice about my code, go for it!
class img {
constructor(id, left, top) {
this.id = "#" + id;
this.left = parseInt(left, 10);
this.top = parseInt(top, 10);
};
};
$(".draggable").on("mousedown", function detectClick(focus) {
var stop = false;
var clickPos = [focus.pageX, focus.pageY];
var selected = new img($(this).attr('id'), $(this).css("left"), $(this).css("top"));
console.log(clickPos, selected);
$(selected.id).on("mousemove", function startMvt(move) {
var newPos = [move.pageX, move.pageY];
console.log(newPos);
$(selected.id).css("top", selected.top + newPos[1] - clickPos[1]);
$(selected.id).css("left", selected.left + newPos[0] - clickPos[0]);
$(selected.id).on("mouseup", function stop() {
var stop = true
console.log("stopped moving!");
return;
});
if (stop) {
return false;
};
});
if (stop) {
return false;
};
}).delay(5);
.draggable {
position: relative;
}
#img_1 {
top: 10%;
left: 20%;
z-index: 1;
cursor: move;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<img class="draggable" id="img_1" draggable="false" src="content/medium_im_test.jpg">

$(".draggable").on("mousedown",
you are only calling a function on the event of mousedown, you need a corresponding mouseup event handler.

how about adding
.mouseup(function(){
alert('up');
});
at the end or .delay(5);
like
$(".draggable").on("mousedown", function detectClick(focus) {
var stop = false;
var clickPos = [focus.pageX, focus.pageY];
var selected = new img($(this).attr('id'), $(this).css("left"), $(this).css("top"));
console.log(clickPos, selected);
$(selected.id).on("mousemove", function startMvt(move) {
var newPos = [move.pageX, move.pageY];
console.log(newPos);
$(selected.id).css("top", selected.top + newPos[1] - clickPos[1]);
$(selected.id).css("left", selected.left + newPos[0] - clickPos[0]);
$(selected.id).on("mouseup", function stop() {
var stop = true
console.log("stopped moving!");
return;
});
if (stop) {
return false;
};
});
if (stop) {
return false;
};
}).delay(5).mouseup(function(){
alert('up');
});
it worked for me!! I am also a beginner js programmer! so I don't fully understand your code, but I think you have to do something where I put alert('up')

Related

How do I reference a variable for clearInterval in a different function?

I have a circular image I want to spin with a button click and stop with a different button click.
The spin() function works, but the stop() does not because the values die outside of the spin() function (correct me if I'm wrong). How should I solve this? I'm open to using the same button to spin and stop, but my if else statement produces the same result - spinning the image works, but I can't stop it.
Another unintended bug is that if I click on the spin button more than once, the spinning gets faster.
Here's my code:
var spinImage;
function spin(){
spinImage = document.getElementById('spinImage'), degree = 0;
setInterval(function() {
spinImage.style.transform = "rotate(" + ++degree + "deg)";}, 1);
}
function stop(){
clearInterval(spinImage);
}
I need to stick to plain javascript and use the setInterval/clearInterval methods.
the value you are looking for is the return value of setInterval:
var intervalId;
function spin() {
var spinImage = document.getElementById('spinImage');
var degree = 0;
intervalId = setInterval(function() {
spinImage.style.transform = 'rotate(' + ++degree + 'deg)';
}, 1);
}
function stop() {
clearInterval(intervalId);
}
You need to get the id when initializing setInterval (here store in spin variable). That variable will have reference to setInterval so that you can clear it up using stop function.
Another unintended bug (faster spinning on consecutive clicks) is due to the fact that, previous setInterval id is not cleared and new one is created. So, the number of times you click; you will have that many setIntervals. To avoid it, you need to clear up the existing id and then re-initialize with the new set interval.
Have fixed the code. Please see:
var spinImage;
var spin;
function spin(){
spinImage = document.getElementById('spinImage');
degree = 0;
if(spin) stop();
spin = setInterval(function() {
spinImage.style.transform = "rotate(" + ++degree + "deg)";
}, 1);
}
function stop(){
clearInterval(spin);
}
Hope it helps. Revert for any doubts/clarifications
You need to remember the interval id, so that you can stop it.
let intervalId = null;
function spin() {
spinImage = document.getElementById('spinImage'), degree = 0;
intervalId = setInterval(function() {
spinImage.style.transform = "rotate(" + ++degree + "deg)";
}, 1);
}
function stop() {
clearInterval(intervalId);
}
ES6 Class
You can encapsulate this logic into a reusable class object i.e. ImageSpinner.
const main = () => {
const spinner = new ImageSpinner('#spinImage');
addEventListeners('.btn-toggle', 'click', (e) => {
spinner.toggle();
let attr = `data-text-${spinner.isRunning() ? 'off' : 'on'}`;
e.currentTarget.textContent = e.currentTarget.getAttribute(attr);
});
addEventListeners('.btn-reset', 'click', (e) => {
spinner.reset();
let btn = document.querySelector('.btn-toggle');
btn.textContent = btn.getAttribute('data-text-on');
});
}
class ImageSpinner {
constructor(selector, rate) {
/* #protected */ this.image = document.querySelector(selector);
/* #protected */ this.rate = rate;
/* #private */ this.__intervalId = null;
/* #private */ this.__degree = 0;
}
/* #public */ isRunning() {
return this.__intervalId != null;
}
/* #public */ reset() {
if (this.isRunning()) this.stop();
this.__degree = 0;
this.__rotate();
}
/* #public */ start() {
if (!this.isRunning()) {
this.__intervalId = setInterval(() => { this.update() }, this.rate);
} else {
console.log('Already running...');
}
}
/* #public */ stop() {
if (this.isRunning()) {
clearInterval(this.__intervalId);
this.__intervalId = null;
} else {
console.log('Not currently running...');
}
}
/* #public */ toggle() {
this.isRunning() ? this.stop() : this.start();
}
/* #protected */ update() {
this.__degree = (this.__degree + 1) % 360;
this.__rotate();
}
/* #private */ __rotate() {
this.image.style.transform = "rotate(" + this.__degree + "deg)";
}
}
/*
* Can either be an object of event handlers,
* or an event name followed by a function.
*/
function addEventListeners(elementsOrSelector, events) {
((els) => els.forEach(el =>
(typeof events === 'string' && arguments.length > 2)
? el.addEventListener(arguments[1], arguments[2])
: Object.keys(events)
.forEach(name => el.addEventListener(name, events[name]))))
(Array.from((typeof elementsOrSelector === 'string')
? document.querySelectorAll(elementsOrSelector)
: elementsOrSelector.entries
? elementsOrSelector
: [elementsOrSelector]));
}
main(); // Execute main function
body {
background: #222;
padding: 1.25em;
}
img {
border: 2px dotted white;
margin-left: 0.5em;
}
.btn-wrapper {
margin-top: 1.75em;
}
.btn-toggle {
display: inline-block;
width: 64px;
cursor: pointer;
}
<img id="spinImage" src="https://lh5.googleusercontent.com/-ajlbNdBMWwA/AAAAAAAAAAI/AAAAAAAAAAo/ND2LpoxaqAQ/photo.jpg?sz=96" alt="" width="96" height="96"/>
<div class="btn-wrapper">
<button class="btn btn-toggle" data-text-on="Start" data-text-off="Pause">Start</button>
<button class="btn btn-reset">Reset</button>
</div>
<!DOCTYPE html>
<head>
<title>Spin</title>
</head>
<body>
<button id='spinStart'>Start</button>
<button id='spinStop'>Stop</button>
<script>
var spinning = false;
var handle;
var counter = 0;
// Add click event listener to the start button
window.document.getElementById('spinStart').addEventListener("click", start);
// Add click event listener to the stop button
window.document.getElementById('spinStop').addEventListener("click", stop);
function start() {
if(spinning === true) {
console.log('spinning already')
// Do nothing here as it's already spinning.
spinning = !spinning;
}
if(spinning === false) {
// Not spinning here, so we have to init the setInterval
handle = setInterval(() => {
console.log(counter);
counter++;
}, 1000);
spinning = !spinning;
}
}
function stop() {
//We only want to stop the spinner if it's actually spinning
if(spinning === true){
console.log('clearing the interval now!');
clearInterval(handle);
}
}
</script>
</body>
</html>
Substitute your code as fits.

call onmousedown and onclick using this inside a custom class with JS

I want to make circle(army) with move when I drag it and when I click it, it should not move but do something else.
fucntion unit() {
this.circle = document.createElementNS(svgNs, "circle");
//Some attributes are added to this.circle
document.getElementById("svg1").appendChild(this.circle);
this.moving = false;
this.circle.onclick = function () {
showForm(this);
}
this.circle.onmousedown = function () {
this.moving = true;
}
}
Alright, I know that you didn't tag jQuery in your code, but if you have looked at my other posts, I tend to go full jQuery. If you wanted to do this, do this:
let's say this applies to your circle id element:
$("#circle").draggable( 'enable' )
var isDragging = 0;
var isUp = 1;
$("#circle").mousedown(function() {
isDragging = 0;
isUp = 0;
})
$("#circle").mousemove(function() {
isDragging = 1;
});
$("#circle").mouseup(function() {
isDragging = 0;
isUp = 1;
});
$("#circle").mousedown(function() {
if (isUp == 1) {
//click function
} else if (isDragging == 1) {
//drag function
}
});
This might be a little buggy, but I tried.

toggle the mousemove mousestop event

My question is simple. When my mouse moves, I would like to execute a function which would print "you are moving" in a div tag. When my mouse is not moving, I would like the text function to go away.
Below is a pseudo code that i can think of. So for example, the text function will be called every 50/1000 of a second and check if the mouse is moving. if it is moving, the text will show. if the mouse is not moving, the text will not show. How can i achieve this since there is no mousestop event?
$(function() { setInterval(text, 50);
});
function text() {
/*Do something to check if mouse is moving*/
/*if mouse is moving*/
if{
$("#shu").text("You are moving");
} else {
$("#shu").remove();
}
}
Pure javascript solution:
var shu = document.getElementById("shu");
var timeout;
document.addEventListener("mousemove", function() {
shu.innerHTML = "You are moving";
if (timeout) clearTimeout(timeout);
timeout = setTimeout(mouseStop, 150);
});
function mouseStop() {
shu.innerHTML = "";
}
jsFiddle
You can use jQuery .mousemove function along with set interval to check the mouse movement.
Here is an example code.
var lastTimeMouseMoved = "";
jQuery(document).ready(function(){
$(document).mousemove(function(e){
$(".text").show();
lastTimeMouseMoved = new Date().getTime();
var t=setTimeout(function(){
var currentTime = new Date().getTime();
if(currentTime - lastTimeMouseMoved > 1000){
$('.text').fadeOut('fast');
}
},1000)
});
});
body{
background-color:#333;
}
.text{
display:none;
color:white;
font-size:25px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="text">Your are moving!!</div>
$(window).mousemove(
function() {
$("#shu").text("You are moving");
}, function() {
$("#shu").remove();
}
);
With jquery
var lastMove = new Date().getTime() ,currentTime;
$(window).mousemove(function(){
lastMove = new Date().getTime();
});
setInterval(function(){
currentTime = new Date().getTime();
if(currentTime - lastMove > 400)
$("#shu").empty();
else
$("#shu").text("moving");
},20);
function mouseStop(callback) {
$(window).mousemove((function() {
var t;
return function() {
if(typeof t !='undefined')
clearTimeout(t); //we should check that `t` is not null
t = setTimeout(function() {
callback();
}, 200)//change to 200 so it will not trigger on mouse move
}
})())
}
mouseStop(function() {
console.log("stop!!!")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Stop timeOut and animation on mouseenter and continue on mouseleave

I wanted to get rid of a slider-plugin, so i tried to build my own,
everything works nice but i´m stuck to stop the whole thing on hover and restart it again on mouseleave,
here is my js :
function startPslider(val) {
if (!val) {
val = 0;
}
var holder = 'slideHolder';
var text = 'slideText';
startInterval(holder, text, val);
}
function startInterval(holder, text, val) {
var t;
var i = val;
if (i > 2) {
i = 0
}
$('#' + holder + i).animate({
opacity: 1,
}, function () {
$(this).addClass('active')
$('.' + text + i).animate({
opacity: 1,
left: 0
}, 1200);
t = setTimeout(function () {
$('.' + text + i).animate({
opacity: 0,
left: '-400px'
}, 1200);
$('#' + holder + i).animate({
opacity: 0,
}, 2200).removeClass('active');
startPslider(i + 1);
}, 4000)
});
// Here´s the not working hover-function
$('#hpCanvas').hover(function () {
clearTimeout(t);
}, function () {
var id = $('.active').attr('id');
var slide = id.substring(11, 22);
console.log(slide)
startPslider(slide);
});
}
$(function () {
startPslider();
});
tryed to solve this with adding class 'active' to the current holder and at hover-out try to catch the current-slide number (val) and restart it again, starting on the correct slide, but it´s not working as I wish,
have a look at this fiddle, http://jsfiddle.net/zDh76/ you will find html and css there, as you see everything works fine as long you do not hover.
Maybe anyone has a helping hint how to stop the animation, clear the timer and go on with the correct slide on mouseleave?
UPDATE
i separated start and end-interval
function startPslider(i) {
if(!i){
i=0;
}
if(i >2){
i=0
}
console.log('started Slider with slide:'+i)
var holder = 'slideHolder';
var text = 'slideText';
startInterval(holder, text, i);
}
function startInterval(holder,text,i) {
var t;
var v;
console.log('started Interval with slide:'+i);
$('#'+holder+i).animate({
opacity:1,
}, function(){
$('.'+text+i).animate({
opacity:1,
left:0
},1200);
t= setTimeout(function(){endInterval(holder,text,i); },4000);
});
}
function endInterval(holder,text,i,cont){
console.log('end Interval with slide:'+i);
$('.'+text+i).animate({
opacity:0,
left:'-400px'
},1200);
$('#'+holder+i).animate({
opacity:0,
},2200, function(){
$('.slideHolder').css('opacity',0);
i = i+1;
startPslider(i);
});
}
I found it out myself,
i needed to unbind the hover event on #hpCanvas inside the mouseleave function like
$('#hpCanvas').hover(function(){
$('.slideHolder, .slideText').stop(true,true);
clearTimeout(t)
},function(){
endInterval(holder,text,i);
$('#hpCanvas').unbind('mouseenter mouseleave')
})
as the next call to bind it with hover event is inside the mouseleave-part of the first hover event.
thanks anyway for everyone reading this

Clear a non-global timeout launched in jQuery plug-in

I try to set a timeout on an element, fired with a jQuery plugin. This timeout is set again in the function depending on conditions. But, I want to clear this element's timeout before set another (if I relaunch the plug-in), or clear this manually.
<div id="aaa" style="top: 0; width: 100px; height: 100px; background-color: #ff0000;"></div>
Here's my code (now on http://jsfiddle.net/Ppvf9/)
$(function() {
$('#aaa').myPlugin(0);
});
(function($) {
$.fn.myPlugin = function(loops) {
loops = loops === undefined ? 0 : loops;
this.each(function() {
var el = $(this),
loop = loops,
i = 0;
if (loops === false) {
clearTimeout(el.timer);
return;
}
var animate = function() {
var hPos = 0;
hPos = (i * 10) + 'px';
el.css('margin-top', hPos);
if (i < 25) {
i++;
} else {
if (loops === 0) {
i = 0;
} else {
loop--;
if (loop === 0) {
return;
} else {
i = 0;
}
}
}
el.timer = window.setTimeout(function () {
animate();
}, 1000/25);
};
clearTimeout(el.timer);
//$('<img/>').load(function() {
// there's more here but it's not very important
animate();
//});
});
return this;
};
})(jQuery);
If I make $('#element').myPlugin();, it's launched. If I make it a second time, there's two timeout on it (see it by doing $('#aaa').myPlugin(0);
in console). And I want to be able to clear this with $('#element').myPlugin(false);.
What am I doing wrong?
EDIT :
SOLVED by setting two var to access this and $(this) here : http://jsfiddle.net/Ppvf9/2/
try saving the timeout handle as a property of the element. Or maintain a static lookup table that maps elements to their timeout handles.
Something like this:
el.timer = window.setTimeout(...);
I assume you need one timer per element. Not a single timer for all elements.

Categories