Fabricjs object moving fire on finish - javascript

I want to call a function if the object move Event is finished.
Currently it Looks like this:
canvas.on('object:moving', function (event) {
recCanvas(canvas) // fire this if finished
});
The issue is that it fires the function everytime an object is moved how can I prevent this and fire the function once the moving finished.

What event happened when move event is finished?
Mouse up will finish event object moving. So you need a boolean variable to check when object is moving, then on mouse up if object has been moved call you function:
var isObjectMoving = false;
canvas.on('object:moving', function (event) {
isObjectMoving = true;
});
canvas.on('mouse:up', function (event) {
if (isObjectMoving){
isObjectMoving = false;
recCanvas(canvas) // fire this if finished
}
});

There is an object event when the object is finished moving
canvas.on('object:moved', function(event){
recCanvas(canvas) // fire this if finished
});

Yeah i know this is like pretty late and you probably found a suitable answer, but in the current version of Fabric js, if you try to use the above marked answer or try to use the object:modified event, you are likely going to encounter some bugs, not immediately but here is what I found;
when you use the mouse:up or object:modified to know if object position is changed, when you try to drag the object to change the position a couple of more times to be sure if it's working great, the browser page might stop responding, and sometimes it will take time before the canvas responds and the value to get updated.
So here is what I do, although i am using react js with fabric js so I am not fully sure if you who are using just vanilla javascript encounter this problem but in case someone also using react search for this problem here is a solution.
use a useEffect hook or onComponentDidMount, and inside it create a setInterval that get the currently active object after every 0.5 seconds or whatever time you think will suit you, and then try to update the variable and also don't forget to clear the interval.
//import your fabric js and react
const GetActivePosition = () => {
const [pos, setPos] = useState({left: 0, top: 0})
useEffect(() => {
const active = canvas.getActiveObject()
let interval = undefined
if (active){
interval = setInterval(() => {
setPos({left: active.left, top: active.top})
}, 500)
}
return () => clearInterval(interval)
}, [])
return (
<>
<p>Left: {pos.left}</>
<p>Top: {pos.top}</>
</>
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Related

React does not declare value outside of function

So, I have a function:
let animation = null;
const animationRef = (ref) => {
console.log(ref);
animation = ref;
}
And at some point later in the code, I need to access animation. But when I print animation at that point, animation is still null. I am certain that animationRef gets called, because the print of ref is shown in the console.
For context, here is an example from the library that calls animationRef. The reason why I am not putting animation into a state is, that it will get accessed using the event handler of matter.js. Animation is both null if I try to access it inside the Event and outside.
When I put an
useEffect(() => {
console.log(animation);
})
After animationRef, it gets printed, but after the next re-render it is null again.

JavaScript setInterval only works once

I hope you are doing well.
I'm working on a minigame based on JS and want to prevent the user from shooting infinite bullets, so I created this condition. Which only works for the first time and then breaks for the next clicks
also to mention this whole section is inside the loop so shooting works well with or without setInterval
let shootController = true
canvas.addEventListener("click", () => {
if (shootController) {
//shooting code goes here
shootController = false;
}
});
if (shootController === false) {
setInterval(() => {
shootController = true;
}, 1000);
}
Thanks for your replies beforehand
Somehow, I defined the 'setInterval' before and outside the loop and then called that function below and outside. This one WORKS
But from curiosity, I didn't define it at the top as a function and only used it below the loop. This one didn't work.
The problem is solved, but I don't know why.

stopping a nested javascript function running more than once

I'm trying to get a javascript function to run only once. I've seen this question has been asked before, e.g. Function in javascript that can be called only once, but I can't get the solutions in here to work. I'm not sure if it's because I've got nested functions, or whether there's something I'm missing. Essentially, I'm trying to run a function which, when a webpage is scrolled, it:
- runs a little animation on a canvas in the header
- reduces the size of the header
- leaves it at that
But when there is any subsequent scrolling, the animation keeps re-running. Here's a summarised version of the non-working code:
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
},1000);
}
});
I've also tried declaring a global variable, setting it to "false" in a "window.onload" function, then set it to true in an if function that runs the animation (the if function running only if the variable is false), but that doesn't stop it either. Thoughts?
What you're looking for is something along the lines of listenToOnce where the listener fires the one time, but never again. This could be modified to a number of calls, but the logic is like so:
Register the listener.
Then once the listener fires, remove it.
See .off
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
// $(document).off('scroll'); // or here
},1000);
}
$(document).off('scroll'); // remove listener, you can place this in the setTimeout if you wish to make sure that the classes are added/removed
});
Don't use a time out. That is why you are getting in trouble. Declare a variable outside of your function using var, that will make it global. Your code should be inside of a check for that variable. Before executing your code the first time but inside of the check, change that variable so that the code will never run again.
Try avoid setTimeout. Almost all animation can be watched for end.
function doHeaderAnimation() {
return $('header').animate();
}
function makeHeaderSmall() {
$("header").removeClass("large").addClass("small");
}
function handleScroll(event) {
if ($(document).scrollTop() > 0) {
doHeaderAnimation().then(makeHeaderSmall);
$(document).off("scroll", handleScroll);
}
}
$(document).on("scroll", handleScroll);

