Scroll function moves exponentially - javascript

I have a scroll function for my canvas which detects distance moved by my mouse and offsets all my images in the canvas.
The problem is i barely move the mouse and the offset number exponentially increases and im not sure why... this is my function that deals with offset calculation:
canvas.addEventListener('mousedown', scrol_cnv, false);
function scroll_cnv(e) {
if (e.button == 2) {//right click only
var x = e.pageX; // get click X
var y = e.pageY; //get click Y
function clear() {
this.removeEventListener('mousemove', updt, false);
}
function updt(evt) {
var difx = evt.pageX - x;
var dify = evt.pageY - y;
//this is where offset is becoming incorrect
//offsets is globally defined `window.offsets = {}`
offsets.cur_offsetx -= difx;
offsets.cur_offsety -= dify;
}
this.addEventListener('mousemove', updt, false);
this.addEventListener('mouseup', clear, false);
}
}
Am i subtracting the offset incorrectly ?

You want the offset to be based on the offset at the time of mousedown. Events that happen frequently shouldn't be changing things without a basis.
You probably also want to remove your mouseup listener, otherwise you get an additional one every click (no functional harm, just unnecessary overhead).
canvas.addEventListener('mousedown', scrol_cnv, false);
function scroll_cnv(e) {
if (e.button == 2) {//right click only
var x = e.pageX; // get click X
var y = e.pageY; //get click Y
// store the initial offsets
var init_offsetx = offsets.cur_offsetx;
var init_offsety = offsets.cur_offsety;
function clear() {
this.removeEventListener('mousemove', updt, false);
this.removeEventListener('mouseup', clear, false);
}
function updt(evt) {
var difx = evt.pageX - x;
var dify = evt.pageY - y;
//this is where offset is becoming incorrect
//offsets is globally defined `window.offsets = {}`
offsets.cur_offsetx = init_offsetx - difx;
offsets.cur_offsety = init_offsetx - dify;
}
this.addEventListener('mousemove', updt, false);
this.addEventListener('mouseup', clear, false);
}
}

Related

Scroll the page when the user's mouse reaches the top/bottom of the page

I'm trying to create drag-and-drop and I want my window to be scrolled in case I start the drag and reach the top / bottom of my page until I come out of the top/bottom "zone". So far what I have written works differently and I can't figure out a way to make it work the way I want.
Is there any way to do so using vanilla JS?
let mouseDown = true;
function mousedown() {
mouseDown = true;
}
function mouseup() {
mouseDown = false;
}
if (!document.querySelector(".arrow-timeline")) {
this.element.addEventListener('mousemove', function() {
let x, y;
function handleMouse(e) {
// Verify that x and y already have some value
if (x && y && mouseDown) {
// Scroll window by difference between current and previous positions
window.scrollBy(e.clientX - x, e.clientY - y);
}
// Store current position
x = e.clientX;
y = e.clientY;
}
// Assign handleMouse to mouse movement events
document.onmousedown = mousedown;
document.onmousemove = handleMouse;
document.onmouseup = mouseup;
})
}

How to create a resizable rectangle in JavaScript?

