-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fabric fails to correctly import SVG gradients #5810
Comments
http://victorblog.com/html5-canvas-gradient-creator/ |
I'm afraid I really stink at linear algebra. Not nearly enough practice in recent years. I think the place to fix this is in src/gradient.class.js, fromElement, just after gradient.gradientTransform is established. The problem is that the coords object has no information regarding the height of the bounding box. This coords object is fully converted away from percentages in _convertPercentUnitsToValues. Without the height, determining the correct rotation of the bounding box is impossible. |
a suggestion: |
I think my earlier assertion that fromElement is the right place to fix this is incorrect now, as it doesn't cover the case in #3665... which is essentially the same problem without the SVG. |
ok i get the geometric problem. Once we do that, we take away the angle property of the decomposition and we redo the matrix with the rest. We use the angle to rotate x1, x2, y1, y2. Indeed a gradient rotated of 90 degree is swapping width with height basically. ( x1 - x2 with y1 - y2 ). May break everything else, so needs to be done with an eye over: |
Well i would say before i need to craft an example with many weird gradients that can fail. |
I'm sorry, but I won't be in a good position to fix this one. Too many moving parts and not enough time. |
no worries at all, i was planning to do it, is complicated and requires hostorical memory on that area. You provided a failing example and fixed the opacity. That is good |
Here's what I think is happening: The colors are mostly right, just that the gradients aren't steep enough. The actual final gradients we want are past the 0% to 100% range. This arises naturally from the Pythagorean theorem. For purposes of the gradient, you're calculating the a^2 part (the x value of the bounding box) but not the b^2 part (the y value). So when the gradient is applied, the gradient box is computed for a shorter length than it should be. When you're past that length, you're effectively past the 100% point and you get locked into one color. This is why I was pointing out that the coords object doesn't have all the information it needs to fix this. |
I think your work on this should be organized into three distinct parts, with unit tests at each part:
For the last part, I'd say simply follow the example from victorblog above: compute the new height and width with trigonometry, then adjust the bounding box's x1, y1, x2, y2 distances from cx and cy as being half the new width and height. |
the problem is the logic in general. Canvas does not have a gradient transform, and i need to decide how the fabric gradientTransform relate to the SVG one ( but import has to work good anyway ). The default gradient space reference is the object bounding box, when the transform has a rotation, the width and the height change meaning. All those examples needs to be duplicated with custom coordinates ( here using the defaults ) percentage based and non, objectBoundingBox or UserSpaceUse. Then there is skew and flip that i did not expect at all. I still need to build more examples combining more features to reverse engineer how they work. on the w3c website things are described, but they use bigger conceptual blocks that i do not know, so i have difficulties understanding how the SVG specs works |
Since I'm dealing with SVG path elements, I hope to create a path element whose gradient is horizontal, and then both translate it and rotate it for Fabric to get the correct rendering where I currently have incorrect gradients now. I'm going to attempt this tonight. |
Victory: with the opacity fix landed, I should be unblocked. |
I'll try to collect more examples and work on much data as possible <svg xmlns="http://www.w3.org/2000/svg" width="760" height="760"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 380 380">
<defs>
<linearGradient id="three_stops_1">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699"/>
<stop offset="100%" style="stop-color: #66cc99;"/>
</linearGradient>
<linearGradient id="three_stops_2">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 0.5"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 0.9"/>
</linearGradient>
<linearGradient id="three_stops_3" gradientTransform="rotate(45)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_4" gradientTransform="rotate(90)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_5" gradientTransform="rotate(90) translate(0.5, 0)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_6" gradientTransform="translate(0.5, 0) rotate(90)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_7" gradientTransform="translate(0, 0.5) rotate(90)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_8" gradientTransform="translate(0, 0.5) scale(2, 1)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_9" gradientTransform="translate(0, 0.5) scale(1, 2)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_10" gradientTransform="translate(0.5, 0.5) scale(-1, 2)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<linearGradient id="three_stops_11" gradientTransform="skewX(30)">
<stop offset="0%" style="stop-color: #ffcc00;"/>
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</linearGradient>
<radialGradient id="radial_1" gradientUnits="objectBoundingBox" r="0.8">
<stop offset="0%" style="stop-color: #ffcc00;" />
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</radialGradient>
<radialGradient id="radial_2" gradientUnits="objectBoundingBox" cx="0.5" cy="0.1" r="0.8">
<stop offset="0%" style="stop-color: #ffcc00;" />
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</radialGradient>
<radialGradient id="radial_3" gradientTransform="rotate(45)" gradientUnits="objectBoundingBox" r="0.8">
<stop offset="0%" style="stop-color: #ffcc00;" />
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</radialGradient>
<radialGradient id="radial_4" gradientTransform="rotate(90)" cx="0.5" cy="0.1" r="0.8">
<stop offset="0%" style="stop-color: #ffcc00;" />
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</radialGradient>
<radialGradient id="radial_5" fx="0.5" fy="0.9" cx="0.5" cy="0.2" r="0.8">
<stop offset="0%" style="stop-color: #ffcc00;" />
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</radialGradient>
<radialGradient id="radial_6" gradientTransform="rotate(30)" fx="0.4" fy="0.7" cx="0.5" cy="0.2" r="0.8">
<stop offset="0%" style="stop-color: #ffcc00;" />
<stop offset="33.3%" style="stop-color: #cc6699; stop-opacity: 1"/>
<stop offset="100%" style="stop-color: #66cc99; stop-opacity: 1"/>
</radialGradient>
<polygon id="base" points="5,5 35,5 35,40 5,40 5,35 30,35 30,10 5,10" />
<rect width="35" height="80" id="base2" />
</defs>
<use href="#base" style="fill: url(#three_stops_1); stroke: black;"/>
<use href="#base" transform="translate(45, 0)" style="fill: url(#three_stops_2); stroke: black;"/>
<use href="#base" fill-opacity="0.5" transform="translate(90, 0)" style="fill: url(#three_stops_2); stroke: black;"/>
<use href="#base" transform="translate(135, 0)" style="fill: url(#three_stops_3); stroke: black;"/>
<polygon transform="translate(0, 45)" points="5,5 80,5 80,40 5,40 5,35 75,35 75,10 5,10" style="fill: url(#three_stops_4); stroke: black;"/>
<use href="#base" transform="translate(90, 45)" style="fill: url(#three_stops_5); stroke: black;"/>
<use href="#base" transform="translate(135, 45)" style="fill: url(#three_stops_6); stroke: black;"/>
<use href="#base" transform="translate(0, 90)" style="fill: url(#three_stops_7); stroke: black;"/>
<use href="#base" transform="translate(45, 90)" style="fill: url(#three_stops_8); stroke: black;"/>
<use href="#base" transform="translate(90, 90)" style="fill: url(#three_stops_9); stroke: black;"/>
<use href="#base" transform="translate(135, 90)" style="fill: url(#three_stops_10); stroke: black;"/>
<use href="#base" transform="translate(0, 135)" style="fill: url(#three_stops_11); stroke: black;"/>
<use href="#base2" transform="translate(185, 5)" style="fill: url(#radial_1); stroke: black;"/>
<use href="#base2" transform="translate(235, 5)" style="fill: url(#radial_2); stroke: black;"/>
<use href="#base2" transform="translate(285, 5)" style="fill: url(#radial_3); stroke: black;"/>
<use href="#base2" transform="translate(335, 5)" style="fill: url(#radial_4); stroke: black;"/>
<use href="#base2" transform="translate(185, 95)" style="fill: url(#radial_5); stroke: black;"/>
<use href="#base2" transform="translate(235, 95)" style="fill: url(#radial_6); stroke: black;"/>
</svg> |
spent LOT of time on this. Got crazy pulled out nothing. |
OK, it's become painfully obvious to me in an hour's experimentation that I don't understand transforms on linear gradients in SVG either, much less canvas. How I thought it worked is clearly wrong, including my bounding box suggestion, and this probably should be listed as unsupported for the foreseeable future. Getting a mental model of this right is going to be a nightmare, and is way too much to ask of Fabric without a professional need for it that can't be worked around. Honestly, I'm not even sure now that there is a bug in Fabric here. Certainly no one should be messing with gradient transforms they don't understand. My sincerest apologies for sending you down the rabbit hole... |
well just a particular combination of transform are parsed bad, most are ok. |
Version
2.4.0
Test Case
http://jsfiddle.net/ajvincent/b3xL0v2g/4/
Information about environment
Mozilla Firefox 68.0, Google Chrome 75.0.3770.142
Steps to reproduce
None.
Expected Behavior
The red, green and blue sides of the triangle should each have a gradient leading to yellow in the center.
Actual Behavior
The red, green, and blue sides of the triangle are solid colors.
Other notes
The text was updated successfully, but these errors were encountered: