Using MouseMove function to move elements in svg with svelte framework - javascript

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');
}

Related

How to "click through" fixed overlay div, without disabling its pointer events

I have a use case where I need to overlay fixed div over canvas element. This div is used to listen for scroll event and then transfer that data to canvas it is covering. Simplified html for these could look like following:
<canvas style="width: 100vw; height: 100vh;"></canvas>
<div style="position: fixed; width: 100vw; height: 100vh; top: 0; left: 0;"></div>
I ran across an issue. My canvas is also interactive, it has button elements and other stuff like that, unfortunately I had to implement scrolling behaviour this way for better performance and to keep default browser behaviours like "bounce scroll" on ios.
Is it at all possible to have this approach but also translate all mouse / touch event from the div to canvas behind it?
okay, so you cannot really pass a click event programmatically and have x and y coordimates (as seen in the fiddle I attached).
What you can do is define global vars that store x and y coords on click of div. and then trigger click on canvas like this:
var div = document.querySelector('#theDiv')
var canvas = document.querySelector('#theCanvas')
var x = 0;
var y = 0;
div.addEventListener('click', function(event) {
console.log('div clicked', event); // event.clientX and event.clientY have correct values
x = event.clientX;
y = event.clientY;
canvas.click();
});
canvas.addEventListener('click', function(event) {
console.log('canvas clicked', x, y, event); // event.clientX and event.clientY are both 0
})
Here is the fiddle, where you can see it in action (I have added some styles to differentiate between the div and canvas)
https://jsfiddle.net/m1n4xave/10/

Position mouse pointer on line/path in Raphael

In this jsFiddle I have a Raphael canvas with a line/path where the user can drag it.
Problem is that the exact top pixel of the mouse pointer has to be on the line to be able to drag it. I need Raphael to be more "forgiving" and let the user drag the line if it touches the top, say, 3 pixels of the pointer.
I added mouseover/mouseout events to actually tell when the mouse is over the line, otherwise is very difficult to know.
So the question is: is there a way to drag the line without having to accurately position the mouse pointer?
var paper = Raphael("canvas", 600, 600);
var line = this.paper.path('M10 10 L50 50');
var start = function () {
this.odx = 0;
this.ody = 0;
},
move = function (dx, dy) {
this.translate(dx - this.odx, dy - this.ody);
this.odx = dx;
this.ody = dy;
},
up = function () {};
line.drag(move, start, up);
line.mouseover(function(e) {
line.attr('stroke', 'red');
});
line.mouseout(function(e) {
line.attr('stroke', 'black');
});
The Raphael library creates an SVG element (not a CANVAS element) for drawing purposes, and drawing lines creates PATH elements within the SVG element.
I had some success by increasing the line width on hover. You do have to trigger the width change by at least moving the mouse over the line, but it is a lot easier to select afterwards. While the visual effect produced may not be perfect (some width flicking can occur) continuing the drag operation proceeds without problem.
You could try increasing the line width by either modifying the stroke-width attribute in the mouseover and mouseout handlers:
line.mouseover(function(e) {
line.attr('stroke', 'red');
line.attr('stroke-width', '9')
});
line.mouseout(function(e) {
line.attr('stroke', 'black');
line.attr('stroke-width', '1')
});
or by modifying the stroke-width styling when hovering over all path elements in CSS using
path:hover {
stroke-width: 9;
}
According to this question Raphael does not provide an abstraction of all possible CSS styling so it could use VML in XML documents. If you can live without IE 9 support and expect users to have a modern browser you may be interested in the following snippet which
increases the width of a line on mouseover by setting class name draggable, which sets a move cursor,
when a move is started replaces draggable with dragging ( which removes the width increase) and sets a move cursor over the svg container,
removes class draggable, but not dragging, on mouseout
cleans up classes and the cursor in the up function.
"use strict";
var container = document.getElementById("canvas");
var paper = Raphael("canvas", 600, 600);
var line = this.paper.path('M10 10 L50 50');
var start = function () {
this.odx = 0;
this.ody = 0;
},
move = function (dx, dy) {
this.translate(dx - this.odx, dy - this.ody);
this.odx = dx;
this.ody = dy;
if( this.node.classList.contains("draggable")) {
this.node.classList.replace("draggable", "dragging");
container.classList.add("moveCursor");
}
},
up = function (e) {
line.node.classList.remove("draggable", "dragging");
container.classList.remove("moveCursor");
};
line.drag(move, start, up);
line.mouseover(function(e) {
if(!e.target.classList.contains("dragging")) {
e.target.setAttribute("class", "draggable");
}
});
.draggable:hover {
stroke-width: 9;
stroke:red;
cursor: move;
}
path.dragging {
stroke: red;
}
.moveCursor{
cursor:move;
}
svg {
border: thin solid blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<!-- body-html -->
<div id="canvas"></div>
The code is basically for demonstration - if you drag a line outside the SVG element you can't see it or grab it to drag back.
Some CSS rules (i.e. path.dragging) needed additional specificity to take priority over the defaults supplied by Raphael.

Interspersing KineticJS and jQuery events

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.

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