How to trigger lazy loading function before user reaches scrollController.position.maxScrollExtent? - flutter

The result I want to achieve, is to trigger a lazy loading function before user reaches scrollController.position.maxScrollExtent, so in that way I will "minimize" the waiting time in the eyes of user.
In my case, I want the lazy loading function to trigger every time users scroll an 80% of the screen
Here is my working code snippet with lazy loading function triggered when user reaches the end of the screen (classic way):
scrollController.addListener(() {
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
print('bottomReached');
// lazy load function
}
});
I tried to change the if statement to this:
scrollController.position.pixels >= scrollController.position.maxScrollExtent*0.8
but it didn't work as expected. What else can I do ? Thanks in advance.

You have to set a delta limit, which checks if the scroll is about to read max extent.
scrollController.addListener(() {
if (scrollController.position.pixels > scrollController.position.maxScrollExtent*0.8) {
print('bottomReaching');
// lazy load function
}
});
Here it checks if the current pixel is more than the delta value (maxExtent * 0.8) and if true, loads the function.
One more thing to note is that if in delta range, the function will be triggered with change of every pixel. So it is suggested to have a bool variable outside of scrollController.addListener to check if the function is already running.
For reference https://www.youtube.com/watch?v=EXJboDdIpEA
Happy coding!

Related

Understanding Flutter's SchedulerBinding in the context of an animated timeline

I'm trying to understand the part of this code below that uses SchedulerBinding.instance.scheduleFrameCallback(beginFrame);. beginFrame is listed in the other code block below.
The code comes from here, which is an animated timeline for Flutter. I don't expect anyone to read all this, obviously. But given some context, can you understand what for it is being used?
Context: this part of the code is inside a function called setViewport. The viewport of a timeline is simply the visible part of that timeline. So, once a viewport is set (a start and end point in the timeline are given), it ends animating something in the timeline. You can see that in the process of doing it, it calls SchedulerBinding.instance.scheduleFrameCallback, which is what I want to know what is used for. I obviously went into the page for SchedulerBinding but the explanation is so generic that I don't have an idea what it is used for.
if (!animate) {
_renderStart = start;
_renderEnd = end;
advance(0.0, false);
if (onNeedPaint != null) {
onNeedPaint();
}
} else if (!_isFrameScheduled) {
_isFrameScheduled = true;
_lastFrameTime = 0.0;
SchedulerBinding.instance.scheduleFrameCallback(beginFrame);
}
Here's beginFrame:
/// Make sure that all the visible assets are being rendered and advanced
/// according to the current state of the timeline.
void beginFrame(Duration timeStamp) {
_isFrameScheduled = false;
final double t =
timeStamp.inMicroseconds / Duration.microsecondsPerMillisecond / 1000.0;
if (_lastFrameTime == 0.0) {
_lastFrameTime = t;
_isFrameScheduled = true;
SchedulerBinding.instance.scheduleFrameCallback(beginFrame);
return;
}
double elapsed = t - _lastFrameTime;
_lastFrameTime = t;
if (!advance(elapsed, true) && !_isFrameScheduled) {
_isFrameScheduled = true;
SchedulerBinding.instance.scheduleFrameCallback(beginFrame);
}
if (onNeedPaint != null) {
onNeedPaint();
}
}
According to the project README, it's used to keep the Flare animations in sync:
"To have the animation reproduce correctly, it's also necessary to call advance(elapsed) on the current FlutterActor each frame. Moreover, the current ActorAnimation requires that the function apply(time) is called on it to display it's correct interpolated values.
This is all made possible by relying on Flutter's SchedulerBinding.scheduleFrameCallback()."

Update with state change triggering GetMouseButtonDown

I'm struggling to find an elegant solution to what seems to be a fairly simple problem.
In Update() some code executes in state1, and when if (Input.GetMouseButtonDown(0)) is clicked, some more code is executed and the state is changed to state2.
However, state2 also has a if (Input.GetMouseButtonDown(0)) which is being triggered by the original if (Input.GetMouseButtonDown(0)) from state1 as the change happens quickly, as Input.GetMouseButtonDown(0) can return true for several frames.
I've tried using booleans, and using Time.deltaTime() but I'm having issues with ensuring that the first mouse click doesn't trigger the second.
If anyone has any ideas or suggestions, I'd be very grateful.
Thanks
I think what you're looking for is edge detection. Essentially you check a condition for a change in state. While the boolean is the same as the last time you asked it you don't do anything but once it changes you execute some code.
//global variable
bool lastState = false;
//inside a method
bool newState = Input.GetMouseButtonDown(0);
if(newState != lastState)
{
lastState = newState;
//do stuff
}

How to avoid Thread.sleep() in a for loop from interrupting the UI Thread?

