StageXL icon indicating copy to clipboard operation
StageXL copied to clipboard

Juggler progress on hidden browser windows

Open bp74 opened this issue 11 years ago • 4 comments

As soon as the browser window is hidden, there are no more render frame events and therefore the Juggler does not advance the time any longer. It would be great if the Juggler would offer a feature to advance the time even if the browser window is hidden.

bp74 avatar Jun 25 '14 18:06 bp74

It seems to me that the solution for this would be to track the time since the last update and set the newest position accordingly?

kevinsheehan avatar Jun 25 '14 18:06 kevinsheehan

I've played a little bit with the problem and it's really difficult to come up with a good solution. Below is a test i did with a timer. I've extended the standard Juggler with a feature to advance the time with a timer if the RenderLoop doesn't advance the Juggler for a given duration (100ms in my example). After those 100ms the advanteTime function is called internally. The problem with this is, that the Timer is also kind of suspended while the window is minimized or you switch to another browser tab. The timer is executed but only every second or so. The result is that the Juggler animates all Animatables (like Tween, DelayedCall, etc.) but the resolution is only a second.

import 'dart:async';
import 'dart:html' as html;
import 'package:stagexl/stagexl.dart';

var canvas = html.querySelector('#stage');
var stage = new Stage(canvas, webGL: true);
var renderLoop = new RenderLoop();
var juggler = new ContinuousJuggler();

void main() {

  renderLoop.addStage(stage);
  renderLoop.juggler.add(juggler);

  var delayedCall = new DelayedCall(() => print(new DateTime.now()), 0.1);
  delayedCall.repeatCount = 10000;

  // stops when minimized
  // renderLoop.juggler.add(delayedCall);

  // continues when minimized
  juggler.add(delayedCall);
}

//-----------------------------------------------------------------------------

class ContinuousJuggler extends Juggler {

  Stopwatch _stopwatch = new Stopwatch();
  double _lastTime = 0.0;
  Timer _timer = null;
  Duration _timerDuration = const Duration(milliseconds: 100);

  ContinuousJuggler():super() {
    _stopwatch.start();
  }

  bool advanceTime(num time) {
    _resetTimeout();
    var time = _stopwatch.elapsedMilliseconds / 1000.0;
    var delta = time - _lastTime;
    _lastTime = time;
    return super.advanceTime(delta);
  }

  void _resetTimeout() {
    if (_timer != null) _timer.cancel();
    _timer = new Timer(_timerDuration, () => advanceTime(0.0));
  }
}

bp74 avatar Jun 26 '14 09:06 bp74

import 'dart:math' as math;
import 'dart:html' as html;
import 'dart:async';
import 'package:stagexl/stagexl.dart';

Stage stage = new Stage(html.querySelector('#stage'), webGL: true);

RenderLoop renderLoop = new RenderLoop();
var juggler = new ContinuousJuggler();

ResourceManager resourceManager = new ResourceManager();

int traffic=0;

void main() {
  resourceManager
  ..addBitmapData('www','images/box.png');
  resourceManager.load().then( (_) => init() ).catchError( (e) => print(e) );
}

void init(){
  renderLoop.addStage(stage);
  renderLoop.juggler.add(juggler);
  var delayedCall = new DelayedCall(() => execut(), 0.1);
  delayedCall.repeatCount=999999999999999999999999999999999;
  juggler.add(delayedCall);
}

//The procedure of execution
void execut(){
  if (traffic<10){
    new Box();
    traffic++;
  }
}

//The graphics object must be created and destroyed in a minimized window browser
class Box extends DisplayObjectContainer{
  Box(){
    double speed;
    double anglespeed;
    List<BitmapData> bpm;
    bpm = TileList.prepareTiles('www', 25, 25, 0, 4);
    var random = new math.Random();
    var scaling = random.nextInt(130);
    this.addTo(stage);
    var flipBook = new FlipBook(bpm, 10, true)
    ..x = scaling+60
    ..y = -25
    ..addTo(this)
    ..pivotX=12.5
    ..pivotY=12.5
    ..play();
    stage.juggler.tween(flipBook, 10, TransitionFunction.linear)

    ..animate.y.to(600.0)
    ..animate.rotation.by(25* math.pow(-1, random.nextInt(2)+1))
    ..onComplete = (){
      drop(flipBook);  
    };

    stage.juggler
    ..add(flipBook);
  }

  void drop(FlipBook flipbook){
    traffic--;     
    this.removeFromParent();
    flipbook.removeFromParent();
    stage.juggler.remove(flipbook);
  } 
}

//For processing tile sheet
class TileList { 
  static List<BitmapData> prepareTiles(String name, int tilewidth, int tileheight, int starttile, int counttiles) {
    List<BitmapData> tilelist = new List();
    Rectangle rect;
    Point point = new Point(0, 0);
    BitmapData tilesdata = resourceManager.getBitmapData(name);
    int counttilesx = tilesdata.width ~/ tilewidth; 
    int counttilesy = tilesdata.height ~/ tileheight; 
    for(int i = starttile; i < starttile+counttiles; i++) {
      BitmapData bufbmp = new BitmapData(tilewidth,tileheight,false,0xFFFFFFFF,1);
      int y= i~/counttilesx*tileheight; 
      int x= (i-i~/counttilesx*counttilesx)*tilewidth;
      rect=new Rectangle(x,y,tilewidth,tileheight);
      bufbmp.copyPixels(tilesdata, rect,point);
      tilelist.add(bufbmp);
    }  
    return tilelist;
  }
}

class ContinuousJuggler extends Juggler {

  Stopwatch _stopwatch = new Stopwatch();
  double _lastTime = 0.0;
  Timer _timer = null;
  Duration _timerDuration = const Duration(milliseconds: 100);

  ContinuousJuggler():super() {
    _stopwatch.start();
  }

  bool advanceTime(num time) {
    _resetTimeout();
    var time = _stopwatch.elapsedMilliseconds / 1000.0;
    var delta = time - _lastTime;
    _lastTime = time;
    return super.advanceTime(delta);
  }

  void _resetTimeout() {
    if (_timer != null) _timer.cancel();
    _timer = new Timer(_timerDuration, () => advanceTime(0.0));
  }
}

Vasilisk8888 avatar Jul 07 '14 10:07 Vasilisk8888

Okay i see the problem. You create an instance of ContinuousJuggler but you use the stage.juggler in the Box class. The stage.juggler is driven by the renderLoop.juggler which is driven by the requestAnimationFrame function of HTML5. This function is suspended when the tab has no focus.

Therefore we invented the ContinuousJuggler which has a fallback heartbeart which is triggered by a timer. This timer is called even if the tab has no focus - at least once a second. This is not perfect but i'm not aware of a better solution right now.

bp74 avatar Jul 16 '14 18:07 bp74