What I mean is that the user presses a mouse button at point xy on an HTML canvas and while the mouse button is pressed the rectangle can be resized according to the movement of the cursor with point xy fixed. Like how highlighting works.
This is what I've got so far but it doesn't seem to be working:
canvas.addEventListener('mousedown', function(e){
var rectx = e.clientX;
var recty = e.clientY;
canvas.onmousemove = function(e){
var df = e.clientX;
var fg = e.clientY;
};
context.rect(rectx, recty, df-rectx, fg-recty);
context.stroke();
}, false);
Assuming there are no transforms (scale, translate) on your canvas context.
Basic steps for having a resizable rectangle are as follows:
Create a mousedown listener that sets a flag indicating the use is holding down the mouse button, as well as sets the "anchor," or initial coordinates.
Create a mouseup listener that unsets the flag.
Create a mousemove listener that, if the flag indicates the mouse is down, redraws the canvas with the rectangle's size changed according to mouse coordinates.
An important note is that client coordinates in the event object are relative to the page, not to your canvas element. You will frequently need to convert clientX and clientY into canvas coordinates:
var getCanvasCoords = function (clientX, clientY) {
var rect = canvas.getBoundingClientRect();
return {
x: clientX - rect.left,
y: clientY - rect.top
};
};
The first two steps look something like this:
var anchorX;
var anchorY;
var mouseDown = false;
canvas.addEventListener('mousedown', function (event) {
var coords = getCanvasCoords(event.clientX, event.clientY);
anchorX = coords.x;
anchorY = coords.y;
mouseDown = true;
});
canvas.addEventListener('mouseup', function (event) {
mouseDown = false;
});
And the mousemove handler:
canvas.addEventListener('mousemove', function (event) {
var coords = getCanvasCoords(event.clientX, event.clientY);
var width = coords.x - anchorX;
var height = coords.y - anchorY;
// clear canvas for redrawing
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillRect(anchorX, anchorY, width, height);
});
Don't Render from mouse events!
The given answer is correct but it is not the best way to do this.
The are two reasons. First the mousemove event can fire up to 600+ times a second but the display only refreshes 60 times a second. Rendering from the input event is many time just a waste of CPU time as the results will be overwritten by the next mouse event before it is ever had a chance to be displayed.
The second reason is that dedicating an event listener to a single task makes it hard to add more functionality. You end up adding more and more code to the mousemove event to handle all the types of input, most of which can be ignored because of the high update speed of the mouse.
Mouse listeners
Mouse event listeners do the minimum possible. They just record the mouse state and no more. Also all mouse events return the mouse position. You should not ignore the mouse position for events like mouse down and up
The following function creates a mouse object for a element. The mouse object has the x,y position relative to the to left of the element, and the current button states for 3 buttons it is left to right button1, button2, button3.
Also when the mouse leaves the element and then releases the mouse button the mouse for the element will not see the mouseup event and not know the mouse button is up. To prevent the mouse buttons from getting stuck you turn off the buttons when the mouse leaves the element.
The best mouse listener is to the whole page as it can track mouse events that happen even when the mouse is outside the window/tab (if the window/tab has focus), but that is a little to complex for this answer.
Function to create a mouse for an element
function createMouse(element){
var mouse = {
x : 0,
y : 0,
button1 : false,
button2 : false,
button3 : false,
over : false,
};
function mouseEvent(event){
var bounds = element.getBoundingClientRect();
mouse.x = event.pageX - bounds.left - scrollX;
mouse.y = event.pageY - bounds.top - scrollY;
if(event.type === "mousedown"){
mouse["button"+event.which] = true;
} else if(event.type === "mouseup"){
mouse["button"+event.which] = false;
} else if(event.type === "mouseover"){
mouse.over = true;
} else if(event.type === "mouseout"){
mouse.over = false;
mouse.button1 = false; // turn of buttons to prevent them locking
mouse.button2 = false;
mouse.button3 = false;
}
event.preventDefault(); // stops default mouse behaviour.
}
var events = "mousemove,mousedown,mouseup,mouseout,mouseover".split(',');
events.forEach(eventType => element.addEventListener(eventType,mouseEvent));
mouse.remove = function(){
events.forEach(eventType => element.removeEventListener(eventType, mouseEvent));
}
return mouse;
}
Using the mouse
It is now just a matter of creating a mouse for the element
var canMouse = createMouse(canvas);
And then in your main render loop do the dragging.
var drag = {
x : 0,
y : 0,
x1 : 0,
y1 : 0,
dragging : false,
top : 0,
left : 0,
width : 0,
height : 0,
}
function mainLoop(){
if(canMouse.button1){ // is button down
if(!drag.dragging){ // is dragging
drag.x = canMouse.x;
drag.y = canMouse.y;
drag.dragging = true;
}
drag.x1 = canMouse.x;
drag.y1 = canMouse.y;
drag.top = Math.min(drag.y, drag.y1);
drag.left = Math.min(drag.x, drag.x1);
drag.width = Math.abs(drag.x - drag.x1);
drag.height = Math.abs(drag.y - drag.y1);
}else{
if(drag.dragging){
drag.dragging = false;
}
}
}
Putting it all together
function createMouse(element){
var mouse = {
x : 0,
y : 0,
button1 : false,
button2 : false,
button3 : false,
over : false,
};
function mouseEvent(event){
var bounds = element.getBoundingClientRect();
// NOTE getting the border should not be done like this as
// it will not work in all cases.
var border = Number(element.style.border.split("px")[0])
mouse.x = event.pageX - bounds.left - scrollX - border;
mouse.y = event.pageY - bounds.top - scrollY - border;
if(event.type === "mousedown"){
mouse["button"+event.which] = true;
} else if(event.type === "mouseup"){
mouse["button"+event.which] = false;
} else if(event.type === "mouseover"){
mouse.over = true;
} else if(event.type === "mouseout"){
mouse.over = false;
mouse.button1 = false; // turn of buttons to prevent them locking
mouse.button2 = false;
mouse.button3 = false;
}
event.preventDefault(); // stops default mouse behaviour.
}
var events = "mousemove,mousedown,mouseup,mouseout,mouseover".split(',');
events.forEach(eventType => element.addEventListener(eventType,mouseEvent));
mouse.remove = function(){
events.forEach(eventType => element.removeEventListener(eventType, mouseEvent));
}
return mouse;
}
var drag = {
x : 0,
y : 0,
x1 : 0,
y1 : 0,
dragging : false,
top : 0,
left : 0,
width : 0,
height : 0,
}
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
var mouse = createMouse(canvas);
function update(){
ctx.clearRect(0,0,canvas.width,canvas.height);
if(mouse.button1){ // is button down
if(!drag.dragging){ // is dragging
drag.x = mouse.x;
drag.y = mouse.y;
drag.dragging = true;
}
drag.x1 = mouse.x;
drag.y1 = mouse.y;
drag.top = Math.min(drag.y, drag.y1);
drag.left = Math.min(drag.x, drag.x1);
drag.width = Math.abs(drag.x - drag.x1);
drag.height = Math.abs(drag.y - drag.y1);
}else{
if(drag.dragging){
drag.dragging = false;
}
}
if(drag.dragging){
ctx.strokeRect(drag.left, drag.top, drag.width, drag.height);
}
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
border : 1px solid black;
}
Click drag to draw rectangle.<br>
<canvas id="canvas" width= "512" height = "256"></canvas>