How to let JavaScript wait until certain event happens?

I am writing a webpage with the following structure:
One section (table A) depends on another section (table B);
Another section (table B) has elements that require recalculation on each update. The calculation is handled by external tools, and will cause an event when finished.
In order to guarantee correctness, the table need to be updated only after the other table is fully updated (i.e., done with computation). However, I don't know how to effectively achieve this, and I could not find any wait facility within JavaScript.
For now, I am using the following method:
Declare a global variable updated and make it false;
After the first table received input, I make an empty while loop until updated is true;
Add an listener, once the calculation is done and the event received, set updated to true.
This seems unintuitive to me but I cannot think of any other way of doing it. Is there any good ways to do this?
Thanks for any inputs!
In 2022, it's useful to have an event listener that fires off a Promise (which can be used in promise-chains, or async/await code). A clean way to make one:
function getPromiseFromEvent(item, event) {
return new Promise((resolve) => {
const listener = () => {
item.removeEventListener(event, listener);
resolve();
}
item.addEventListener(event, listener);
})
}
async function waitForButtonClick() {
const div = document.querySelector("div")
const button = document.querySelector("button")
div.innerText = "Waiting for you to press the button"
await getPromiseFromEvent(button, "click")
div.innerText = "The button was pressed!"
}
waitForButtonClick()
<button>ClickMe</button>
<div></div>
Add an listener, once the calculation is done and the event received, set updated to true.
Instead of setting updated to true, and then waiting for updated to be true- just do whatever you want to do in the listener.
myEventBus.addListener(function () {
// do whatever
updateTable();
alert('table updated!');
});
Doing empty while loops is a bad idea. Not only do you burn CPU cycles, but Javacript is single threaded so you will loop forever without giving anyone a chance to change the variable.
What you can do is rewrite the table that has other people depending on it to "fire an event itself". There are many ways to do this, but basicaly you just want it to call a "continuation' function instead of blindily returning. This function can be predefined or you can pass it as a parameter somewhere.
//this is just illustrative
//Your actual code will be probably very different from this.
function update_part(){
//do something
signal_finished_part()
}
var parts_done = 0;
function signal_finished_part(){
parts_done ++;
if(parts_done >= 5){
signal_all_parts_done();
}
}
function signal_all_parts_done()
{
//do something to table A
}
You could write a callback function for whatever triggers the update. To avoid messy callbacks, you could use promises too, and update parts of the table depending on the data retrieved in the update operation. Open to suggestions.

dojo mouseover with delay

I wish to do something like as follows:
When the mouse goes over to some element, record it
If the mouse stays there for 3 seconds, then execute some action f() for that element
If the mouse leaves that element before 3 seconds, then the action should not be executed.
How can I implement this delayed execution with possible cancellation? An answer using DOJO library would be nicer since I am using DOJO toolkit in my project.
Try the following:
var delay = 3000;
dojo.forEach(dojo.query(".some-element-set"), function(element) {
dojo.connect(element, "onmouseover", function() {
// dojo.partial(f, this.id) will apply `this.id` to `f`, but it
// will not execute it and will only a new function
this._timer = setTimeout(dojo.partial(f, this.id), delay);
});
dojo.connect(element, "onmouseout", function() {
// this._timer was set as an element attribute in the above event
// handler so we don't have to keep track of them separately in
// some silly array/object
clearTimeout(this._timer);
});
});
See the query, forEach, connect and partial documentation for more information.
Edit: I've update my answer per the OP's comment

Categories