jonobr1 / two.js

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

[Bug] RadialGradient assigned to Path.fill will result in a solid color #725

Closed Carlo-C2 closed 2 months ago

Carlo-C2 commented 2 months ago

Describe the bug A RadialGradient assigned to Path.fill no longer works correctly since 0.8.0 version. A circle with a solid color will be displayed instead of a radial gradient with fade out.

To Reproduce Steps to reproduce the behavior:

  1. Copy-Paste the following code in a test.html file (or copy paste the contents of the script section)

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Two.js Radial Gradient Circle</title>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }
        #draw-shapes {
            display: block;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/two.js/0.8.3/two.min.js"></script>
    </head>
    <body>
    <script>
    // Create an instance of Two.js and append it to the document
    const elem = document.body;
    const params = { width: 500, height: 500 };
    const two = new Two(params).appendTo(elem);
    
    // Create a circle
    const circle = two.makeCircle(params.width / 2, params.height / 2, 100);
    
    // Create a radial gradient with reverse colors
    const gradient = two.makeRadialGradient(
        0, 0, 100,  // x, y, radius
        new Two.Stop(0, 'grey'),
        new Two.Stop(1, 'white')
    );
    
    // Apply the gradient as the fill of the circle
    circle.fill = gradient;
    circle.stroke = 'black'; // Optional: Add a stroke to the circle
    circle.linewidth = 2;    // Optional: Set the width of the stroke
    
    // Update the Two.js instance
    two.update();
    </script>
    </body>
    </html>
  2. Open test.html in any browser
  3. If you like to test the working version, simply change the two.js version in script src (line 15) from 0.8.3 to 0.7.9

Expected behavior It should appear a Circle with a radial fade out instead of a solid grey

Screenshots 0.8.3 image

0.7.9 image

Desktop:

EDIT: I admit I've quickly read the changelog and the release notes, which could be the reason why I didn't read anything that should have broken this functionality. TypeScript claims Path.fill can accept string|Gradient|Texture but if that's not the case, please point me in the right direction. Thanks in advance.

jonobr1 commented 2 months ago

Sorry for the delay on this. As of v0.7.13 the default unit space for gradients changed from screen space to object space. This means that all values are written in 0 - 1 notation as a percentage instead of projected world space pixels. So your demo would need to be like this instead:

    // Create a radial gradient with reverse colors
    const cx = 0.5; // where 0 is the left side of the object and 1 is the right most
    const cy = 0.5; // where 0 is the top side of the object and 1 is the bottom
    const r = 0.5; // where 0 is nothing and 1 is the minimum of the width or height of the object
    const gradient = two.makeRadialGradient(
        0.5, 0.5, 0.5,  // x, y, radius
        new Two.Stop(0, 'grey'),
        new Two.Stop(1, 'white')
    );
jonobr1 commented 2 months ago

This being said, there was a discrepancy between how the percentage of gradients were applied between SVG and Canvas renderers. I just patched this so they operate the same.

Alternatively, you can switch the unit space that the gradient is applied in like so:

gradient.units = 'userSpaceOnUse'; // Now all values are read as pixels
Carlo-C2 commented 2 months ago

Thanks for the thorough reply, no worries for the delay. I'll evaluate what fits best, but I'll close this issue regardless since you both patched it and provided a solution with the previous version