Use mouse movement for change level/window in AMI Medical Imaging (AMI) JS ToolKit

I'm playing a bit with AMI Medical Imaging (AMI) JS ToolKit. Is there a way to move the windowing to a mouse event like right click & move?
I know that it's possible to change window/level with the menus on the examples, but I would like to change the controller to do it moving the mouse.
Thanks!
To control the window/level by moving the mouse you will have to listen to the mousemouve event then update the stackHelper -> slice -> windowWidth/Center as you wish.
You could enable window/level if the user press shift:
var drag = {
x: null,
y: null
}
var shiftDown = false;
function onKeyPressed(event){
shiftDown = event.shiftKey;
if(!shiftDown){
drag.x = null;
drag.y = null;
}
}
container.addEventListener('keydown', onKeyPressed);
container.addEventListener('keyup', onKeyPressed);
Then update the window/level on mouse move:
function onMouseMove(event){
if(!shiftDown || !stack || !stackHelper){
return;
}
if(drag.x === null){
drag.x = event.offsetX;
drag.y = event.offsetY;
}
var threshold = 15;
var dynamicRange = stack.minMax[1] - stack.minMax[0];
dynamicRange /= container.clientWidth;
if(Math.abs(event.offsetX - drag.x) > threshold){
// window width
stackHelper.slice.windowWidth += dynamicRange * (event.offsetX - drag.x);
drag.x = event.offsetX;
}
if(Math.abs(event.offsetY - drag.y) > threshold){
// window center
stackHelper.slice.windowCenter -= dynamicRange * (event.offsetY - drag.y);
drag.y = event.offsetY;
}
}
container.addEventListener('mousemove', onMouseMove);
See a live demo at (shift + mouse move to control the window level):
http://jsfiddle.net/vabL3qo0/41/

Two second delay on key down event [duplicate]

Here is my problem: http://testepi.kvalitne.cz/test/
I do not want the delay between a keypress and the movement of the square. I would also like to know how to move diagonally (pressing two keys at same time).
My code:
$(function(){
document.addEventListener("keydown", move, false);
var x = 0;
var y = 0;
function move(event){
if(event.keyCode==37){
x -= 10;
$("#square").css("left", x);
}
if(event.keyCode==39){
x += 10;
$("#square").css("left", x);
}
if(event.keyCode==38){
y -= 10;
$("#square").css("top", y);
}
if(event.keyCode==40){
y += 10;
$("#square").css("top", y);
}
}
});
First, to avoid the keypress/repeat delay, you have to wrap your program in a loop, and make the state of the keyboard available inside the scope of that loop, secondly to monitor multiple keypresses you need to keep track of individual keydown and keyup events:
var x = 0;
var y = 0;
// store the current pressed keys in an array
var keys = [];
// if the pressed key is 38 (w) then keys[38] will be true
// keys [38] will remain true untill the key is released (below)
// the same is true for any other key, we can now detect multiple
// keypresses
$(document).keydown(function (e) {
keys[e.keyCode] = true;
});
$(document).keyup(function (e) {
delete keys[e.keyCode];
});
// we use this function to call our mainLoop function every 200ms
// so we are not relying on the key events to move our square
setInterval( mainLoop , 200 );
function mainLoop() {
// here we query the array for each keyCode and execute the required actions
if(keys[37]){
x -= 10;
$("#square").css("left", x);
}
if(keys[39]){
x += 10;
$("#square").css("left", x);
}
if(keys[38]){
y -= 10;
$("#square").css("top", y);
}
if(keys[40]){
y += 10;
$("#square").css("top", y);
}
}
If you are trying to implement game-like 2d sprite movement, I suggest you have a notion of x and y velocity, rather than moving the sprite a fixed amount on keypress.
So on keypress, add or subtract from x or y velocity.
var xvel = 0,
yvel = 0,
x = 0,
y = 0;
setInterval(function () {
y += yvel;
x += xvel;
$("#square").css("left", x);
$("#square").css("top", y);
}, 16); //run an update every 16 millis
document.addEventListener("keydown", move, false);
function move(event){
if(event.keyCode==37){
xvel -= 10;
}
if(event.keyCode==39){
xvel += 10;
}
if(event.keyCode==38){
yvel -= 10;
}
if(event.keyCode==40){
yvel += 10;
}
}
You would also need to worry about a keyup event, however, as the velocity would stay until you cancelled it out.
You can use setInterval to update the position of the sprite every x milliseconds. (Game tick)
To move diagonally, just add/subtract from the velocity of both x and y simultaneously.
This is just an example, but there are more examples out there on sprite movement.
have you looked here? you should be able to do diagonal moving by checking if multiple keys have been pressed down without being released.

