jonobr1 / two.js

A renderer agnostic two-dimensional drawing api for the web.
https://two.js.org
MIT License
8.29k stars 454 forks source link

[Bug] stop-opacity not supported for Canvas/Webgl renderers #645

Open przyb opened 2 years ago

przyb commented 2 years ago

Describe the bug I used the built-in SVG interpreter to import my graphics, including linear-gradient strokes. All works well, however, the opacity property available for Stop is being totally ignored by renderers other than Svg.

To Reproduce Steps to reproduce the behavior:

  1. Create LinearGradient
  2. Define stops with hex colors and set stop opacity to a value other than 1
  3. Rendered color will not be affected by the set opacity

Expected behavior Opacity should be taken into consideration or at least a warning that opacity is not supported for a given renderer should be displayed in console. I believe a good solution would be to convert hex color to RGB values and set the opacity in rgba format for addColorStop method calls (in the case of the Canvas renderer). My ad hoc solution was setting the color as rgba string and ignoring the stop-opacity property, but if you use the SVG importer it's very inconvenient as e.g. Figma exports svgs with stop-opacity.

I'd be happy to try to create a PR however there are multiple ways of how to solve this so I'd love to hear from the maintainers what's the preferred solution. IMO keeping colors in a Color object instead of string would be best but would also require quite a huge refactor 😅

jonobr1 commented 2 years ago

Thanks for posting. Unfortunately, I'm not aware of a command that can be sent to Canvas renderings that can control the opacity of a stop (as can be seen here: https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient/addColorStop).

Further, I don't think you can use globalCompositeOperation to single out the styles of one particular stop's opacity. So, the only way I'm aware of applying this in Canvas or WebGL is to set it as an rgba string like so:

var stop = new Two.Stop(0, 'rgba(255, 50, 50, 0.5)');

Would be eager to hear your thoughts on different implementations.

przyb commented 2 years ago

@jonobr1 didn't have time to do deeper research but came to the same conclusion. Anyway, assuming that's the solution to set alpha for a stop, it would be cool if the renderer had converted hex + opacity value to rgba value or at least informed a developer that usage of opacity property is unsupported for renderers other than svg. A caveat is that it would require some additional code to handle conversions, however in a simple way it could be done in a few lines of code so it shouldn't generate a huge overhead. How do you feel about that?

jonobr1 commented 2 years ago

Great points. I definitely should add a caveat to the documentation. I worry that there could be some other bugs or confusion if Two.js tries to handle interpreting the opacity and injecting it into the rgba string, because then I need to add a lot of functionality for converting hex, common phrases (like gray), and rgb/a strings into objects. Which, isn't out of the question, but would take some time (mainly for writing all and confirming all the tests).

Three.js already has this type of functionality in THREE.Color: https://threejs.org/docs/?q=color#api/en/math/Color

jonobr1 commented 2 years ago

Okay, the documentation is updated here: https://two.js.org/docs/effects/stop/#opacity