A javascript adventure
JavaScript widget game?
How feasible and fun is a super portable mini-format JS game? Let's find out!
The idea is based around a single portable <script> tag. It should create the canvas the content
will
be drawn
on. For this we will use currentScript. As a demo let's
create a script tag that adds a canvas element above itself and draws some text to it.
Neat. using currentScript we get a reference to the current script element. Then we create a canvas
element and add it just above our script. Then we get the 2D drawing context for that canvas and use it to draw some
text to it.
Now let's add a render loop.
Using requestAnimationFrame and supplying to it our draw method we get a render loop. Do remember to request another animation frame, otherwise we won't loop.
The next step is adding user input.
And we can get the canvas relative click position by:
cv.addEventListener('pointerdown', (e) => {
	const rect = cv.getBoundingClientRect()
	const x = e.clientX - rect.left
	const y = e.clientY - rect.top
	console.log(x, y)
})
This subtracts the click event's position with the canvas element's position, giving us x, y relative to the top left of the canvas. Thus x and y increases towards the bottom right corner. And using that we can do:
Now let's explore keyboard input
Here we do a small bit of trickery in order to make sure that the canvas can grab input focus and that we give the user some indication that the canvas has focus. Click the canvas and it starts accepting keyboard input. Click outside it or tab away and it stops accepting that input.
cv.setAttribute("tabindex", 0) // This allows the canvas element to be focusable
cv.addEventListener('keydown', (e) => {
	// key handle code
});
// Add a border when the canvas has focus. Remove it when focus is lost: blurred
cv.addEventListener('focus', () => {
	cv.style.outline = '2px solid #2d2d2d'
})
cv.addEventListener('blur', () => {
	cv.style.outline = 'none'
})
This brings us to a quite good place to start building a game. Some helper functions to make drawing graphics easier would be great. But one thing I really think would make the "widget" or game more portable is the ability to change its size. I think that the widget itself should determine aspect ratio, but the final scale should be determined in situ.
Here we set the canvas size to 200x200 and draw to it.
Now we have added the ability to scale the widget by supplying a data-scale attribute to the scale
element. The canvas buffer, the drawing surface, is still 200 by 200 pixels but we have increased the output size to
400 by 400 by scaling it up to 2x. We have also added imageRendering = "pixelated" to ensure that when
the content is scaled we get that pixel graphics sharpness. Do note that when scaling text drawn using
fillText in this manner we can get some artefacts. That is because these fonts might have anti-aliasing,
which we then scale up to become more noticeable. This can maybe be solved using a bitmap font.
Here we have all this put together.
Do note that in order to get the correct click event pixels we need to scale them. We divide the canvas buffer size
with the actual canvas width. This is normally just 1/scale but by calculating it like this we avoid
issues that can come from CSS styles applied to the canvas by the end user.
Here is a finished simple example. An endless runner widget you can copy and paste where you'd like on the web. Some learnings from this is that in order to keep the graphics as crisp pixels you gotta keep to whole numbers. Drawing a square at x=0.5 will give bad results.