I'm doing my first project in kineticJS; I have to drag some shapes on the canvas and in the meanwhile, I have to write somewhere on the page the coordinates of the shape in real time. In order to do this I use the event "dragmove" on the layer like this:
layer.on("dragmove",function(evt) {//detect shape and write stuff});
I used "targetNode" for catching the clicked shape.
The problem is this: if I use targetNode on a shape that is not part of any group, everything works, but if the node that has to be moved is part of a group, targetNode only works the first time and then return an undefined value for all times following (dragmove usually takes some time, not like dragstart or click).
I made a fiddle so you can see what I'm talking about:
http://jsfiddle.net/UScmU/
The 2 rects are in a group. if you click on a shape, an alert return its name, if you drag the circle or the rects, on the console will be written the shape's name.
I would appreciate some help. sorry for my english, I also hope the text is understandable.
For some reason evt.targetNode is only defined the first call on dragmove then it turns undefined, you can see this by doing this:
layer.on("dragmove",function(evt) {
console.log(evt.targetNode);
console.log(evt.targetNode.getName()+"");
});
Instead, try using defining var nodo outside of the event functions, and then set nodo = evt.targetNode on mousedown.
var nodo;
layer.on('mousedown', function(evt) {
nodo = evt.targetNode;
});
layer.on("click", function (evt) {
console.log(nodo.getName() + "");
});
layer.on("dragmove", function (evt) {
console.log(nodo.getName() + "");
});
JSFIDDLE
Related
NOTE: We need go with Viewbox concept ONLY !
I have a div(resize-div) in which I render SVG-polygon. I should be able to rearrange polygon points (by dragging and dropping them) or I can resize the resize-div.
Scenario:1 : I should be able to re-arrange polygon points by dragging them anywhere and resize-div(parent-div) will be rearranged/resized to accommodate all points within it. Which is working fine.
Scenario-2: Once I'm done with rearranging the polygon points, If I try to resize the resize-div(parent-div), the entire svg or some points flicker. OR in other word, after re-arranging points, If I double tap the resize-div(parent-div), the entire shape flickers.
In scenario-2, I use
arrVertexes = arrVertexes.map(item => {
return [(xScale.invert(item[0])), (yScale.invert(item[1])) ];
});
but for scenario-1 I don't use scale.invert.
The expected behavior is it should not flicker. It should work as is.
Been trying since last 2 weeks but no luck. Actual application is different. This is just a reproduction of what is happening in real-app.
DEMO - APP
function resize(){
arrVertexes = arrVertexes.map(item => {
return [(xScale.invert(item[0])), (yScale.invert(item[1])) ];
});
const eve = $('.resize-div').style;
eveWidth = $('.resize-div').width() - 2;
eveHeight = $('.resize-div').height() - 2;
if (width !== eveWidth) {
width = eveWidth;
}
if (height !== eveHeight) {
height = eveHeight;
}
getScaledPoints();
reload();
}
Not sure what is wrong. Tried to set preserveAspectRation(which I don't need) to none but it didn't work.
You code is a mess... I hardly understand what is going on there.
But one thing to mention. This code in getScaledPoints function:
return [Math.round(xScale(item[0])), Math.round(yScale(item[1]))];
Should be changed to:
return [xScale(item[0]), yScale(item[1])];
Do not make any rounding here! When you are working with "scaling", you should go into "real" numbers, not "integers". Example: click.
I made a maze application with a route finder. As it travels along the route, it makes a call to draw the canvas, highlighting each cell en route. However what ends up happening is it blocks until it has completely finished and then draws to the canvas. Roughly like so:
Path.prototype.travelTo (cell) {
this.previous = this.cell;
this.cell = cell;
this.cell.fill = 'yellow';
this.maze.draw();
var choice = findChoice();
this.travelTo(choice);
};
The maze class keeps track of all cells and its draw function loops through each cell, calling the cells own draw function.
I made a quick jsfiddle (http://jsfiddle.net/amctammany/99VVu/) that emulates the problem.
Here it travels from one cell to the next by recursively calling itself via setTimeout. The result is the same, it instantly fills all cells, without even waiting for any amount of time.
Any help would be appreciated.
I had a look at your jsfiddle, and the first thing I noticed is your setTimeout:
window.setTimeout(fillNext(i + 1), 500);
But probably you meant to do this:
window.setTimeout(function() {fillNext(i + 1)}, 500);
You should set the fillNext(i+1) as a callback to setTimeout,
not use the return value of fillNext(i+1) as the argument of window.setTimeout()
Update the following line as given below.Then you can see the animation.
"window.setTimeout(fillNext(i + 1), 500);" To "window.setTimeout(fillNext, 100,(i + 1));"
I have updated the fiddle.You can check it now.
I have assigned a "mousemove" event on some div elements. Those elements might overlap each other due to an animation process, so several "mousemove" events could be called at once by moving the mouse over the overlapping parts.
The problem is that two triggered "mousemove" events can lead to conflicting decisions. Hence, I would like to make a decision based on ALL elements that are concerned by the "mousemove" event, when such an event occur for at least one of them.
My question is : do you know an efficient way to do it ?
Thanks !
If I understand the q correctly, you want to synchronize the execution of mousemove events for each div. There are hacky ways to do it, though the best would be to change your HTML markup, so you don't have overlaps.
Anyway, for your case, you could do the following:
var g_focusDivId = "";
function onMouseMove(e)
{
if (g_focusDivId != "" && g_focusDivId != e.target.id)
return; // Deciding to not exec any other mouse moves
g_focusDivId = e.target.id;
// Do your stuff
g_focusDivId = "";
}
This, of course, assumes that JS event handling is single-threaded, which is not always true: Is JavaScript guaranteed to be single-threaded?
The alternative is to do this (I have not tried this). I am using a queue to run the events in sequence on a single method. Much more controlled, but it may lead to some events getting processed late.
var g_syncEventQueue = new Array();
function onMouseEvent(e)
{
g_syncEventQueue.push(e);
}
function queueListenerProc()
{
if (g_syncEventQueue.size() > 0)
{
var evt = g_syncEventQueue[0];
g_syncEventQueue = g_syncEventQueue.splice(0, 1);
return queueListenerProc(); // Immediately process the next event
}
setTimeout("queueListenerProc()", 1000);
}
queueListenerProc(); // Not ideal because it keeps running without an exit condition.
I would like to trigger some functions according to a position of an element. This element's position changes every tenth second. There is two dozens functions to trigger.
I thought about this pseudo-code :
When element position changes{
Loop through all the coordinates to see if a function can be triggered{
if the current element position matches the function's triggering position
execute the function
}
}
But looping through all possible position each split seconds burdens the browser. So if there is a way to have events to do that.
Is it possible ?
Edit:
After Beetroot-Beetroot comment, I must say that the element that moves only moves on an X abscissa : so just one dimension.
It's much like a horizontal timeline moving from left to right, where some animation happen when a certain year is reached.
However the moving speed can be increased by the user, so fixed time to trigger animation is not an option.
There must be many ways to achieve what you want. The code below exploits jQuery's capability to handle custom events to provide a "loosely-coupled" observer pattern.
$(function() {
//Establish the two dozen functions that will be called.
var functionList = [
function() {...},
function() {...},
function() {...},
...
];
var gridParams = {offset:10, pitch:65};//Example grid parameters. Adjust as necessary.
//Establish a custom event and its handler.
var $myElement = $("#myID").data('lastIndex', -1).on('hasMoved', function() {
$element = $(this);
var pos = $element.position();//Position of the moved element relative to its offset parent.
var index = Math.floor((pos.left - gridParams.offset) / gridParams.pitch);//Example algorithm for converting pos.left to grid index.
if(index !== $element.data('lastIndex')) {//Has latest movement align the element with the next grid cell?
functionList[index](index, $element);//Call the selected function.
$element.data('lastIndex', index);//Remember index so it can be tested mext time.
}
});
});
$(function() {
//(Existing) function that moves the element must trigger the custom 'hasMoved' event after the postition has been changed.
function moveElement() {
...
...
...
myElement.trigger('hasMoved');//loosely coupled 'hasMoved' functionality.
}
var movementInterval = setInterval(moveElement, 100);
});
As you can see, an advantage of loose-coupling is that a function and the code that calls it can be in different scopes - .on('hasMoved', function() {...} and myElement.trigger('hasMoved') are in different $(function(){...}) structures.
If you wanted to add other functions to change the position of myElement (eg first, previous, next, last functions), then, after moving the element, they would each simply need to trigger 'hasMoved' to ensure that the appropriate one of your two dozen functions is called, without needing to worry about scopes.
The only thing you need to ensure is that your two dozen functions are scoped such that they can be called by the custom event handler (ie that they are in the same scope or an outer scope, up to and including the global scope).
I've had to make many assumptions, so the code above will not be 100% correct but hopefully it will provide you with a way ahead.
I have a rather interesting issue with SVG animation.
I am animating along a circular path using Raphael
obj = canvas.circle(x, y, size);
path = canvas.circlePath(x, y, radius);
path = canvas.path(path); //generate path from path value string
obj.animateAlong(path, rate, false);
The circlePath method is one I have created myself to generate the circle path in SVG path notation:
Raphael.fn.circlePath = function(x , y, r) {
var s = "M" + x + "," + (y-r) + "A"+r+","+r+",0,1,1,"+(x-0.1)+","+(y-r)+" z";
return s;
}
So far, so good - everything works. I have my object (obj) animating along the circular path.
BUT:
The animation only works if I create the object at the same X, Y coords as the path itself.
If I start the animation from any other coordinates (say, half-way along the path) the object animates in a circle of the correct radius, however it starts the animation from the object X,Y coordinates, rather than along the path as it is displayed visually.
Ideally I would like to be able to stop/start the animation - the same problem occurs on restart. When I stop then restart the animation, it animates in a circle starting from the stopped X,Y.
UPDATE
I created a page that demonstrates the issue:
http://infinity.heroku.com/star_systems/48eff2552eeec9fe56cb9420a2e0fc9a1d3d73fb/demo
Click "start" to start the animation.
When you stop and re-start the animation, it continues from the current circle coords in a circle of the correct dimensions.
The problem is that Raphael has no way of knowing that the circle is already part-way along the path. The "start" function means just that -- start an animation. imo it would be broken if it did anything else.
That said, your use case is a valid one, and might warrant another function -- a 'pause' of some sort. Of course, getting that into trunk would take longer probably than you want to wait.
From the Raphael source code, here's what happens when you call 'stop'.
Element[proto].stop = function () {
animationElements[this.id] && animationElements[length]--;
delete animationElements[this.id];
return this;
};
This decrements the total number of animations, and removes that animation from the list. Here's what the 'pause' function might look like:
Element[proto].pause = function () {
animationElements[this.id] && animationElements[length]--;
this._paused_anim = animationElements[this.id];
delete animationElements[this.id];
return this;
};
this saves the animation to be resumed later. then
Element[proto].unpause = function () {
this._paused_anim && (animationElements[this.id]=this._paused_anim);
++animationElements[length] == 1 && animation();
return this;
};
would unpause. Given scoping conditions, these two functions might need to be injected right into the Raphael source code (it's core hacking, I know but sometimes there's no alternative). I would put it right below the "stop" function shown above.
Try that, and tell me how it goes.
====EDIT====
Ok, so it looks like you'll have to modify the "start" attribute of animationElements[this.id]... something like:
this._pause_time = (+new Date) - animationElements[this.id].start;
in the pause, and then
animationElements[this.id].start = (+new Date) - this._pause_time;
on resume.
http://github.com/DmitryBaranovskiy/raphael/blob/master/raphael.js#L3064