Move svg element via Drag and Arrow Keys - javascript

I am currently working on a project where I am placing an svg text element wrapped in a group on a template. I need the user to both drag the element around (which I have working) and move SELECTED element via arrow keys. The code below is what I have been working on for a while now and the element seems to jump right and down by several pixels at a time. Something is just not right. Can anyone help me with this? Thanks!
function drag(r) {
r.drag(move, start, stop);
}
var move = function() {
this.attr({style:'cursor: grabbing;'});
}
var start = function() {
}
var stop = function() {
this.attr({style:'cursor: pointer;'});
xyc(this);
}
function xyc(svg){
var bb = svg.getBBox();
console.log('New: x: ' + bb.x + ' y: '+ bb.y);
keymove(svg,bb.x,bb.y);
}
function keymove(g,bx,by) {
console.log('New: x: ' + bx + ' y: '+ by);
$(document).keydown(function(e){
e.preventDefault();
if(e.keyCode == 39) {//right
g.attr('kx', bx+=1);
g.attr('ky', by);
g.attr({transform:'translate('+ g.attr('kx') + ',' + g.attr('ky') + ')'});
xyc(g);
}else if(e.keyCode == 37) {//left
g.attr('kx', bx-=1);
g.attr('ky', by);
g.attr({transform:'translate('+ g.attr('kx') + ',' + g.attr('ky') + ')'});
xyc(g);
}
else{
return;
}
});
}

Related

OpenLayers 4 - LayerFilter on forEachFeatureAtPixel

I have 2 vector layers of which I want only 1 to be selectable for a WFS get feature info layer. OL4 docs tell me there is an opt_layerfilter for the forEachFeatuerAtPixel function.
I’m in a similar situation like this: OpenLayers 3 hasFeatureAtPixel filter for layer.
Due to my lack of JavaScript knowledge I can’t seem to make it work with the following code in OpenLayers 4:
var displayFeatureInfo = function (pixel) {
var features = [];
map.forEachFeatureAtPixel(pixel, {
layerFilter: function (layer) {
return layer.get('name') === 'isochrones';
}
}, function (feature) {
features.push(feature);
});
if (features.length > 0) {
var info = [];
var i, ii;
for (i = 0, ii = features.length; i < ii; ++i) {
info.push('<div id="infobox">' + '<p2>' + 'Isochroon ' + features[i].get('name') + ', locatie ' + features[i].get('facilityid') + '</p2>' + '<p>' + 'aantal lopend: ' + features[i].get('n_pedestrians') + ', fiets: ' + features[i].get('n_bike') + ', ebike: ' + features[i].get('n_ebike') + '<br>' + 'speedpedelec: ' + features[i].get('n_speedpedelec') + ', auto: ' + features[i].get('n_car') + '</p>' + '</div>');
}
document.getElementById('info').innerHTML = info.join(', ') || '&nbsp';
} else {
document.getElementById('info').innerHTML = ' ';
}
};
map.on('click', function (evt) {
displayFeatureInfo(evt.pixel);
});
The layer I want to be selectable is named ‘isochrones’.
It throws me an error “d.call is not a function” when I try to click any vector layer in the map.
Could anyone point me in the right direction?
Looks like you have your args swapped.
The params for forEachFeatureAtPixel are (pixel, callback, options)
You have (pixel, options, callback)

HTML5 Drag-Drop: SetDragImage not working

I'm new to front-end programming and experimenting with HTML5 drag-drop. The object to drag is a bootstrap div.panel which does its job pretty well. However, whilst the object is being dragged, I want to show a custom image using SetDragImage function. I've used this function precisely as described in the Mozilla Developer Network, but still its not working - the default blank rectangle is shown when the object is dragged.
Below is the JavaScript for the drag/drop events, and here is the JSFiddle.
<script type="text/javascript">
function drag(ev){
//var style = window.getComputedStyle(ev.target, null);
var ss = (parseInt($(ev.target).position().left,10) - ev.clientX) + ',' + (parseInt($(ev.target).position().top,10) - ev.clientY);
ev.dataTransfer.setData("text/plain", ss);
ev.dataTransfer.setDragImage(document.getElementById("draggit"), 0, 0);
console.log("drag:target", $(ev.target).position().left + "," + $(ev.target).position().top);
console.log("drag:offset", ss);
}
function drop(ev) {
//console.log("drop:" + $(ev.target).position().left + "," + $(ev.target).position().top);
//console.log("drop:" + ev.clientX + "," + ev.clientY);
var offset = ev.dataTransfer.getData("text/plain");
var npos = offset.split(",");
console.log("drop_clientpos:" + ev.clientX + "," + ev.clientY);
console.log("drop_newpos:" + (ev.clientX + parseInt(npos[0])) + "," + (ev.clientY + parseInt(npos[1])));
document.getElementById("dragme").style.left = (ev.clientX + parseInt(npos[0])) + "px";
document.getElementById("dragme").style.top = (ev.clientY + parseInt(npos[1])) + "px";
ev.preventDefault();
return false;
}
function dragOver(ev) {
ev.preventDefault();
return false;
}
</script>

