Interspersing KineticJS and jQuery events - javascript

I am looking to build a simple photo collage using KineticJS and Canvas. In the canvas container, I have pre-defined image areas. I also have (in the DOM and outside of the canvas container), a strip of images which I want to drag and drop to the image wells on the canvas. I am able to drag an image and snap it to a (hard-coded) image well but while the image is moving on canvas I'm unable to get the x and y co-ordinates. I've tried using jQuery's drag/drag event but then it is blind to the canvas and I've tried to use use the KineticJS's dragover function but then I can't get the DOM image's x and y. The idea is that knowing the x,y of the image being dragged I can write some logic to figure out which image location it should get snapped to - as opposed to hard coding it's target.
Is it even possible to do this with KineticJS+jQuery - drag images from a DOM onto a canvas and have them snap into pre-defined image areas? Or, is there a simpler way?

as far as I understand your problem, I tried to reproduce it, but not seeing any difficulties
<div width="600" height="500" style="background-color:green;padding-left:60px" id="wrap">
<canvas id="a" width="300" height="225" style="background-color:yellow"></canvas>
</div>
<img id="dragMe" src="http://www.linuxmint.com/img/logo.png"/>
<div id="log">
<span class="a"></span>
<span class="b"></span>
<span class="c"></span>
<span class="d"></span>
</div>
$("#wrap").mousemove(function(e){
var pageCoords = "( " + e.pageX + ", " + e.pageY + " )";
var clientCoords = "( " + e.clientX + ", " + e.clientY + " )";
$("#log > span.a").text("( e.pageX, e.pageY ) : " + pageCoords);
$("#log > span.b").text("( e.clientX, e.clientY ) : " + clientCoords);
});
var p = $('#a').position();
$("#dragMe").draggable({
drag: function(event, ui) {
$("#log > span.c").text( ui.offset.left+':' +ui.offset.top);
$("#log > span.d").text( ( ui.offset.top-p.top)+':' +(ui.offset.left - p.left));
}
});
http://jsfiddle.net/agj3N/1/

