Как установить и выровнять по центру Pixi.js этап в развернутом элементе холста?

#html5-canvas #resize #pixi.js

#html5-холст #изменение размера #pixi.js

Вопрос:

Я подготовил полный jsfiddle для моего вопроса —

скриншот

Для настольной игры я пытаюсь использовать кнопки пользовательского интерфейса jQuery с обеих сторон и назначить остальную часть экрана области рисования Pixi.

Я нашел предложение от Pixi.js разработчику необходимо масштабировать сцену, чтобы ее дочерние элементы масштабировались автоматически, и ниже я пытаюсь это реализовать:

 $(function() {
  var WIDTH = 400;
  var HEIGHT = 400;
  var RATIO = WIDTH / HEIGHT;

  var scaleX = 1,
    scaleY = 1,
    offsetX = 0,
    offsetY = 0,
    oldX, oldY;

  var app = new PIXI.Application({
    width: WIDTH,
    height: HEIGHT,
    view: document.getElementById('pixiCanvas'),
    backgroundColor: 0xFFFFFF
  });

  app.stage.interactive = true;
  app.stage.on('pointerdown', onDragStart)
    .on('pointerup', onDragEnd)
    .on('pointerupoutside', onDragEnd)
    .on('pointermove', onDragMove);

  var background = new PIXI.Graphics();
  for (var i = 0; i < 8; i  ) {
    for (var j = 0; j < 8; j  ) {
      if ((i   j) % 2 == 0) {
        background.beginFill(0xCCCCFF);
        background.drawRect(i * WIDTH / 8, j * HEIGHT / 8, WIDTH / 8, HEIGHT / 8);
        background.endFill();
      }
    }
  }
  app.stage.addChild(background);

  var bunny = PIXI.Sprite.from('https://pixijs.io/examples/examples/assets/bunny.png');
  bunny.anchor.set(0.5);
  bunny.scale.set(2);
  bunny.x = WIDTH / 2;
  bunny.y = HEIGHT / 2;
  app.stage.addChild(bunny);

  function onDragStart(ev) {
    var stagePoint = ev.data.getLocalPosition(app.stage);
    if (bunny.getBounds().contains(stagePoint.x, stagePoint.y)) {
      bunny.alpha = 0.5;
      oldX = stagePoint.x;
      oldY = stagePoint.y;
      this.data = ev.data;
    }
  }

  function onDragMove() {
    if (this.data) {
      var stagePoint = this.data.getLocalPosition(app.stage);
      bunny.x  = stagePoint.x - oldX;
      bunny.y  = stagePoint.y - oldY;
      oldX = stagePoint.x;
      oldY = stagePoint.y;
    }
  }

  function onDragEnd() {
    if (this.data) {
      bunny.alpha = 1;
      var stagePoint = this.data.getLocalPosition(app.stage);
      this.data = null;
    }
  }

  $(':button').button();
  $('#scaleCheck').checkboxradio();

  window.onresize = function() {
    if (!$('#scaleCheck').prop('checked')) {
      return;
    }

    var canvasRatio = $('#pixiCanvas').width() / $('#pixiCanvas').height();

    if (canvasRatio > RATIO) {               // if canvas is too wide
      scaleX  = 1 / canvasRatio;
      scaleY  = 1;
      offsetX = 0;
      offsetY = 0;
    } else {
      scaleX  = 1;
      scaleY  = canvasRatio;
      offsetX = 0;
      offsetY = 0;
    }

    app.stage.scale.set(scaleX, scaleY);
    app.stage.position.set(offsetX, offsetY); // how to center-align?
  }

  window.onresize();
});  
 body {
  margin: 0;
  padding: 0;
}

#mainDiv {
  width: 100%;
  height: 100vh;
  background: #FFF;
  display: flex;
}

#leftMenuDiv {
  text-align: center;
  background: #FCC;
}

#rightMenuDiv {
  text-align: center;
  background: #CFC;
  flex-shrink: 1;
}

#canvasDiv {
  flex-grow: 1;
}

#pixiCanvas {
  width: 100%;
  height: 100%;
  background: #CCF;
  box-sizing: border-box;
  border: 4px red dotted;
}  
 <div id="mainDiv">
  <div id="leftMenuDiv">
    <button id="btn1">Button 1</button><br>
    <button id="btn2">Button 2</button><br>
    <button id="btn3">Button 3</button><br>
  </div>

  <div id="canvasDiv">
    <canvas id="pixiCanvas"></canvas>
  </div>

  <div id="rightMenuDiv">
    <input id="scaleCheck" type="checkbox">
    <label for="scaleCheck">Fit and center Pixi stage</label><br>
    <button id="btn4">Button 4</button><br>
    <button id="btn5">Button 5</button><br>
    <button id="btn6">Button 6</button><br>
  </div>
