mattjennings / svelte-pixi

Create PixiJS apps with Svelte
https://svelte-pixi.com
MIT License
112 stars 15 forks source link

Reactive `Application` height and width #31

Closed thismatters closed 1 year ago

thismatters commented 1 year ago

Hi and thanks for your great work!

I'm trying to allow my canvas to resize with the window and haven't had luck with the following:

<Application width={parseInt(1200 * scaling)} height={parseInt(600 * scaling)}>
...
</Application>

Is there some other strategy I should try?

mattjennings commented 1 year ago

<Application width={xyz} height={xyz} /> will translate to doing new PIXI.Application({ width: xyz, height: xyz }). Pixi's API for resizing the canvas is to supply a resizeTo of the HTML element or window you wish to match the size of. The <Application /> component supports this as a prop.

If you wanted to resize the canvas to a specific dimension, you could possibly mutate the application instance's renderer.view.width/height properties - although I am not sure if/how Pixi reacts to changes to those properties. You can access the application instance with getApp from a child component of Application, or bind to the instance prop of Application.

thismatters commented 1 year ago

Thank you! I've gotten the canvas to resize with something like:

<script>
...
let canvasDiv;
let scaling = 0.5;
</script>

<div bind:this={canvasDiv} style="width: {1200 * scaling}px; height: {600 * scaling}px;">
  <Application resizeTo={canvasDiv}>
    ...
  </Application>
</div>

This is working, but I would like to also like to use render="demand" on the Application because there is little animation to speak of, but when I include the render attribute the redraw never ticks when the resize occurs. I'm considering drawing a sprite to the screen when a resize happens to trigger the render, is there a better strategy?

mattjennings commented 1 year ago

Hmm. I suppose you could use the “invalidate” function returned from getRenderer and call it whenever the element would resize? This will have to be done from a child component of Application, however, since “getRenderer” is essentially a getContext().

thismatters commented 1 year ago

Do you think it might be better to forego the Application and instead compose a Renderer and Container? Would that give easier access to invalidate?

I'm also considering just making the canvas refresh with a very low framerate (2 fps; to allow candle flicker and the like), but I cannot really tell how to affect the framerate when using an Application; any hints?

mattjennings commented 1 year ago

I don't think you'd have to. You can manually trigger re-renders on the application instance like so:

<script>
import { onMount} from 'svelte'
import { Application } from 'svelte-pixi'

let app

onMount(() => { 
  app.renderer.render(app.stage)
})
</script>

<Application bind:instance={app} render="demand">
</Application>

So you could do that in an effect on scaling, perhaps:

$: scaling, app?.renderer.render(app.stage)

If you wanted to lower the framerate down to something specific like 2fps, then composing the Renderer with Container would be the way to go as it does not set up any render loop. You can also re-implement the "render on demand" behaviour by referencing how the Application component does it.

However, it'd of course be nicer if you didn't need go to through all that trouble! (It would make sense for render on demands to automatically render on canvas resize, that is something I will look into)

thismatters commented 1 year ago

Thank you for this advice!