Let's say we have a number of related objects (in this case, a few foods) that each have a unique type.
We can type all of them together as a union (Food), and extract their common types as a second different union (FoodType).
It might be nice to have a mapping of each FoodType to its corresponding Food.
Nice!
We can use this map to create a new utility that will access a Food by its FoodType.
Handy!
We might also want to pull a union of Food types that share a certain property. We'll need some more utilities for that one...
We can put that in practice like this.
Right on!
API time. Our Food union would let us do something like this.
Which is nice, because once we add a type...
The argument will be of the corresponding Food interface with that type. Here we've added the "carrot" type, so TypeScript is expecting to receive the length and radius properties, too.
That's a decent API, but we can do better. Let's say we just want to provide the type as an argument, with the rest of the corresponding Food as options.
That works well too.
Even better of those options are optional.
And since we already have the type, we can omit that too.
These utilities can be helpful in plenty of places. In my case (tldraw.com), I'm using the pattern for "shapes". Here I'm getting a shape by its ID, however TypeScript doesn't know what kind of shape it is.
To help it out, I can write a helper that asserts that the shape actually has a certain property—which narrows its type.
Got it!
Anyway, something to try out or maybe come back to later.
• • •
Missing some Tweet in this thread? You can try to
force a refresh
Live cursors are interesting. You don't really want to be constantly sending updates but you do want the cursors to move smoothly and naturally like this. What's the trick?
Here's what it looks like without any animations.
Regular tweens/duration-based animations won't work here because each new update will "redirect" the animation. While the cursor is moving from A to B, a new update may come in that means it should move instead from its current point (C) to point D instead.
perf breakthru on ios, panning and zooming back to 60 FPS
I was over here staring at the performance tab, all signs green, no dropped frames reported—while also watching things obviously skip around at <30 fps. Super frustrating, couldn't figure it out.
Finally I think, maybe I'm not actually updating the state on every frame? Of course I was, but somehow in a way that didn't quite count for Safari.
A few updates to tldraw.com. First off, lots of perf improvements, even when lots of shapes are on the screen. Slickest SVG canvas on the web.
Slick when creating shapes, too.
I'm also handling window resize events much better. There's a slight debounce before showing shapes that had previously fallen outside of the viewport.
Let's look at rotation in a canvas UI. Rotation is hard. 💀
When we have a single shape selected, we can start dragging on a handle here and then rotate the shape. The shape rotates around its center point.
When more than one item is selected, we do something a little different. We're actually making two changes to these shapes as we rotate the group. I'll show each change separately.
This is building on some of the patterns that I used in globs.design: a normal design canvas space and a code editor that can be used to create things on the canvas.
I've added the ability to create controls like these. A control's values may be used in the code; and when you change the control's value, the code will run again with the new value.
Turning brush strokes into polygons is fun, but what does a regular dab-style brush engine look like in the browser? brush-engine.vercel.app
This is a *much* easier problem than creating pressure-sensitive stroke polygons. All we do is repeat a texture (in this case, a circle) at a regular distances along a line, with the pressure (real or simulated) determining the size of the texture.
This is how apps like Photoshop or Procreate work. There's some interpolation between points in order to fill in the blank spaces—and you can use a lot more data to determine the texture's size, skew, opacity, etc—but that's the general idea.