</div>

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> 
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/pixi.js@5.3.3/dist/pixi.min.js"></script>  

Моя проблема с приведенным выше кодом заключается в следующем —

При первом запуске моего кода он работает. Но после того, как вы установите флажок и измените размер окна браузера, перетаскивание прервется.

Ответ №1:

Я смог решить свою проблему с помощью следующего кода:

скриншот

 $(function() {
    var WIDTH = 400;
    var HEIGHT = 400;
    var RATIO = WIDTH / HEIGHT;

    var scaleX = 1, scaleY = 1, offsetX = 0, offsetY = 0;

    var app = new PIXI.Application({
        width: WIDTH,
        height: HEIGHT,
        view: document.getElementById('pixiCanvas'),
        backgroundColor: 0xFFFFFF
    });

    var background = new PIXI.Graphics();
    for (var i = 0; i < 8; i  ) {
        for (var j = 0; j < 8; j  ) {
            if ((i   j) % 2 == 0) {
                background.beginFill(0xCCCCFF);
                background.drawRect(i * WIDTH / 8, j * HEIGHT / 8, WIDTH / 8, HEIGHT / 8);
                background.endFill();
            }
        }
    }
    app.stage.addChild(background);

    var bunny = PIXI.Sprite.from('https://pixijs.io/examples/examples/assets/bunny.png');
    bunny.interactive = true;
    bunny.buttonMode = true;
    bunny.anchor.set(0.5);
    bunny.scale.set(2);
    bunny.x = WIDTH / 2;
    bunny.y = HEIGHT / 2;
    bunny.on('pointerdown', onDragStart)
         .on('pointerup', onDragEnd)
         .on('pointerupoutside', onDragEnd)
         .on('pointermove', onDragMove);
    app.stage.addChild(bunny);

    function onDragStart(ev) {
        this.data = ev.data;
        this.alpha = 0.5;
    }

    function onDragMove() {
        if (this.data) {
            var newPos = this.data.getLocalPosition(this.parent);
            this.x = newPos.x;
            this.y = newPos.y;
        }
    }

    function onDragEnd() {
        if (this.data) {
            this.alpha = 1;
            this.data = null;
        }
    }

    $(':button').button();
    $('#scaleCheck').checkboxradio();

    window.onresize = function() {
        if (!$('#scaleCheck').prop('checked')) {
            return;
        }

        var canvasRatio = $('#pixiCanvas').width() / $('#pixiCanvas').height();
        
        if (canvasRatio > RATIO) {          // if canvas is too wide
            scaleX = RATIO / canvasRatio;
            scaleY = 1;
            offsetX = (1 - scaleX) * WIDTH / 2;
            offsetY = 0;
        } else {
            scaleX = 1;
            scaleY = canvasRatio / RATIO;
            offsetX = 0;
            offsetY = (1 - scaleY) * HEIGHT / 2;
        }

        app.stage.scale.set(scaleX, scaleY);
        app.stage.position.set(offsetX, offsetY);
    }

    window.onresize();
});  
 body {
    margin: 0;
    padding: 0;
}

#mainDiv {
    width: 100%;
    height: 100vh;
    background: #FFF;
    display: flex;
}

#leftMenuDiv {
    text-align: center;
    background: #FCC;
}

#rightMenuDiv {
    text-align: center;
    background: #CFC;
    flex-shrink: 1;
}

#canvasDiv {
    flex-grow: 1;
}

#pixiCanvas {
    width: 100%;
    height: 100%;
    background: #CCF;
    box-sizing: border-box;
    border: 4px red dotted;
}  
 <div id="mainDiv">
  <div id="leftMenuDiv">
    <button id="btn1">Button 1</button><br>
    <button id="btn2">Button 2</button><br>
    <button id="btn3">Button 3</button><br>
  </div>

  <div id="canvasDiv">
    <canvas id="pixiCanvas"></canvas>
  </div>

  <div id="rightMenuDiv">
    <input id="scaleCheck" type="checkbox" checked>
    <label for="scaleCheck">Fit and center Pixi stage</label><br>
    <button id="btn4">Button 4</button><br>
    <button id="btn5">Button 5</button><br>
    <button id="btn6">Button 6</button><br>
  </div>
</div>

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> 
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/pixi.js@5.3.3/dist/pixi.min.js"></script>  

Основным изменением для меня было сделать перетаскиваемый спрайт интерактивным и добавить к нему прослушиватели перетаскивания — вместо app.stage .

Может app.stage быть, это может быть интерактивным, а также в моем исходном коде? Я думаю, что в этом случае требуется какое-то матричное вычисление, поэтому математику сложно понять.