I have the following pseudo code to clarify my problem and a solution. My original posting and detailed results are on Stack Overflow at: Wait() & Sleep() Not Working As Thought.
public class PixelArtSlideShow { // called with click of Menu item.
create List<File> of each selected pixelArtFile
for (File pixelArtFile : List<File>) {
call displayFiles(pixelArtFile);
TimeUnits.SECONDS.sleep(5); }
}
public static void displayFiles(File pixelArtFile) {
for (loop array rows)
for (loop array columns)
read-in sRGB for each pixel - Circle Object
window.setTitle(....)
}
// when above code is used to Open a pixelArtFile, it will appear instantly in a 32 x 64 array
PROBLEM: As detailed extensively on the other post. Each pixelArtFile will display the setTitle() correctly and pause for about 5 secs but the Circle’s will not change to the assigned color except for the last file, after the 5 secs have passed. It's like all the code in the TimeUnits.SECONDS.sleep(5); are skipped EXCEPT the window.setTitle(...)?
My understanding is the TimeUnits.SECONDS.sleep(5); interrupts the UI Thread uncontrollable and I guess must somehow be isolated to allow the displayFiles(File pixelArtFile) to fully execute.
Could you please show me the most straight forward way to solve this problem using the pseudo code for a more completed solution?
I have tried Runnables, Platform.runLater(), FutureTask<Void>, etc. and I'm pretty confused as to how they are meant to work and exactly coded.
I also have the two UI windows posted on the web at: Virtual Art. I think the pixelArtFile shown in the Pixel Array window may clarify the problem.
THANKS
Don't sleep the UI thread. A Timeline will probably do what you want.
List<File> files;
int curFileIdx = 0;
// prereq, files have been appropriately populated.
public void runAnimation() {
Timeline timeline = new Timeline(
new KeyFrame(Duration.seconds(5), event -> {
if (!files.isEmpty()) {
displayFile(curFileIdx);
curFileIdx = (curFileIdx + 1) % files.size();
}
})
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
// prereq, files have been appropriately populated.
public void displayFile(int idx) {
File fileToDisplay = files.get(idx);
// do your display logic.
}
Note, in addition to the above, you probably want to run a separate task to read the file data into memory, and just have a List<ModelData> where ModelData is some class for data you have read from a file. That way you wouldn't be continuously running IO in your animation loop. For a five second per frame animation, it probably doesn't matter much. But, for a more frequent animation, such optimizations are very important.

make a simple transition in easelsjs

I'm using easelsjs and I need to make a simple transition.
Just change the opacity of the stage gradually.
I'm using the alpha property and the Ticker but I don't see the transition but only the last stage of it.
Is there a simple example for this - I tried to look but couldn't find
I don't know if you want to change the stage opacity or the shape opacity, but if you want your entire canvas to have less opacity, you can simply use JavaScript + CSS transitions to do it. No need for EaselJS in this case.
But if you want to create alpha transitions on canvas objects you should take a look at TweenJS, it simplifies the animation process a lot.
Also, if you want to continue using EaselJS only (or with TweenJS), you'll need to implement the ticker properly, here's how I create my ticker function:
createjs.Ticker.on("tick", tick);
createjs.Ticker.setFPS(60); // This will call the tick() function at every 1000/60ms
function tick() {
stage.update();
}
And then you'll need to say when your button was clicked to your ticker function, so it can decrease your object's alpha every frame. I highly recommend using TweenJS instead of doing this (I've put a TweenJS demonstration at the end of this answer):
$(document).ready(function () {
var stage = new createjs.Stage(canvas);
var s = new createjs.Shape();
s.graphics.beginStroke("#F00").beginFill("#FF0000").drawRect(150.5, 100.5, 300, 300);
stage.addChild(s);
$("#d").click(function () {
s.fadeOut = true;
});
// Tick:
createjs.Ticker.on("tick", tick);
createjs.Ticker.setFPS(60);
function tick() {
if(s.fadeOut){
s.alpha -= 0.01;
s.fadeOut = (s.alpha > 0.4);
}
stage.update();
}
});
I've simplified your code a little bit too. Here's how the code inside the click event handler would look using TweenJS:
createjs.Tween.get(s).to({alpha: 0.4},1000);
Note that both EaselJS and TweenJS work together.

Time Delay for a process in Unity 3D

I have to give the delay for the process to happen, which I am calling in the Update function.
I have tried CoUpdate workaround also. Here is my code:-
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while(true)
{
yield CoUpdate();
}
}
function CoUpdate()
{
//I have placed the code of the Update().
//And called the wait function wherever needed.
}
function wait()
{
checkOnce=1; //Whenever the character is moved.
yield WaitForSeconds(2); //Delay of 2 seconds.
}
I have to move an object when a third person controller(which is another object) moves out of a boundary. I have included "yield" in my code. But, the problem happening is: The object which was moving when I gave the code for in the Update(), is moving, but isn't stopping. And it is moving up and down. I don't know what is happening! Can someone help? Please, thanks.
I am not entirely clear what you are trying to accomplish, but I can show you how to set up a Time Delay for a coroutine. For this example lets work with a simple cool down, much like you set up in your example. Assuming you want to continuously do something every 2 seconds while your game is running a slight modification can be made to your code.
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(2);
}
}
You can also calculate a wait time using some other logic
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(CalculateWait());
}
}
function CalculateWait() : float
{
// use some logic here to determine the amount of time to wait for the
// next CoStart cycle to start
return someFloat;
}
If I have missed the mark entirely then please update the question with a more detail about what you are attempting to accomplish.
I am not 100% sure that I understand you question but if you want to start one object to move when the other is out of bound then just make a reference in the first object to the second object and when the first object is out of bounds (check for this in Update of the first object) call some public function StartMove on the second object.
I wouldn't suggest CoRoutines. It can sometimes crash your computer. Just define a variable and decrement it. Example:
private float seconds = 5;
then do anywhere you want to delay:
seconds -= 1 * Time.deltaTime;
if(seconds <= 0) {your code to run}
This will make a delay of 5 seconds. You can change 5 to any value to change the number of seconds. Also you can speed up the decrement by changing the value of 1. (This is mostly useful when you want to sync 2 delayed actions, by using the same variable)
Hope this helps. Happy coding :)