4
\$\begingroup\$

For a game, there is some WebGLRenderer object that wraps around canvas element. There is need to update it's size when browser window size changes. Here goes my current code written using BaconJS. What do you think about it ? Would you improve it in any way or maybe do it other way? I have read that listening to resize event is just slow for game purposes.

# Make the property of window object 
rWindow = Bacon.constant window

# Sample window by tick event
sTickWindow = rWindow.sampledBy app.land.stream('tick')

# Look for width and height changes on every tick
sWidth = sTickWindow.map('.innerWidth').skipDuplicates()
sHeight = sTickWindow.map('.innerHeight').skipDuplicates()
sSize = Bacon.combineAsArray sWidth, sHeight

# Resize renderer when size is changed
Bacon.onValues rRenderer, sSize, (renderer, size) ->
    # Apply size update to renderer
    renderer.setSize size...

Note that app.land.stream('tick') is basically EventStream of requestAnimationFrame invocations.

Update

To make the understanding easier, I have made fiddle for it. Open dev console and try to resize the Result window rather then browser window.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I think your code style is fine. If you are happy with this method, I think you've written it pretty well.

As potential upgrades, here are two alternative methods that better obey the Rules of Clarity and Simplicity.

First, is there a reason you're using so much machinery to get the window size? This would be clearer with fewer moving parts. Anyone coming to your codebase knowing only JavaScript/CoffeeScript will understand what is happening here:

# setup canvas
canvas = document.createElement 'canvas'
document.body.appendChild canvas

# update ui every frame
tick = ->
  resize()
  requestAnimationFrame tick
requestAnimationFrame tick

resize = ->  
  # only resize canvas if necessary
  canvas.width = window.innerWidth if canvas.width isnt window.innerWidth
  canvas.height = window.innerHeight if canvas.height isnt window.innerHeight

There's a fiddle of this here.

You could also employ modules that throttle redraws and polyfill requestAnimationFrame like this example. I would argue that this method would also be clearer for a newcomer to your codebase.

raf = require 'raf' # requestAnimationFrame polyfill
throttle = require 'lodash.throttle'

# only redraw every 200 milliseconds
raf().on 'data', throttle(resize, 200)

See it in action (as JavaScript) on RequireBin here.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks for the review. Reason for the machinery is mostly because that sSize is going to be used in more places. I could probably just emit event with new values, however having plain Bacon stream allows me to merge that stream with others more easily. I am using Bacon more extensively because it seems so tidy and efficient to write. Thanks for the tip about raf, that's helpful. \$\endgroup\$
    – FredyC
    Commented May 30, 2014 at 6:58
  • \$\begingroup\$ Another thing I like about my solution is that writing tests is much easier. I can simply replace that rWindow with my mock object and rest goes smoothly. Otherwise I would have to keep property holding window just for the sake of tests which is bad in my opinion. \$\endgroup\$
    – FredyC
    Commented May 30, 2014 at 7:02
  • \$\begingroup\$ I'm all for using tools that make sense to you and (most importantly) work. It sounds like you're doing that. I'm curious: what are you testing that would require holding onto a reference to window? If any of this app is open source, I'd appreciate a repo link! \$\endgroup\$
    – nrw
    Commented May 30, 2014 at 20:22
  • \$\begingroup\$ Well in this case, there using I have to verify that size of renderer is updated accordingly and also I am checking for the output of sSize. Basic stuff really, but having it wrapped like this I can easily replace the result of rWindow without messing with real window object. Game is in development and not in public yet and it's not open source, sorry. \$\endgroup\$
    – FredyC
    Commented May 31, 2014 at 10:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.