.classname element needs mousemove event even when the cursor is not on top of them. How to get event to elements?

Is there a way to get the mousemove event when the cursor is not on top of the element itself?
This is what I currently have:
$(document).on('mousemove', '.className', OnMouseMove);
function OnMouseMove(e) {
var clientCoords = "( " + e.clientX + ", " + e.clientY + " )";
var pageCoords = "( " + e.pageX + ", " + e.pageY + " )";
var offsetCoords = "( " + e.offsetX + ", " + e.offsetY + " )";
console.log('clientCoords: '+clientCoords+', pageCoords: '+pageCoords+', offsetCoords: '+offsetCoords);
}
I have a lot of elements using the .className that I will be adding animations to but I need the mouse coordinates even when the cursor is not on top of the elements. How can I do this?
$(document).on('mousemove', OnMouseMove);
That will get you the event based on the document.
That's the only way to get it no matter where it is.
You can then check to see if it's over a specific element, maybe with a hover on the .classname elements to toggle a booleaen or something.
Like
var isOverElement = false;
$(document).on('mouseenter', '.classname', function(e){
isOverElement = true;
}).on('mouseleave', '.classname', function(e){
isOverElement = false;
});
HTH,
-Ted
EDITING to add additional code samples requested from comments...
To add elements to the cachedElems array, you need to add some code to whatever code is adding them to the page, or redefine the array.
The array would need to be declared globally, or at least in a context each function can get to it.
Such as
<script>
var cachedElems;
$(document).ready(....
Then in whatever function is adding the elements, or loading a page with those elements, on load complete, define the array, like:
cachedElems = $('.className');
Just iterate over the elements in the mouse move function.
$(function(){
$(document).on( "mousemove", MouseMove );
var cachedElems = $( '.tracker' );
function MouseMove(e){
var pageX = e.pageX,
pageY = e.pageY;
$.each(cachedElems, function(i,elem){
elem = $(elem);
var elemX = pageX - elem.offset().left,
elemY = pageY - elem.offset().top;
$(elem).text(
'page x: ' + pageX + '\n' +
'page y: ' + pageY + '\n' +
'\n' +
'elem x: ' + elemX + '\n' +
'elem y: ' + elemY + '\n'
);
});
}
});
http://jsfiddle.net/f1Lzqxom/

KineticJS Setting a Variable Value On an Event

I define several kineticJS circles in an object and create and display several of those objects. What I want is to generate an XML object based on the ones the user selects out of the displayed circles.
For this I tried to set a variable value defined in the object when clicked on a particular circle and call it by an object.property kind of a way. However, this doesn't work.
Following is some of the code I used for the job:
function Graph(i, j, refRel, refRelTxt) {
subNodes = seventeen + eighteen + nineteen;
graphNode = "<" + refRelTxt + ">" + subNodes + "<" + refRelTxt + ">";
var layer = new Kinetic.Layer({
width: 200,
height: 100,
x: (stage.getWidth() / 2.5) + i,
y: (stage.getHeight() / 2.5) + j
});
var circle19 = new Kinetic.Circle({
x: 10,
y: layer.getHeight() / 2,
radius: cradius,
fill: '#CCCCCC'
});
//++++++++++++++++++++++++++++++++++++++++++++++++++
var circle18 = new Kinetic.Circle({
x: 10 + n,
y: (layer.getHeight() / 2) - m,
radius: cradius,
fill: '#CCCCCC'
});
var circle17 = new Kinetic.Circle({
x: 10 + n,
y: (layer.getHeight() / 2) + m,
radius: cradius,
fill: '#CCCCCC'
});
//============Mouse in/out operations
circle17.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
seventeen = "<node>seventeen<node>";
});
circle18.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
eighteen = "<node>eighteen<node>";
});
circle19.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
nineteen = "<node>nineteen<node>";;
});
// add the shape to the layer
layer.add(circle19);
layer.add(circle18);
layer.add(circle17);
stage.add(layer);
}
Create some graph objects like the following:
var graph19 = new Graph(-(4*hdist), (0*vdist),"circle19","nineteen");
And then call the graph19.graphNode value to get the xml out based on the circles the user has clicked. However, graph19.graphNode doesn't get set with the values dynamically. I know I am doing something wrong here that I cannot figure out. When I set global variables and do this it works. What I want is to extract the xmls form the various Graph objects that are created.
If I understand correctly... you want to be able to call graph19.graphNode and it will output some pre-defined xml string that is dynamically populated depending on which circles the user clicked.
If so:
First off, to be able to call the property graphNode from a new Graph object, you have to assign this.graphNode instead of just graphNode by itself. Otherwise when you call graph19.graphNode it will be undefined.
Then you need to assign this to a variable like: var node = this; so that you can access node within the click function. (This is because by default, when you use this inside a Kinetic event function, click in this case, this will be assigned to the Kinetic.Shape you are binding the event to)
Finally, you need to update node (this.graphNode) every time you click a circle, use an array to keep track of the elements selected:
this.graphNode = '';
var node = this;
var nodeText = '';
circle17.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
seventeen = "<node>seventeen<node>";
nodeText += seventeen;
node.graphNode = "<" + refRelTxt + ">" + nodeText + "<" + refRelTxt + ">";
});
circle18.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
eighteen = "<node>eighteen<node>";
nodeText += eighteen;
node.graphNode = "<" + refRelTxt + ">" + nodeText + "<" + refRelTxt + ">";
});
circle19.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
nineteen = "<node>nineteen<node>";
nodeText += nineteen;
node.graphNode = "<" + refRelTxt + ">" + nodeText + "<" + refRelTxt + ">";
});
The reason your code wasn't working was because you've defined
subNodes = seventeen + eighteen + nineteen;
Before seventeen eighteen and nineteen have even been defined (since they are defined when the user clicks on the the respective Kinetic.Circle
Also, as a suggestion I recommend that instead of creating a new Kinetic.Layer and adding it to the stage every time you call your Graph function, you might want to create a new Kinetic.Group instead and add that to the layer. See here for more information: What are the differences between group and layer in KineticJs
UPDATE
Okay if you need to deselect the objects as well, we'll need to keep track of which objects are selected in an array. Then we can use that array to determine how this.graphNode will be populated.
Something like this (note I haven't tested this code..):
this.graphNode = '';
this.selectedNodes = [];
this.nodeText = '';
var node = this;
circle17.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
seventeen = "<node>seventeen<node>";
selectedNodes.push(seventeen);
node.nodeText = '';
for (var i=0; i<selectedNodes.length; i++) {
node.nodeText += selectedNodes[i];
}
node.graphNode = "<" + refRelTxt + ">" + node.nodeText + "<" + refRelTxt + ">";
});
circle18.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
eighteen = "<node>eighteen<node>";
selectedNodes.push(eighteen);
node.nodeText = '';
for (var i=0; i<selectedNodes.length; i++) {
node.nodeText += selectedNodes[i];
}
node.graphNode = "<" + refRelTxt + ">" + node.nodeText + "<" + refRelTxt + ">";
});
circle19.on('click', function (evt) {
this.setFill('#00FF00');
layer.draw();
nineteen = "<node>nineteen<node>";
selectedNodes.push(nineteen);
node.nodeText = '';
for (var i=0; i<selectedNodes.length; i++) {
node.nodeText += selectedNodes[i];
}
node.graphNode = "<" + refRelTxt + ">" + node.nodeText + "<" + refRelTxt + ">";
});
After this you can do something similar to remove the object from the selectedNodes array on dblclick event.