Create Dynamic Function Name for Event Listeners in JavaScript

I have this code for a math game. I have a function for a button which defines the location and then draws the button specified in the callback.
The problem is I can only create one of these, since event listeners are, for some baffling reason, referred to by the function they execute, rather than by some other string name.
Unfortunately this means writing a lot of repetitive code instead. Each function called by the eventListener would only differ by the name of the function for that particular button. How do people work around this problem? Is there no way to name the function (see: functAnim & functClick) using a string?
function factsButton(x, y, doFunction, contentCallback)
{
var getID = document.getElementById("canvas_1");
if (getID.getContext)
{
var ctx = getID.getContext("2d");
var btnW = 100;
var btnH = 100;
var cx = x - btnW/2;
var cy = y - btnH/2;
var left = cx;
var right = cx + btnW;
var top = cy;
var bottom = cy + btnH;
function functAnim(event)
{
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var mouseX = mousePos.x;
var mouseY = mousePos.y;
if (mouseX >= left
&& mouseX <= right
&& mouseY >= top
&& mouseY <= bottom)
{
contentCallback(cx, cy, btnW, btnH, true);
}
else
{
contentCallback(cx, cy, btnW, btnH, false);
}
}
function functClick(event)
{
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var clickX = mousePos.x;
var clickY = mousePos.y;
if (clickX >= left
&& clickX <= right
&& clickY >= top
&& clickY <= bottom)
{
doFunction();
getID.removeEventListener("mousemove", functAnim, false);
getID.removeEventListener("click", functClick, false);
}
}
contentCallback(cx, cy, btnW, btnH, false);
getID.addEventListener("click", functClick, false);
getID.addEventListener("mousemove", functAnim, false);
}
}
EDIT:
For some reason it wasn't as clear as to what I wanted. I'm not wanting to call the functions by string, but rather create them with dynamic names. For instance, since I need 4 buttons created, I would like to call my function like this: factsButton(50, 50, addClicked, "add"); With "add", instead of "functClick" they could be called "addClick" and "addAnim" or say it was "sub" instead of "add", it would create those events and functions as "subAnim" and "subClick". That way I get 8 separate event listeners each with different "names". Otherwise I end up with only 2 despite having 4 buttons.
The wording of your question confuses me for some reason :-\
Anyway,
If you want to call a function by string instead of by function pointer, add your functions as elements in a javascript object.
// a javascript object containing function definitions
var myFunctions={
functAnim: function(event)
{
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var mouseX = mousePos.x;
var mouseY = mousePos.y;
if (mouseX >= left
&& mouseX <= right
&& mouseY >= top
&& mouseY <= bottom)
{
contentCallback(cx, cy, btnW, btnH, true);
}
else
{
contentCallback(cx, cy, btnW, btnH, false);
}
},
functClick: function(event)
{
var mousePos = getMousePos(getID, event);
var rect = getID.getBoundingClientRect();
var clickX = mousePos.x;
var clickY = mousePos.y;
if (clickX >= left
&& clickX <= right
&& clickY >= top
&& clickY <= bottom)
{
doFunction();
getID.removeEventListener("mousemove", functAnim, false);
getID.removeEventListener("click", functClick, false);
}
}
}
Then you can call your functions by string.
// useage
myFunctions["functAnim"](eventObject); // calls functAnim
myFunctions["functClick"](eventObject); // calls functClick

Categories

Resources