Scale KineticJS Stage with Browser Resize?

2019-03-31 21:39发布

问题:

Recently discovered KineticJS as I've been trying to convert my Flash skills to HTML Canvas. It is an extremely impressive tool! Question: If I'm using a front-end like Bootstrap and I have a page with several divs that contain KineticJS-generated canvases, can I get those canvases to scale along with everything else on the page? Thanks much.

----- Due to character limit of comments, I'm replying in the OP. ---------

Would you set it up so that there is a maximum size and then have it scale as when the browser is scaled? For example, in Actionscript I did this to track the size of the browser and scale the stage:

stageListener.onResize = function():Void {  
    // Calculate % of total allowable change for each dimension  
    var percentWScale:Number = (Stage.width - minStageWSize)/stageWResizeRange;  
    var percentHScale:Number = (Stage.height - minStageHSize)/stageHResizeRange;
    //  Test to see if the stage size is in the rescale range
    if ((Stage.width < maxStageWSize)  || (Stage.height < maxStageHSize))   {
        pfarm.stageChanged = true;
        // Calculate the scale factor for each dimension, but don't go below the minimum
        var percentHScale:Number = Math.max(minimumScale, Stage.width/1024);
        var percentVScale:Number = Math.max(minimumScale, Stage.height/768);
        //trace ("H Scale Factor: "+percentHScale+". V Scale factor: "+percentVScale);
        // Determine which window dimension to use for scaling -
        // Use the one which is the most scaled down from the maximum stage size.
        var getScaleFrom:String = (percentHScale <<  percentVScale)? "hScale" : "vScale";
        // Scale the object
        if (getScaleFrom == "hScale") {
            baseParent._width = projectContentXSize * percentHScale;
            baseParent._height = projectContentYSize * percentHScale;
            } else {
                baseParent._width = projectContentXSize * percentVScale;
                baseParent._height = projectContentYSize * percentVScale;
        }
    } // END RESIZE OF SCROLLPANE CONTENT

For the benefit of flash refugees (like me) who are crossing the complex border into HTML5 land, could you provide an example or 2?

回答1:

Sure!

But, be careful about scaling the Kinetic container element with CSS (as Bootstrap does) because the browser will resize by "stretching" the pixels rather than letting Kinetic redraw the pixels.

Since html canvas (and therefore Kinetic objects) are really just pixels, the common warnings about resizing also apply when scaling the Kinetic container.

  • The drawings will distort if you scale horizontally and vertically by different factors.
  • Drawings that are scaled up/down by a large factor may get the "jaggies".

A better way to resize your kinetic stage is to:

  • Resize the kinetic container element proportionally
  • Let Kinetic know its container is resizing with .setWidth and .setHeight
  • Use .setScaleX and setScaleY followed by stage.draw to scale the stage and all its components.


回答2:

Here's an implemetation of the method recommended by markE. It also incorporates techniques described in this article on auto-resizing HTML5 games.

Find a working demo here: http://jsfiddle.net/klenwell/yLDVz/

var iPadLandscapeDimensions = {
  width: 1024,
  height: 768
};

var KineticScreenAutoSize = (function() {
  var self = {
    $container: null,
    stage: null,
    baseWidth: 0,
    baseHeight: 0
  };

  var init = function(selector, baseWidth, baseHeight) {
    self.$container = $(selector);
    self.baseWidth = baseWidth;
    self.baseHeight = baseHeight;
    initStage();
  };

  var initStage = function($container) {
    self.stage = new Kinetic.Stage({
      container: self.$container.attr('id'),
      width: self.baseWidth,
      height: self.baseHeight
    });
  };

  /*
   * Screen-Sizing Methods
   */
  var autoSize = function() {
    // Style required for resizeScreen below
    self.$container.css({
      position: 'absolute',
      left: '50%',
      top: '50%',
      width: '100%',
      height: '100%'
    });

    // Resize automatically
    window.addEventListener('resize', resizeStageToFitScreen, false);
    window.addEventListener('orientationchange', resizeStageToFitScreen, false);

    // Resize now
    resized = resizeStageToFitScreen();
  };

  var resizeStageToFitScreen = function() {
    /*
     * Following directions here: https://stackoverflow.com/a/19645405/1093087
     */
    var resized = calculateResize();

    // Resize the kinetic container element proportionally
    resized.cssSettings = {
      left: resized.xToCenter + 'px',
      top: resized.yToCenter + 'px',
      width: resized.width,
      height: resized.height,
    }
    self.$container.css(resized.cssSettings);

    // Let Kinetic know its container is resizing with .setWidth and .setHeight
    self.stage.setSize(resized);

    // Use .setScaleX and setScaleY followed by stage.draw to scale the stage
    // and all its components.
    self.stage.scaleX(resized.xScale);
    self.stage.scaleY(resized.yScale);

    self.stage.draw();
    return resized;
  };

  var calculateResize = function() {
    var resized = {
      width: 0,
      height: 0,
      xScale: 0,
      yScale: 0,
      xToCenter: 0,
      yToCenter: 0
    }

    var windowWidth = window.innerWidth,
        windowHeight = window.innerHeight,
        desiredWidthToHeightRatio = self.baseWidth / self.baseHeight,
        currentWidthToHeightRatio = windowWidth / windowHeight;

    if ( currentWidthToHeightRatio > desiredWidthToHeightRatio ) {
      resized.width = windowHeight * desiredWidthToHeightRatio;
      resized.height = windowHeight;
    }
    else {
      resized.width = windowWidth;
      resized.height = windowWidth / desiredWidthToHeightRatio;
    }

    resized.xToCenter = (window.innerWidth - resized.width) / 2;
    resized.yToCenter = (window.innerHeight - resized.height) / 2;
    resized.xScale = resized.width/self.baseWidth,
    resized.yScale = resized.height/self.baseHeight;

    return resized;
  };

  /*
   * Public API
   */
  var publicAPI = {
    init: init,
    stage: function() { return self.stage },
    autoSize: autoSize
  }

  return publicAPI;

})();

// Launch Resize
$(document).ready(function() {
  KineticScreenAutoSize.init(
    'div#stage-container',
    iPadLandscapeDimensions.width,
    iPadLandscapeDimensions.height
  );
  KineticScreenAutoSize.autoSize();
});