2020-10-01
This is a blog where the posts are little JavaScripts that give me pleasure, and hopefully to you too.
window.w = x => document.write(x);
w('Hello, World!')
It would be nice if I could build on my previous work as I'm advancing the blog... Hm, I actually can.
w("Dependencies between posts...")
Embedding HTML into JS strings won't always be fun. I'll add support for HTML snippets to the blog engine.
<em>Huh</em> that's better now.</em>
Some syntax highlighting would be nice, but I don't want to bloat the framework... I can actually do it in a post right here!
<script src="/hljs/highlight.pack.js"></script>
<link rel="stylesheet" href="/hljs/styles/a11y-light.css">
<style>
.hljs { padding-left: 30px; }
</style>
hljs.initHighlightingOnLoad()
2020-10-02
Now something more exciting...
<canvas id="curling" width=600 height=300></canvas>
with(Math) with(curling) with(getContext('2d')) { const
r = 11,
ell = (...$) => { beginPath(); ellipse(...$); stroke() },
dof = (x, vx, μ=-1e-4) => (dt=1, bump=0) =>
x += dt * (vx += vx*bump + dt*sign(vx)*μ),
stone = (x, y, φ) => () =>
ell(x(), y(), r-1, r-1, φ(), 0, 6),
clamp = (d, lo, hi) => () => {
const x = d();
return x < lo || x > hi ? d(0,-2) : x },
X = (...$) => clamp(dof(...$), r, width - r),
Y = (...$) => clamp(dof(...$), r, height - r),
a = stone(X(20, 0.8), Y(10, 1.5), dof(0, 0.04));
setInterval(() => { clearRect(0, 0, width, height);
a()}, 16)
}
I've tried using some kind of functions which serve as components: the function returned from stone(), and the DOFs (degree of freedom). Actually, the DOF is an intriguing abstraction that I just came up with, so I hope I can explore it further.
I couldn't correctly implement friction and collisions that involve several DOFs at once - for this reason, I will need some kind of interaction between the DOFs. And I'd like to do it on a higher level - one behavior (collision on wall (clamp here), collision between stones, friction) as one separate component.
2020-10-03
What is a component? What's so great about React, Svelte, Vue and the like?
Encapsulation of
behavior (how output depends on input)
state (how consecutive inputs influence further outputs)
Structure
composition (a component can use other components' output as output)
slots/children (a component can take other components' output as input)
Event handling
different paths of input
In a typical component system, a component has
a main output (e.g. JSX elements in React)
a main input (props)
optionally an internal state
an event handling mechanism for handling changes in the main input (re-rendering)
an event system (event handlers, event triggering, bubbling)
a way to pass input to non-direct child components (context)
What would be a good way for writing a component in JavaScript?
Let's build a component system for the HTML canvas, for the purpose of the above Curling game. The main output of graphical components will be drawing method calls of the 2D context:
function stone(r, x, y, φ) {
return {
draw(ctx) {
ctx.beginPath();
ctx.ellipse(x, y, r, r, φ, 0, 6);
ctx.stroke();
}
}
}
window.cn = 0;
window.draw = (W, H, comp) => {
w(`<canvas id=c${++cn} width=${W} height=${H}></canvas>`)
comp.draw(window[`c${cn}`].getContext('2d'))
}
draw(600, 80, stone(10, 50, 20, 2))
For the DOF components, the main output is a number. And for some syntactic fun, we'll use JavaScript's valueOf method for outputing it.
function dof(x) {
return {
valueOf() {
return x;
}
}
}
w(+dof(4))
We need speed and animation. For that, I will use a tick method:
window.dof = function dof(x, v) {
return {
tick(dt) {
x += v * dt
},
valueOf() {
return x;
}
}
}
w(`<span id=currentX></span>`)
const x = dof(1000, 1)
setInterval(() => currentX.textContent = +x, 10)
setInterval(() => x.tick(1), 10)
And I will use that tick method for the graphical component too.
function animation(children) {
return {
draw(ctx) {
setInterval(() => {
children.forEach(c => c.tick(1))
children.forEach(c => c.draw(ctx))
}, 10)
}
}
}
function stone(r, x, y, φ) {
const dofs = [x,y,φ]
return {
tick(dt) {
dofs.forEach(dof => dof.tick(dt))
},
draw(ctx) {
ctx.beginPath();
ctx.ellipse(+x, +y, r, r, +φ, 0, 6);
ctx.stroke();
}
}
}
draw(600, 50, animation([
stone(10, dof(20, 1), dof(30, 0.2), dof(2, 0.02))
]))
To be continued...
2020-10-04
Just so I can test my codes in the blog.
window.test = (a, b) => {
a = JSON.stringify(a); b = JSON.stringify(b)
if (a !== b) alert(`${a} ≠ ${b}`)
}
test(5 + 5, 10)
<iteration style="--a: 1; --b: 0"><fib><fab><fib><fab><fib><fab><fib><fab><fib><fab><fib><fab><fib><fab><fib><fab><fib><fab><fib><fab><style>
iteration::before { content: var(--a);}fib { --a2: calc(var(--a) + var(--b)); --b2: var(--a); counter-reset: x var(--a);}fib::before { content: counter(x) ' ';}fab { --a: var(--a2); --b: var(--b2);}</style>