using touchstart causes screen to become fuzzy on touchstart

I am currently testing touchstart functionality on my device (Samsung galaxy S2) for my game. I am programming using javascript and jquery wrapped under phonegap in android and currently having issues as follows:
my touch start event (e.g triggering an attack button "touchstart" event to run some javascript to perform the attack action) causes my screen to become temporarily fuzzy, then back to normal in less than a second, so more like a screen flicker where images become jittery). I am not using css transforms or transitions just plain css and images.
Can someone please let me know if they have encountered moreorless similar issues to mine. A bit at a loss whether it is a hardware or touchstart issue where i can solve that problem.
Sample Javascript below for my navigation controls (left, up, down, right touchstart tap):
if ('ontouchstart' in document.documentElement) {
var left = document.getElementById('left');
left.addEventListener("touchstart", function(e){
if(controlsPlayerChar == '')
{
return false;
}
var l_oldCell = $('#' + controlsPlayerChar).parent().attr('id');
var l_xy = l_oldCell.split('_');
var l_x = l_xy[0];
var l_y = l_xy[1];
if(l_y == 1)
{
direction = "left";
setCharDynamics(controlsPlayerChar);
return false;
}
var l_newCell = l_x + '_' + (parseInt(l_y) - 1);
// validate if next cell is empty
if($('#' + l_newCell + ':has(".shadow")').val() != undefined
|| $('#' + l_newCell + ':has(".ally")').val() != undefined
|| $('#' + l_newCell + ':has(".obstacle")').val() != undefined)
{
direction = "left";
setCharDynamics(controlsPlayerChar);
return false;
}
$('#' + l_newCell).append($('#' + controlsPlayerChar));
$('#' + l_oldCell + ' ' + '#' + controlsPlayerChar).remove();
// set char direction to 'left' and set next footstep
setDirection('left');
setFootstep(footstep);
setCharDynamics(controlsPlayerChar);
});
var up = document.getElementById('up');
up.addEventListener("touchstart", function(e){
if(controlsPlayerChar == '')
{
return false;
}
var u_oldCell = $('#' + controlsPlayerChar).parent().attr('id');
var u_xy = u_oldCell.split('_');
var u_x = u_xy[0];
var u_y = u_xy[1];
if(u_x == 1)
{
direction = "up";
setCharDynamics(controlsPlayerChar);
return false;
}
var u_newCell = (parseInt(u_x) - 1) + '_' + u_y;
// validate if next cell is empty
if($('#' + u_newCell + ':has(".shadow")').val() != undefined
|| $('#' + u_newCell + ':has(".ally")').val() != undefined
|| $('#' + u_newCell + ':has(".obstacle")').val() != undefined)
{
direction = "up";
setCharDynamics(controlsPlayerChar);
return false;
}
$('#' + u_newCell).append($('#' + controlsPlayerChar));
$('#' + u_oldCell + ' ' + '#' + controlsPlayerChar).remove();
// set char direction to 'up' and set next footstep
setDirection('up');
setFootstep(footstep);
setCharDynamics(controlsPlayerChar);
});
var down = document.getElementById('down');
down.addEventListener("touchstart", function(e){
if(controlsPlayerChar == '')
{
return false;
}
var d_oldCell = $('#' + controlsPlayerChar).parent().attr('id');
var d_xy = d_oldCell.split('_');
var d_x = d_xy[0];
var d_y = d_xy[1];
if(d_x == rows)
{
direction = "down";
setCharDynamics(controlsPlayerChar);
return false;
}
var d_newCell = (parseInt(d_x) + 1) + '_' + d_y;
// validate if next cell is empty
if($('#' + d_newCell + ':has(".shadow")').val() != undefined
|| $('#' + d_newCell + ':has(".ally")').val() != undefined
|| $('#' + d_newCell + ':has(".obstacle")').val() != undefined)
{
direction = "down";
setCharDynamics(controlsPlayerChar);
return false;
}
$('#' + d_newCell).append($('#' + controlsPlayerChar));
$('#' + d_oldCell + ' ' + '#' + controlsPlayerChar).remove();
// set char direction to 'down' and set next footstep
setDirection('down');
setFootstep(footstep);
setCharDynamics(controlsPlayerChar);
});
var right = document.getElementById('right');
right.addEventListener("touchstart", function(e){
if(controlsPlayerChar == '')
{
return false;
}
var r_oldCell = $('#' + controlsPlayerChar).parent().attr('id');
var r_xy = r_oldCell.split('_');
var r_x = r_xy[0];
var r_y = r_xy[1];
if(r_y == cols)
{
direction = "right";
setCharDynamics(controlsPlayerChar);
return false;
}
var r_newCell = r_x + '_' + (parseInt(r_y) + 1);
// validate if next cell is empty
if($('#' + r_newCell + ':has(".shadow")').val() != undefined
|| $('#' + r_newCell + ':has(".ally")').val() != undefined
|| $('#' + r_newCell + ':has(".obstacle")').val() != undefined)
{
direction = "right";
setCharDynamics(controlsPlayerChar);
return false;
}
$('#' + r_newCell).append($('#' + controlsPlayerChar));
$('#' + r_oldCell + ' ' + '#' + controlsPlayerChar).remove();
// set char direction to 'right' and set next footstep
setDirection('right');
setFootstep(footstep);
setCharDynamics(controlsPlayerChar);
});
}
Please let me know if you think anything is amiss with regards to above script. The way I add the touchstart event is the same in other areas of my script when e.g to launch an attack or launch an options menu for instance.
Seems that this is tap highlighting.
You can try to disable this effect applying -webkit-tap-highlight-color CSS property on your controls or disable this in all elements using * selector.
For example:
.someelement {
-webkit-tap-highlight-color: transparent;
}
We've ran into this issue when using translate3d transformations.
We fixed it by setting
* { -webkit-transform: translate3d(0,0,0,); }
so that every element is initialized for the 3d space
First of all, make sure you are calling preventDefault() on the event. I've noticed that if you are targeting mouse events as well, they can fire on touch. Otherwise, I use a slightly different method of disabling touch highlighting. Try:
-webkit-tap-highlight-color: rgba(0,0,0,0);
In the css for your button.

Categories

Resources