When I asked you if you had a reason you had the strip of images outside of the KineticJS stage, you answered:
Only because when I toJSON it (for saving and calling up for a future edit), I just want the images on my photo collage and not the photos on the strip.
My answer to this is:
You don't have to use toJSON on the entire stage.
If you separate the Photo Strip (previously outside of the canvas) into a different layer from the Collage (previously inside the canvas, then you can have two layers and use toJSON on only one of those layers!
The result will be that toJSON will only serialize the objects from that given layer, which sounds like what you need.
jsfiddle - here is an example to illustrate what I mean, excuse my poor logic for snapping the image to the group. I have 2 layers: photoStripLayer and collageLayer
Click on the Collage toJSON button. Notice the console.log output does not include any of the images. This is because collageLayer only has a group inside it that has a child rectangle.
Drag the first yoda (top left, the rest don't work this is just an example) into the red box. Sorry you'll have to play around with this to get it to snap properly (I'm assuming you have your "snap" code already)
On dragend if the Yoda node is inside the red box, it will use KineticJS' moveTo function to move the yoda from the photoStripLayer --> collageLayer. You'll know it worked when the Yoda snaps to position (0,0) relative to the new group.
Now Click on the Collage toJSON button. Notice that the yoda image is now a part of the toJSON console.log output. The yoda was moved to the new group, which is a child of collageLayer. Now part of the collageLayer, Yoda is.
Here's the dragend code:
node.on('dragend', function () {
var pos = stage.getMousePosition();
var X = pos.x;
var Y = pos.y;
var minX = snap.getX();
var maxX = snap.getX() + snap.getWidth();
var minY = snap.getY();
var maxY = snap.getY() + snap.getHeight();
if (node.getX() < minX) {
node.moveTo(snap);
node.setX(0);
node.setY(0);
}
if (node.getX() > maxX) {
node.moveTo(snap);
node.setX(0);
node.setY(0);
}
if (node.getY() < minY) {
node.moveTo(snap);
node.setX(0);
node.setY(0);
}
if (node.getY() > maxY) {
node.moveTo(snap);
node.setX(0);
node.setY(0);
}
photoStripLayer.draw();
collageLayer.draw();
});
And the button click code to illustrate using toJSON:
function collageToJSON() {
var cjson = collageLayer.toJSON();
console.log(cjson);
/* To illustrate, you can also call toDataURL here. But in JSFiddle I think it throws a Security Error.
collageLayer.toDataURL({
callback: function(dataUrl) {
window.open(dataUrl);
}
});
*/
}
document.getElementById('CtoJSON').addEventListener('click', function () {
collageToJSON();
});
And there you have it! The issue is solved by letting KineticJS handle the entire process.

Related

Using MouseMove function to move elements in svg with svelte framework

Apologies if this is a dumb question, as I'm new to web development, and many thanks in advance!
I'm currently using a svg inside svelte framework. I defined my svg as <svg width={svg_width} height={svg_height}>. The whole structure looks like
<div class="demo-container">
|-<div id="playground-container">
|-<svg>
Here is my css:
.demo-container {
display:inline-block;
}
#playground-container {
position: relative;
width: 75%;
float: left;
margin-right:5px;
}
I'm having trouble relating the coordinate in svg (e.g. the location of shapes in the svg) to the mouse event (event.ClientX & event.ClientY). They do not seem to have linear or affine relationship. Additionally, when I inspect the webpage, the dimension of the svg displayed does not match what I defined it to be.
As a result, when I set the location of shapes directly to event.ClientX & event.ClientY, they go nuts. How should I convert the location of the mouse to location of the svg?
Please provide some help. Thanks
Assuming your mousemove event handler is attached to the svg element, the way to get x and y coordinates in reference to the svg itself (with the top left corner of the svg having coordinates (0,0) and the bottom right corner having the coordinates (svg_width, svg_height)) is to use getBoundingClientRect:
<script>
function mouseHandler(e) {
const rect = e.currentTarget.getBoundingClientRect()
console.log(`x: ${e.clientX - rect.x}, y: ${e.clientY - rect.y}`)
}
</script>
<div class="demo-container">
<div id="playground-container">
<svg width={svg_width} height={svg_height} on:mousemove={mouseHandler}>
// svg data (shapes, paths, etc.)
</svg>
</div>
</div>
You need to place a on:mousemove={fixElement} in your html-tag.
a function like this might work as you discribe:
function fixElement(event) {
console.log("in");
let elem = document.querySelector('#playground-container');
let y = event.clientY;
let x = event.clientX;
elem.style.setProperty('position', 'absolute');
elem.style.setProperty('left', x + 'px');
elem.style.setProperty('top', y + 'px');
}

How could we associate a medical image's segment position, to the click's event position in JS?

Hello and thank you for reading me!
This question is heavily related to this library: https://github.com/FNNDSC/ami
I would like to create a function to detect if you have pressed in a image's segment, to be able to display a dialog with a message that informs about it.
By myself, and looking for examples in StackOverflow, I have made the basics to detect if you have pressed and where, and by a condition distinguish if you clicked near a segment or not:
window.addEventListener('click', onWindowClick, false);
function onWindowClick(event) {
if (event.clientX > 200 && event.clientX < 300) {
alert('You have clicked: ' + event.clientX + " " + event.clientY + ' on the segment');
} else {
alert('You have clicked: ' + event.clientX + " " + event.clientY + ' out of the segment');
}
An example output, if you click near the green region:
If you click outside:
The issue is: what data/position/resource do I have to be able to compare the current clicked position and assert that you clicked in or out?
Well, by myself I have examined the data structure, and it looks like there are two options. First, we could use the frame's position, which looks like it represent the segmentation's spatial position, and it looks like:
Where clicked pos is: 277 294
and _imagePosition from data structure is: -201 -59
Also, another alternative is the stack's origin:
Here the clicked pos is: 278 301
and the origin is: -201 -59
So then it looks like whether I choose _imagePosition or origin I would need to do some conversion.
I do have studied that are used an ijk and lps coordinate systems, and in the lessons and examples is common to see a matrix which is updated to convert between them.
Do you have any suggestions or help?
Thank you!
Not quite sure what you mean by "image segment" but I assume you mean you are trying to see if the user clicked or not on the image.
You should use the THREEJS Raycaster (https://threejs.org/docs/index.html#api/core/Raycaster) and see if it intersects the "slice mesh".
That is what we do in the quadview example when you double click in one of the 2D viewers: https://fnndsc.github.io/ami/#viewers_quadview
Relevant code:
function onDoubleClick(event) {
const canvas = event.target.parentElement;
const mouse = {
x: ((event.clientX - canvas.offsetLeft) / canvas.clientWidth) * 2 - 1,
y: - ((event.clientY - canvas.offsetTop) / canvas.clientHeight) * 2 + 1,
};
...
const raycaster = new THREE.Raycaster();
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const lpsCoordinates = intersects[0].point;
const ijkCoordinates = CoreUtils.worldToData(lps);
}
Source code:https://github.com/FNNDSC/ami/blob/dev/examples/viewers_quadview/viewers_quadview.js#L564-L617

Gravity Points not working properly with other elements

There's this awesome codepen called Gravity Points which I liked very much and wanted to use on my next project but it turns out it only works well if it is the only element on the page. Once you start adding content above and below it, it miscalculates the mouse position.
Take a look at my fork here, I've added the content above it. Notice if the canvas is aligned perfectly with the screen, the gravity points are created in the right spot but if you click on the canvas when you're half way scrolled up, the points are created a few pixels down.
I'm not great with javascript and jquery, although I'm able to understand which functions it's calling and which functions are being used to draw the points but I can't understand where the calculations are happening and how it's related to scroll position. This functions seems to be triggered when left clicked but where does the cursor coordinates come from?
function mouseDown(e) {
for (var i = gravities.length - 1; i >= 0; i--) {
if (gravities[i].isMouseOver) {
gravities[i].startDrag(mouse);
return;
}
}
gravities.push(new GravityPoint(e.clientX, e.clientY, G_POINT_RADIUS, {
particles: particles,
gravities: gravities
}));
}
So can someone take a look at it and give some insights?
<canvas> element has its own coordinate system, which differs from the document one (the one sent by mouseEvents).
What you need to do is to check canvas's bounding box and remove its offset to your mouseEvents coordinates :
canvas.onmousemove = function(mouseEvent){
var rect = canvas.getBoundingClientRect();
var x = mouseEvent.clientX - rect.left;
var y = mouseEvent.clientY - rect.top;
// doSomething with x and y
for (var i = gravities.length - 1; i >= 0; i--) {
if (gravities[i].isMouseOver) {
gravities[i].startDrag(mouse);
return;
}
}
gravities.push(new GravityPoint(x, y, G_POINT_RADIUS, {
particles: particles,
gravities: gravities
}));
}

mouseMove very laggy

I am trying to obtain the image effect that 99designs is obtaining when hovering a mouse over a design.. [99designs.ca] Logo design contest: Runningbug Needs Logo 220626
I am currently obtaining the position of the mouse on mousemove, then using that to move my popover <img>, and everything works fine, but it is very laggy.. and presumably its from so many calls being made.
To get the position of the mouse:
jQuery(document).ready(function(){
//$("#special").click(function(e){
$(".imgWrap").mousemove(function(e){
//$('#status2').html(e.pageX +', '+ e.pageY);
//alert(e.pageX + ', ' + e.pageY);
mouseX = e.pageX;
mouseY = e.pageY;
});
})
I'm not sure of another way I can do this.. any ideas?
On the full course of events is the following:
User mouses over an img tag.
I get the position of the mouse as per above.
The <img> tag also calls a js function which changes the position of an img tag to the position of the mouse.
Actually, you can check it here: pokemonsite
update: I see there is a bounty placed (thanks !). I'm a little busy at the moment and can't check all the other answers, but I'll make sure to check them asap
There are several ways to improve performance when using mousemove events.
Use backface-visibility: hidden on popover element to force hardware acceleration. Same thing can be achived with transform: translate3d(0,0,0) but that makes difficult to use CSS transform function (see point #2).
Use CSS transform function for absolute positioning to avoid repaints but keep popover element absolute or fixed positioned.
When setting inline CSS via JS use requestAnimationFrame to avoid unnecesary layout trashing.
(maybe, optionally) hide cursor when hovering and use popover element as position indicator.
Move everything you can from JS to CSS ie. :hover state can be used to toggle display of popover element.
I made demo example combining all things listed. There is still some latency between cursor position and popover image and none of example links in original question work so I can't compare against it but I hope someone finds this useful.
DEMO
<div id="imgHolder" class="imgHolder">
<img src="//placehold.it/200x200" alt="" />
</div>
<div id="bigImgHolder" class="imgHover">
<img src="//placehold.it/500x500" alt="" />
</div>
.imgHover {
display: none;
backface-visibility: hidden;
position: fixed;
top: 0; left: 0;
pointer-events: none;
}
.imgHolder:hover ~ .imgHover { display: block; }
// uncomment if it makes sense
//.imgHolder:hover { cursor: none; }
var posX, posY;
$('#imgHolder').mousemove(HoverImg);
function HoverImg(e) {
posX = e.pageX;
posY = e.pageY;
window.requestAnimationFrame(showBigImg);
}
function showBigImg() {
$('#bigImgHolder').css({'-webkit-transform': 'translateX(' + posX + 'px) translateY(' + posY + 'px)', 'transform': 'translateX(' + posX + 'px) translateY(' + posY + 'px)' });
}
references:
http://davidwalsh.name/translate3d
http://www.paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/
https://css-tricks.com/using-requestanimationframe/
Try this:
jQuery(document).ready(function(){
$(".imgWrap").mousemove(function(e){
e.stopPropagation();
mouseX = e.pageX;
mouseY = e.pageY;
});
})
Use e.offsetX and e.offsetY or (recommended) e.clientX and e.clientY instead of pageX and pageY. Maybe this will be a better solution. Note: offsetx and offsety do not work in Firefox as far as I know.
If the absolute (x, y) position is not so important (meaning: some pixel-values can be omitted without destroying your logic), you could try to skip some frames of your mousemove-event.
var globalSkipCounter = 0;
var globalSkipRate = 5;
$(".imgWrap").mousemove(function(e){
if(globalSkipCounter >= globalSkipRate){
var mouseX = e.pageX;
var mouseY = e.pageY;
do_stuff(mouseX, mouseY);
globalSkipCounter = 0;
}
else{
globalSkipCounter+=1;
}
});
This way, you omit redrawing your image on every mousemove-event, instead your draw-routines (do_stuff) are only invoked, once every 5 events.
Cache the position and wrap the update in an if(oldpos !== newpos) type check (remembering to update oldpos inside it).
Use requestAnimationFrame to handle the update - if you have a normal function and pass that as the callback then it will only get called once per frame (ie, don't use an anonymous function).
Finally make use of transform:translate(x,y) to set the position and make better use of the GPU etc. Related to this, there's no harm in making use of the css will-change keyword if you want to use top/left instead.
try a maximum event per second based approach:
jQuery(document).ready(function(){
var now, then, delta, interval = 1000/60; //maximum 60 eps, you can change
//$("#special").click(function(e){
$(".imgWrap").mousemove(function(e){
now = Date.now();
delta = now - then;
if (delta > interval) {
then = now - delta % interval; //subtract extra waited time
//$('#status2').html(e.pageX +', '+ e.pageY);
//alert(e.pageX + ', ' + e.pageY);
mouseX = e.pageX;
mouseY = e.pageY;
// do your thing
}
});
})
EDIT: didn't know then is a reserved word in javascript, you can rename it.
I have a sortable listview in angular, but dragging items with the mouse in chrome was lagging a lot. I tried disabling all chrome extensions and now the lag is totally gone.
It turned out that TamperMonkey was causing it.
Instead of using "left" or "right" property of CSS rather try to use
"transform" I was trying to do the same thing as asked in question, and using "transform" worked for me like a charm.
for example:
transform: translate(30px);
this will move the element, 30px to the right.
by using transform we can move elements to left, right, top and bottom according to the need.

jquery set mouse position inside a <div> graphic area

using http://www.flotcharts.org/ to realize a complex scatterplot graphic with zoom/pan functionalities, i would like to set mouse position at specific coordinates (by example: when doing a zoom in, I would like that cursor would positioned at the center of canvass).
It ssem this function doesn't work:
function setMousePosition(pos) {
var x = pos.x;
var y = pos.y;
var pointX = ( Math.floor(plot.offset().left + plot.p2c(pos).left) );
var pointY = ( Math.floor(plot.offset().top + plot.p2c(pos).top) );
$("#placeholder").mouseXPos( pointX ).`mouseYPos`( pointY );
About mouseXPos and mouseYPos, firebug say:
TypeError: placeholder.mouseXPos is not a function
So my question is: there is a way in javascript or jquery to set mouse coordinates inside a graphic area ?
The mouse position cannot be set/changed via JavaScript.
There is no any mechanism to place a cusror to specific position programmatically in Javascript

Categories