i am trying to drag and drop an image of a card on the canvas of the javascript but the mouseup event does not seem to be working even though it is inside the main(). Once the card is selected, it follows around the mouse but it does not seem to let go when i let go of the mouse. I also know that the image is repeated from not clearing the screen.
function main(){
var ctx = cvs.getContext("2d");
var img = new Image();
img.src = "allcards.png";
var imgX = 75;
var imgY = 75;
draw();
function draw(){
ctx.drawImage(img,0,0,97,129,imgX,imgY,100,100);
}
cvs.addEventListener("mouseup", function(ev){
greaterX = false;
lessX = false;
greaterY = false;
lessY = false;
}
);
cvs.addEventListener("mousedown", function(ev){
if(ev.clientX <= (imgX + 97)){
var greaterX = true;
}
if(ev.clientY <= (imgY + 129)){
var greaterY = true;
}
if(ev.clientX >= imgX){
var lessX = true;
}
if(ev.clientY >= imgY){
var lessY = true;
}
if(greaterX == true)
if(greaterY == true)
if(lessX == true)
if(lessY == true)
cvs.addEventListener("mousemove", function(ev){
var offsetX = (ev.clientX - imgX);
var offsetY = (ev.clientY - imgY);
imgX = imgX + offsetX;
imgY = imgY + offsetY;
draw();
});
});
};
greaterX, lessX, etc, are all defined with var inside of your mousedown function, meaning their scope is limited to the mousedown function only.
Therefore, it is useless to try and set them back to false inside of your mouseup function. You need to declare your variables in the main part of your function:
function main() {
var greaterX, lessX, greaterY, lessY;
var ctx = cvs.getContext("2D");
//etc...
Now, simply setting greaterX, lessX, etc back to false is not enough, because the mousemove event checker inside of mousedown is still active. When you apply an event listener, it stays there until you remove it.
So, the next step is to separate the mousemove event function into it's own function (I used "mouseMoveHandler" for the name) and remove the event listener using .removeEventListener(type, listener, useCapture) inside of mouseup.
The mousemove function:
function mouseMoveHandler(ev) {
offsetX = (ev.clientX - imgX);
offsetY = (ev.clientY - imgY);
imgX = imgX + offsetX;
imgY = imgY + offsetY;
draw();
}
The mousedown function (important part):
if (greaterX === true) { //you need the brackets for multi-line if statements
if (greaterY === true) {
if (lessX === true) {
if (lessY === true) {
cvs.addEventListener("mousemove",mouseMoveHandler,false);
}
}
}
}
And finally, the mouseup function:
cvs.addEventListener("mouseup", function(ev) {
greaterX = false;
lessX = false;
greaterY = false;
lessY = false;
cvs.removeEventListener('mousemove',mouseMoveHandler,false);
});
Here's a jsFiddle with the solution, but not using your image.
Related
I just adapted this code to play a sequence of images when you move the mouse on the x axis. I have two questions about this code:
I can't adapt the length to the width of the div, not to the width of the whole window.
There are "flashes" when I move the mouse, I have the impression that it comes from the loading of the images?
Thanks for your help.
Live: https://codepen.io/nicolastilly/pen/jOBKxKK
Code:
var blocWidth;
var imgblock = ['https://assets.codepen.io/265602/frame0.png',
'https://assets.codepen.io/265602/frame1.png',
'https://assets.codepen.io/265602/frame2.png',
'https://assets.codepen.io/265602/frame3.png',
'https://assets.codepen.io/265602/frame4.png',
'https://assets.codepen.io/265602/frame5.png',
'https://assets.codepen.io/265602/frame6.png',
'https://assets.codepen.io/265602/frame7.png',
'https://assets.codepen.io/265602/frame8.png',
'https://assets.codepen.io/265602/frame9.png',
'https://assets.codepen.io/265602/frame10.png',
'https://assets.codepen.io/265602/frame11.png',
'https://assets.codepen.io/265602/frame12.png',
'https://assets.codepen.io/265602/frame13.png',
'https://assets.codepen.io/265602/frame14.png',
'https://assets.codepen.io/265602/frame15.png',
'https://assets.codepen.io/265602/frame16.png',
'https://assets.codepen.io/265602/frame17.png',
'https://assets.codepen.io/265602/frame18.png',
'https://assets.codepen.io/265602/frame19.png',
'https://assets.codepen.io/265602/frame20.png'];
function onMouseMove(e) {
var x = e.pageX;
var theimg = imgblock[parseInt(x / blocWidth * imgblock.length)];
$('.bloc1').css("background-image", "url('" + theimg + "')");
}
function onResize() {
blocWidth = $('.bloc1').width();
}
function onResize() {
blocWidth = $(document).width();
}
$(window).on('mousemove', onMouseMove);
$(window).resize(onResize);
onResize();
As for flickering, you can circumvent that by preloading the images
let loaded = 0 ;
imgblock.forEach(url => {
let img=new Image();
img.src=url;
img.onload = onImgLoaded
})
function onImgLoaded() {
loaded++;
if (loaded == imgblock.length-1) console.log('all images have pre-loaded');
}
As for the sizing issue, you need to take into account if you're mousing over the target and accounting for the div's left value in your calculations. Get rid of both these:
function onResize() {
blocWidth = $('.bloc1').width();
}
function onResize() {
blocWidth = $(document).width();
}
replace with:
let blocStartX;
function onResize() {
let pos = $(".bloc1")[0].getBoundingClientRect()
console.log(pos)
blocWidth = pos.width;
blocStartX = pos.x
}
Detect the mouseover:
let isOnDiv = false
$(".bloc1").mouseenter(function(){isOnDiv=true;});
$(".bloc1").mouseleave(function(){isOnDiv=false;});
and add this to the top of your mousemove function:
if (!isOnDiv) return
var x = e.pageX - blocStartX;
https://codepen.io/john-tyner/pen/wvJXXQZ
I am fairly new to JavaScript and have searched everywhere for an answer to my question and cant seem to find anything related at all. This tells me that I'm missing something with my understanding of how my program works.
I have written a small game where the player navigates through a randomly generated maze using a gameloop that checks keydown events every x milliseconds. The game has a difficulty dropdown menu and then the game is started my clicking a button that calls a function to create a canvas where the game is drawn.
My problem is that when the button is clicked again to create a new maze without reloading the page, the gameloop for the original maze is still running and so key events are registered twice. This is causing some unexpected behavior. It's as though every time the button is clicked, a new instance of the function is running. Is there some way that each time the button is clicked I can set it to stop the previous game function?
var canvas;
var div;
var mazeGenButton;
$(document).ready(function () {
canvas = null;
div = document.getElementById('canvascontainer');;
mazeGenButton = document.getElementById("mazeGenButton");
mazeGenButton.onclick = createInstance;
});
function createInstance() {
if (canvas != null) {
div.removeChild(document.getElementById("myCanvas"));
}
canvas = document.createElement('canvas');
canvas.id = "myCanvas";
canvas.width = 1000;
canvas.height = 1000;
div.appendChild(canvas);
drawMaze();
};
var drawMaze = function () {
//code here to create the game(not posted)
//here is the Key listener - not sure if it's related
var keyState = {};
window.addEventListener('keydown', function (e) {
keyState[e.keyCode || e.which] = true;
}, true);
window.addEventListener('keyup', function (e) {
keyState[e.keyCode || e.which] = false;
}, true);
function gameLoop() {
//left
if (keyState[37] || keyState[65]) {
if (isLegalMove(playerXPos - 1, playerYPos)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerXPos -= 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//right
if (keyState[39] || keyState[68]) {
if (isLegalMove(playerXPos + 1, playerYPos)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerXPos += 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//up
if (keyState[38] || keyState[87]) {
if (isLegalMove(playerXPos, playerYPos - 1)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerYPos -= 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
//down
if (keyState[40] || keyState[83]) {
if (isLegalMove(playerXPos, playerYPos + 1)) {
grid[playerXPos][playerYPos].removePlayerCell();
playerYPos += 1;
grid[playerXPos][playerYPos].setPlayerCell();
}
}
drawSurroundingCells();
setTimeout(gameLoop, 50);
}
}
So, I've created myself a little demo with javascript/html canvas in the context of a gameloop. You can move a small square by pressing the w,a,s,d keys. However, when held down for more than 3 or 4 seconds, the canvas becomes stuttery and the square almost stops moving.
Here's the javascript;
// --------------------------------------------------------------------
// -- MAIN GAME LOOP
// --------------------------------------------------------------------
function gameLoop(){
update();
render();
requestAnimationFrame(gameLoop);
}
function update(){
processInput();
};
function render(){
var canvas = document.getElementById('viewport');
var ctx = canvas.getContext('2d');
if(upDown){
rect.top -= rect.speed;
}else if(downDown){
rect.top += rect.speed;
}else if(leftDown){
rect.left -= rect.speed;
}else if(rightDown) {
rect.left += rect.speed;
}
ctx.clearRect(0, 0, 1024, 768);
ctx.beginPath();
ctx.rect(rect.left, rect.top, 50, 50, true);
ctx.closePath();
ctx.fill();
};
var rect = {
top: 0,
left: 0,
speed: 5
};
// --------------------------------------------------------------------
// -- OTHER FUNCTIONS
// --------------------------------------------------------------------
var rightDown = false;
var leftDown = false;
var upDown = false;
var downDown = false;
function processInput(){
$(document).keydown(function(e){
console.log(e.keyCode);
if(e.keyCode == 87){upDown = true;}
if(e.keyCode == 83){downDown = true;}
if(e.keyCode == 68){rightDown = true;}
if(e.keyCode == 65){leftDown = true;}
}).keyup(function(){
upDown = false;
downDown = false;
rightDown = false;
leftDown = false;
})
}
$(document).ready(function(){
requestAnimationFrame(gameLoop);
});
Anyone got any ideas?
Here's my codepen;
http://codepen.io/anon/pen/wKGJOr
The issue is because you're calling processInput (via update) within your gameloop. This function is attaching new keydown and keyup event handlers every time it is called. It's only necessary to call it once. Remove the call from update, and (for example) call it within the ready function instead:
$(document).ready(function(){
processInput();
requestAnimationFrame(gameLoop);
});
By registering more and more event handlers, you're causing a lot more code to run than is necessary, hence the stuttering.
Updated codepen.
I'm making a script to drag an image in a frame using eventListener instead of .onSomeEvent:
var destForm, destFoto, destX, destY;
function destaqueIni(){
destForm = document.forms["form_destaque"];
destFoto = new Object();
destFoto.img = document.getElementById("form_destaque_foto");
destFoto.w = destFoto.img.clientWidth;
destFoto.h = destFoto.img.clientHeight;
destFoto.x = 0; //Number(destForm.fotoDestaque_x.value);
destFoto.y = 0; //Number(destForm.fotoDestaque_y.value);
destFoto.escala = Number(destForm.fotoDestaque_escala.value);
//
document.getElementById("form_destaque_janela").style.height = "380px";
//
destFoto.img.draggable = true;
destFoto.img.addEventListener("dragstart", destaqueFotoDragStart, false);
destFoto.img.addEventListener("dragend", destaqueFotoDrag, false);
}
function destaqueFotoDragStart(evt){
//evt.preventDefault();
//console.log("destaqueFotoDrag: Foto Arrastada... img.x: "+evt.clientX+" destX: "+destX);
destX = evt.clientX;
destY = evt.clientY;
}
function destaqueFotoDrag(evt){
evt.preventDefault();
//console.log("destaqueFotoDrag: Foto Solta... img.x: "+evt.clientX+" destX: "+destX);
var x = destFoto.x + (evt.clientX-destX);
var y = destFoto.y + (evt.clientY-destY);
destForm.fotoDestaque_x.value = x;
destForm.fotoDestaque_y.value = y;
destaqueFotoAtualizar();
}
function destaqueFotoAtualizar(){
destFoto.img.style.left = String(Math.ceil(destForm.fotoDestaque_x.value/2))+"px";
destFoto.img.style.top = String(Math.ceil(destForm.fotoDestaque_y.value/2))+"px";
}
It works (still working, but is dragging). So the image will be moved but before the image be placed at the new x,y coordinates, the browser do an animation back to the original place - after it jumps to the right place. This is a pretty bug! How do I prevent the browser from making the animation after release? I tried preventDefault() but didn't work...
Search "stop drag image in browsers" and you will get your answer.
$("img").mousedown(function(){ return false; });
$(document).on("dragstart", function() { return false; });
$(document).on("dragstart", function(e) {
if (e.target.nodeName.toUpperCase() == "IMG") { return false; }
});
<div onmousedown="event.preventDefault ? event.preventDefault() : event.returnValue = false">asd</div>
<div onmousedown="return false">asd</div>
//also css has selection rules for different browser
I have a canvas that I can draw things what I want to do is generate new canvases dynamically when clicking a button.I've defined a generate function but it did not work
here is script
//<![CDATA[
window.addEventListener('load', function () {
// get the canvas element and its context
var canvas = document.getElementById('sketchpad');
var context = canvas.getContext('2d');
// create a drawer which tracks touch movements
var drawer = {
isDrawing: false,
touchstart: function (coors) {
context.beginPath();
context.moveTo(coors.x, coors.y);
this.isDrawing = true;
},
touchmove: function (coors) {
if (this.isDrawing) {
context.lineTo(coors.x, coors.y);
context.stroke();
}
},
touchend: function (coors) {
if (this.isDrawing) {
this.touchmove(coors);
this.isDrawing = false;
}
}
};
// create a function to pass touch events and coordinates to drawer
function draw(event) {
var type = null;
// map mouse events to touch events
switch(event.type){
case "mousedown":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchstart";
break;
case "mousemove":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchmove";
break;
case "mouseup":
event.touches = [];
event.touches[0] = {
pageX: event.pageX,
pageY: event.pageY
};
type = "touchend";
break;
}
// touchend clear the touches[0], so we need to use changedTouches[0]
var coors;
if(event.type === "touchend") {
coors = {
x: event.changedTouches[0].pageX,
y: event.changedTouches[0].pageY
};
}
else {
// get the touch coordinates
coors = {
x: event.touches[0].pageX,
y: event.touches[0].pageY
};
}
type = type || event.type
// pass the coordinates to the appropriate handler
drawer[type](coors);
}
// detect touch capabilities
var touchAvailable = ('createTouch' in document) || ('ontouchstart' in window);
// attach the touchstart, touchmove, touchend event listeners.
if(touchAvailable){
canvas.addEventListener('touchstart', draw, false);
canvas.addEventListener('touchmove', draw, false);
canvas.addEventListener('touchend', draw, false);
}
// attach the mousedown, mousemove, mouseup event listeners.
else {
canvas.addEventListener('mousedown', draw, false);
canvas.addEventListener('mousemove', draw, false);
canvas.addEventListener('mouseup', draw, false);
}
// prevent elastic scrolling
document.body.addEventListener('touchmove', function (event) {
event.preventDefault();
}, false); // end body.onTouchMove
}, false); // end window.onLoad
function generate(){
var newCanvas = document.createElement('canvas');
newCanvas.width = 400;
newCanvas.height = 400;
document.getElementById('container').appendChild(newCanvas);
ctx = newCanvas.getContext('2d');
}
//]]>
here is jsfiddle http://jsfiddle.net/regeme/WVUwn/
ps:drawing not displayed on jsfiddle however it works on my localhost I have totally no idea about it , anyway what I need is generate function , I did but I think I am missing something..
Any ideas? thanks..
Below is a function I wrote to dynamically create canvas.
If the canvas already exists (same ID) then that canvas is returned.
The pixelRatio parameter can be defaulted to 1. It's used for setting the correct size on retina displays (so for iPhone with Retina the value would be 2)
function createLayer(sizeW, sizeH, pixelRatio, id, zIndex) {
// *** An id must be given.
if (typeof id === undefined) {
return false;
}
// *** If z-index is less than zero we'll make it a buffer image.
isBuffer = (zIndex < 0) ? true : false;
// *** If the canvas exist, clean it and just return that.
var element = document.getElementById(id);
if (element !== null) {
return element;
}
// *** If no zIndex is passed in then default to 0.
if (typeof zIndex === undefined || zIndex < 0) {
zIndex = 0;
}
var canvas = document.createElement('canvas');
canvas.width = sizeW;
canvas.height = sizeH;
canvas.id = id;
canvas.style.width = sizeW*pixelRatio + "px";
canvas.style.height = sizeH*pixelRatio + "px";
canvas.style.position = "absolute";
canvas.style.zIndex = zIndex;
if (!isBuffer) {
var body = document.getElementsByTagName("body")[0];
body.appendChild(canvas);
}
return canvas;
}
Change the jsfiddle option
"onLoad"
to
"No Wrap - in <body>"
EDIT: See also similar question over here: Uncaught ReferenceError for a function defined in an onload function
JSFiddle options: http://doc.jsfiddle.net/basic/introduction.html#frameworks-and-extensions
This is the working update of your JSFIDDLE
javascript:
document.getElementById('generate').addEventListener('mousedown', generate, false);
I guess this is what you want.
I've just added an eventListener to your button in javascript code itself.
P.S.: I've also added black background color to canvas to show it on white background.