Skip to content
Advertisement

Change modal height based on it’s content

I’m trying to create a modal scene for my phaser 3 project. here is my current code in modal.js:

class modal extends Phaser.Scene {

    // constructor
    constructor() {
        super("Modal");
    }

    preload() {
        this.load.image("header", "Assets/popUpTop.png");
        this.load.image("x", "Assets/x.png");
    }

    init(data) {
        this.modalName = data.modalName;
    }

    create() {
        // create a white rounded rectangle as modal background
        var bgGrafics = this.make.graphics();
        bgGrafics.fillStyle(0xffffff, 1);
        bgGrafics.fillRoundedRect(0, 0, gameConfig.width / 3, gameConfig.height / 1.7, 51);
        bgGrafics.generateTexture("modalBg", gameConfig.width / 3, gameConfig.height / 1.7);
        this.modalBg = this.add.sprite(gameConfig.width / 2, gameConfig.height / 2, "modalBg");
        
        this.modalBg.setScale(1.05);
        
        this.headerBg = this.add.image(0, 0, "header");
        this.headerBg.setScale((this.modalBg.width * 1.05 / this.headerBg.width), (1 + (0.05 * this.modalBg.height / this.headerBg.height)));
        
        this.closeBtn = new RoundedButton(this, 0, 0, 43, "x", 1.3, () => {this.close()}); // RoundedButton is a custom class that extends Phaser.GameObjects.Container
        this.add.existing(this.closeBtn);

        // help modal
        if(this.modalName === 'help') {
        // add content of help modal 
        }
        // profile modal
        else if(this.modalName === 'profile') {
        // add content of profile modal 
        }
        // confirm spin modal
        else if(this.modalName === 'spin') {
        // add content of confirm modal 
        }
        
        Phaser.Display.Align.In.TopRight(this.closeBtn, this.modalBg);
        Phaser.Display.Align.In.TopCenter( this.headerBg, this.modalBg );
    }

    close() {
        this.scene.stop('Modal');
        this.scene.run('PlayGame');
    }
}

as you can see in my code, I’m trying to add different contents to modal based on modelName but I couldn’t find a way to change modal height based on it’s content. how can I do this?

Advertisement

Answer

A solution is, to use the function setWordWrapWidth from gameObject Phaser.GameObjects.TextStyle (link to the documentation) to limit the width, then setting the content of the text gameObject and finally getting and using the height property of the text gameObject.

With the width and height of the text gameObject (calculated from Phaser), you can simply render the graphics object, with the correct size (I also would add some padding). If you want/need to generate the texture, you would have to delete it each time, since the size might have changed.

Here is a short demo:
(I’m generating the texture, since it is in the original code, but I would simply use the graphics gameObject.)

document.body.style = 'margin:0;';

const TEXT_SHORT = 'Hello World.'.repeat(8);
const TEXT_LONG = TEXT_SHORT.repeat(2);

class Main extends Phaser.Scene {

    constructor() {
        super("Main");
    }

    create() {
        let buttonShortText = this.add.rectangle(10, 10, 80, 40, 0xcdcdcd)
            .setOrigin(0)
            .setInteractive()
            .on('pointerdown', () => this.scene.start('Modal', {
                name: 'SHORT',
                text: TEXT_SHORT,
                padding: 10,
                maxWidth: 200
            }));

        // x,y Position are not relevant since they will be change on align
        let helperLabel = this.add.text(0, 0, 'SHORT').setColor('#000000');

        Phaser.Display.Align.In.Center(helperLabel, buttonShortText);

        let buttonLongText = this.add.rectangle(100, 10, 80, 40, 0xcdcdcd)
            .setOrigin(0)
            .setInteractive()
            .on('pointerdown', () => this.scene.start('Modal', {
                name: 'SHORT',
                text: TEXT_LONG,
                padding: 20,
                maxWidth: 400
            }));

        // x,y Position are not relevant since they will be change on align
        let helperLabel2 = this.add.text(0, 0, 'LONG').setColor('#000000');

        Phaser.Display.Align.In.Center(helperLabel2, buttonLongText);
    }
}

class Modal extends Phaser.Scene {

    constructor() {
        super("Modal");
    }

    init(data) {
        this._name = data.name;
        this._text = data.text + 'nnClick to Close';
        this._maxWidth = data.maxWidth;
        this._padding = data.padding;
    }

    create() {
        this.add.rectangle(0, 0, config.width, config.height, 0xcdcdcd, .5)
            .setOrigin(0)
            .setInteractive()
            .on('pointerdown', () => this.scene.start('Main'));

        // x,y Position are not relevant since they will be change on align
        this.content = this.add.text(0, 0, this._text).setColor('#000000');
        this.content.setWordWrapWidth(this._maxWidth - (2 * this._padding));
        this.content.setDepth(10);

        let bgHeight = this.content.height + (2 * this._padding);

        let graphics = this.make.graphics({ add: false });

        graphics.fillStyle(0xffffff);
        graphics.fillRect(0, 0, this._maxWidth, bgHeight, 0xffffff);

        // Remove the texture is it was generated and has a different size.
        if (this.textures.exists('bgModal')) {
            this.textures.remove('bgModal');
        }

        graphics.generateTexture('bgModal', this._maxWidth, bgHeight);

        this.bgImage = this.add.image(config.width / 2, config.height / 2, 'bgModal');

        // Center Text onto the Background
        Phaser.Display.Align.In.Center(this.content, this.bgImage);
    }
}

var config = {
    type: Phaser.CANVAS,
    width: 536,
    height: 183,
    scene: [Main, Modal]
};

new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js"></script>
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement