Add confetti.shapesFromImage method to create confetti from image source#243
Add confetti.shapesFromImage method to create confetti from image source#243lionel-rowe wants to merge 3 commits intocatdad:masterfrom
Conversation
| // this test renders a black canvas in a headless browser | ||
| // but works fine when it is not headless | ||
| // eslint-disable-next-line ava/no-skip-test |
There was a problem hiding this comment.
The logic of this test is copied from the corresponding emoji one, so I copied this comment over too, even though for me it seemed to render fine in headless.
There was a problem hiding this comment.
Not entirely surprising. That test was written quite a while ago, and I know a lot of work was done on headless mode since. It's possible both tests just work now.
| resolve(sprites.map(function(sprite) { | ||
| var width = size * sprite.width / baselineWidth; | ||
| var height = size * sprite.height / baselineWidth; | ||
| var scale = 1 / scalar; | ||
|
|
||
| var canvas = new OffscreenCanvas(width, height); | ||
| canvas.getContext('2d').drawImage(img, sprite.x, sprite.y, sprite.width, sprite.height, 0, 0, width, height); | ||
|
|
||
| return { | ||
| type: 'bitmap', | ||
| bitmap: canvas.transferToImageBitmap(), | ||
| matrix: [scale, 0, 0, scale, -width * scale / 2, -height * scale / 2] | ||
| }; | ||
| })); |
There was a problem hiding this comment.
Okay, so some info about the original sprite issue, since I know I didn't document much in the original tasks.
Once upon a time, there was the crazy idea of rendering emoji. This was pretty incompatible with how rectangles and circles are rendered -- since those are point-based, we can calculate where the points would exist if a 3D transformation was applied, and then do very single (read: cheap) 2D objects with silly points. However, at the time, neither text nor images seemed like they were going to be that simple. They are not point based, so I can't just manipulate their points. At the time, the only method I could find render emoji was to use transforms on the canvas, so rather than applying a 3D effect on the confetti, the 3D effect is applied to the canvas and the confetti was drawn normally. This is incredibly expensive and performance was abysmal, so I did not want to put in in production.
I know that in game rendering (at least back in the day), it was not unheard of to have a sprite for any given asset, and that sprite is many many different 2D images of that resource from all possible angles. This means any time you need to render that asset, you just pick the correct angle out of the sprite -- super cheap. So by saying "support sprites", what I meant was support a way to pre-render the entire animation sequence for a single piece of confetti, and then render each frame as a portion of that sprite.
That is all a long-winded way (sorry) of saying: sprites here complicate the API without delivering any benefit. I would not support them... at least not this version. A user just as well could create multiple shapes from multiple separate images (or honestly, just create the shape in a loop and apply cropping each time if for some reason they must load a sprite image of completely separate objects).
| const spritesheetSvg = `<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> | ||
| <defs><polygon id="bow" points="0,0 0,25 30,0 30,25"/></defs> | ||
| <use href="#bow" x="0" y="0" fill="red"/> | ||
| <use href="#bow" x="35" y="0" fill="cyan"/> | ||
| <use href="#bow" x="0" y="30" fill="orange"/> | ||
| <use href="#bow" x="35" y="30" fill="fuchsia"/> | ||
| </svg>`; |
There was a problem hiding this comment.
This may be a moot point given my other comment about sprites, but I think this is a bad thing to demonstrate. SVG paths perform better than images (and text), so when you can you are far better off using shapeFromPath when at all possible. This demonstration looks like an endorsement to use SVGs as images.
| function shapesFromImage(imageData) { | ||
| var scalar = imageData.scalar != null ? imageData.scalar : 1; | ||
| var size = 10 * scalar; | ||
|
|
||
| var img = new Image(); | ||
| img.src = imageData.src; | ||
|
|
||
| return promise(function (resolve) { |
There was a problem hiding this comment.
Okay, so I know why you did this. It is so convenient for this lib to just load the image for you, even from a URL. I've seen this backfire a lot in libraries though, from folks who don't totally understand how image loading works thinking that the library has a bug when their images are not served as expected.
Not to mention, the image here doesn't include error handling (because why not add two problems to one comment, am I right?)
Anyway, I think a much more flexible API would be shapeFromCanvas. Have the user load anything they want into a canvas (could be an image, or you could draw anything in code using the canvas API), and let this lib just create the bitmap, scaling, other shape stuff.
catdad
left a comment
There was a problem hiding this comment.
Thanks for contributing!
Overall, I like the idea of adding general image support. Check out the comments though, I have some suggestions on how to improve this feature.
Closes #80.
Also fixes #117 (if I've understood that issue correctly?), which was marked "not planned", but this also supports spritesheets.
This is a modified version of my
shapeFromImagefunction from #80 (comment), along with docs/tests/examples.