Skip to content
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

svg gradient issue: gradientTransform is ignored #1554

Closed
afdev82 opened this issue Aug 1, 2014 · 36 comments · Fixed by #1557
Closed

svg gradient issue: gradientTransform is ignored #1554

afdev82 opened this issue Aug 1, 2014 · 36 comments · Fixed by #1557

Comments

@afdev82
Copy link

afdev82 commented Aug 1, 2014

When I try to import the following svg, it seems that the "gradientTransform" is ignored.

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 2834.6 2834.6" enable-background="new 0 0 2834.6 2834.6" xml:space="preserve">
<g id="Effect">
  <rect opacity="1.0" fill="#B6C0C6" width="2834.6" height="2834.6"/>
  <radialGradient id="SVGID_2_" cx="458.6157" cy="1137.1204" r="644.0953" fx="494.8075" fy="1386.5354" gradientTransform="matrix(3.321 -0.6998 0.4077 1.9347 -440.9168 -408.0598)" gradientUnits="userSpaceOnUse">
    <stop  offset="0.3301" style="stop-color:#FFFFFF;stop-opacity:0.9"/>
    <stop  offset="0.4549" style="stop-color:#F6F6F6;stop-opacity:0.8"/>
    <stop  offset="0.8525" style="stop-color:#FFFFFF;stop-opacity:0.1"/>
  </radialGradient>
  <rect fill="url(#SVGID_2_)" width="2834.6" height="2834.6"/>
</g>
</svg>

I'm using the latest version of fabric.

@kangax
Copy link
Member

kangax commented Aug 3, 2014

/cc @Kienz @asturur

@asturur
Copy link
Member

asturur commented Aug 3, 2014

this confirms.
image
Taking a look at.

@asturur
Copy link
Member

asturur commented Aug 3, 2014

it can be supported easly, transforming the canvas during the fill. But as of now, i have to tweak the transform matrix to the translate point part. I think because of the various translation that fabric does with objects.
So for sure the matrix has to be parsed , used, then it has to be evaluated if is better to transform the gradient coordinates ( if is possible to replicate the matrix effect just appling it on the gradients coordinates ) or if another canvas transform has to be added in the rendering queue ( with additional save and restore if needed )
image

@afdev82
Copy link
Author

afdev82 commented Aug 4, 2014

I wonder if I can achieve the same result directly with fabric, without having to import a svg, it would be better. Can I fill a rect with the gradient and then apply the same transformation matrix specified in the svg to the rect?

@asturur
Copy link
Member

asturur commented Aug 4, 2014

No for two reason:

  1. you would distort the rect, not just the gradient.
  2. As of now, if you create a rect and with a transforMatrix in the options, fabric does a big mess
    And that is the reason why importing svgs without grouping often result in a big mess.

@afdev82
Copy link
Author

afdev82 commented Aug 5, 2014

  1. Yes, bu for my application should be enough. I have one rect with the gradient and then I can clip to a rect after the transformation.
  2. Yes, you're right. I tried and I noticed some strange effects. Even with the identity matrix there is a shift when I add the object to a group. Is the same issue of Matrix Transformation CenterPoint Moves #1559 ? I tried to offset this effect, but it seems to depend on the transformation and I didn't find the right way to handle this.
    I need these effects, can you provide a "temporary patch" please? I see that in your second image you managed to get the effect in fabric.
    Thanks ;)
    Example : http://jsfiddle.net/gA93y/
    If I comment the line 10 the rect is in the right position (I used the version 1.4.0, because the latest version doesn't work in jsfiddle, it returns "fabric is not defined")

@asturur
Copy link
Member

asturur commented Aug 5, 2014

with 1.4.10 is even worse.
If i manage to get the svg export pulled i will check this gradient transform, i already did the starting piece here #1557 at least to bring the matrix inside the class.

@afdev82
Copy link
Author

afdev82 commented Aug 5, 2014

Thank you very much.
I saw your work, I don't know so much fabric source, but if I could help you in some way, let me know.

@afdev82
Copy link
Author

afdev82 commented Aug 7, 2014

Great! Now does fabric import the svg correctly?

@asturur
Copy link
Member

asturur commented Aug 7, 2014

No we just have the transform inside the class at least.
So it is imported correctly but not rendered correctly.
Can you send me some other of those distorted gradients?

@afdev82
Copy link
Author

afdev82 commented Aug 8, 2014

Yes, as I thought. So I wonder why @kangax closed this issue :P

I have this gradient with a transformation filled in a path.

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 2834.6 2834.6" enable-background="new 0 0 2834.6 2834.6" xml:space="preserve">
<g id="Side_Glow_Effect_Glas_RIGHT">
  <rect fill="#000" width="2834.6" height="2834.6" />
    <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-1030.5988" y1="-4.5913" x2="411.2924" y2="7.9919" gradientTransform="matrix(-1 0 0 -1 1811.5063 1419.2163)">
        <stop  offset="0" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
        <stop  offset="4.070239e-02" style="stop-color:#FFFFFF;stop-opacity:0"/>
    </linearGradient>
    <path opacity="0.9" fill="url(#SVGID_4_)" d="M2829.8,2829.8c0,0,0-1373.6,0-2824.5l-1417.4,0v2824.5H2829.8z"/>
</g>
</svg>

I have also another gradient without transformation, filled in a path that it doesn't render correctly, but I noticed that if I change the shape to a rect, I see it (almost the same gradient).

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 2834.6 2834.6" enable-background="new 0 0 2834.6 2834.6" xml:space="preserve">
<g id="Side_Glow_Effect_Glas_LEFT">
  <rect fill="#000" width="2834.6" height="2834.6" />
  <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-112.0861" y1="1343.9581" x2="1539.774" y2="1488.4772">
    <stop  offset="0" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
    <stop  offset="0.1087" style="stop-color:#FFFFFF;stop-opacity:0"/>
  </linearGradient>
    <path opacity="0.6" fill="url(#SVGID_3_)" d="M5.2,3.7c0,0,0,1374,0,2825h1417.4V3.7H5.2z"/>
</g>
</svg>

I have also another "complex" example, with more than one gradient.
example_2

<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 240.2 240.4" enable-background="new 0 0 240.2 240.4" xml:space="preserve">
<g id="LICHT">
    <g id="Licht_x5F_SQUARES_x5F_KOPIE">
        <rect id="Lighting_Basic" y="0" fill="#FFF" width="240.4" height="240.4"/>
        <rect id="Lighting_Color_4100_K" y="0" opacity="0.5" fill="#DEDC00" stroke="#C6C6C6" stroke-miterlimit="10" enable-background="new    " width="240.4" height="240.4"/>

        <linearGradient id="Verlauf_INSIDE_re_x5F_li_1_" gradientUnits="userSpaceOnUse" x1="114.4676" y1="843.3639" x2="89.6676" y2="843.3639" gradientTransform="matrix(2.0998 0 0 -2.0998 0 1891.0662)">
            <stop  offset="0" style="stop-color:#D7C2B2;stop-opacity:0.9"/>
            <stop  offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
        </linearGradient>
        <rect id="Verlauf_INSIDE_re_x5F_li" x="188.1" y="0" opacity="0.6" fill="url(#Verlauf_INSIDE_re_x5F_li_1_)" enable-background="new    " width="52.1" height="240.4"/>

        <linearGradient id="Gradient_EDGE_re_x5F_li_1_" gradientUnits="userSpaceOnUse" x1="424.4971" y1="187.0762" x2="408.5717" y2="187.0762" gradientTransform="matrix(-2.0998 0 0 2.0998 891.3391 -272.6037)">
            <stop  offset="0" style="stop-color:#D7C2B2;stop-opacity:0.8"/>
            <stop  offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
        </linearGradient>
        <rect id="Gradient_EDGE_re_x5F_li" x="0" y="0" fill="url(#Gradient_EDGE_re_x5F_li_1_)" width="33.4" height="240.4"/>

        <radialGradient id="Lighting_Glow_EDGE_1_" cx="97.9174" cy="-80.5952" r="16.2727" gradientTransform="matrix(2.932935e-10 7.1992 0.3158 -1.562464e-11 37.7916 -584.609)" gradientUnits="userSpaceOnUse">
            <stop  offset="0" style="stop-color:#FFFFFF"/>
            <stop  offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
        </radialGradient>
        <rect id="Lighting_Glow_EDGE" x="6.1" y="0" fill="url(#Lighting_Glow_EDGE_1_)" width="12.6" height="240.6"/>

        <linearGradient id="Gradient_up_1_" gradientUnits="userSpaceOnUse" x1="601.3246" y1="668.3452" x2="592.3494" y2="668.6586" gradientTransform="matrix(-9.427262e-11 -2.0998 -2.0998 9.427262e-11 1523.8982 1258.5059)">
            <stop  offset="0" style="stop-color:#D7C2B2;stop-opacity:0.7"/>
            <stop  offset="0.5655" style="stop-color:#FFFFFF;stop-opacity:0"/>
        </linearGradient>
        <rect id="Gradient_up" y="0" fill="url(#Gradient_up_1_)" width="240.4" height="10.5"/>
    </g>
</g>
</svg>

Thanks for your help!

@asturur
Copy link
Member

asturur commented Aug 8, 2014

I'm on it, i don't know if i can fix it anyway.
i see the spec are simple for the SVG, but i don't know if they can be applied to canvas and gabric.js structure.
Is a very rare feature, you are the first bringing it out.

@afdev82
Copy link
Author

afdev82 commented Aug 8, 2014

Thank you very much.
I hope you will be able to fix it ;)
I didn't know it was a rare feature, these gradients are generated with Illustrator and I think that can happen to have some transformations.
I tried to achieve the same effects directly in fabric (only the first gradient), but it was too complicated, as I explained in a previous comment.

@asturur
Copy link
Member

asturur commented Aug 8, 2014

@afdev82
Just a notice:
I don't know what you need to do with those SVG.
But if JUST need to render them, scale, rotate, do whatever you can do with an image class, and you don't need to access the inner properties of the elements of svg... just use the image class

       fabric.Image.fromURL('impossible.svg', function(oImg) {
        canvas.add(oImg);
    });

And you are perfectly fine.
P.S. in that case you have to be sure that svg has width and height properties set.

image

@afdev82
Copy link
Author

afdev82 commented Aug 8, 2014

Yes, I need only to render the svg.
I use a svg because I need to scale the image without losing quality, but only the first time I add it to the canvas (it's not interactive).
If I could achieve this just with the image class it would be great!

@asturur
Copy link
Member

asturur commented Aug 8, 2014

I don't know, i got this idea, please try and report even to us.
Manipulating gradient transformation would be nice, but it may take long.
Help yourself faster :)

@afdev82
Copy link
Author

afdev82 commented Aug 8, 2014

Yes, it works!
If I assign to the image the desired dimensions, it scales without losing quality.
On the contrary, if I import the image and assign scaleX or scaleY it loses quality.
It should be enough for my application, thank you very much! 👍

@asturur
Copy link
Member

asturur commented Aug 8, 2014

Ok so better you load it with very high width and height, and then you scale it down.
Be aware that on export in SVG the canvas, as of now the image will be referenced as an external resource.
If you need embedded in svg you will need to modify something.

@afdev82
Copy link
Author

afdev82 commented Aug 11, 2014

Probably I will need to export the canvas as PNG on nodejs. I hope that this will work.
Thank you again for your help ;)

@afdev82
Copy link
Author

afdev82 commented Aug 11, 2014

I noticed one issue with the import.
I can't set the opacity of the imported image.
If I change the opacity in the svg file, it works as expected.
For my application I can "solve" duplicating the files and change the opacity in the svg source, but it would be nice to change it within fabric, avoiding to have different requests.

@asturur
Copy link
Member

asturur commented Aug 11, 2014

Can you be more specific?
What is not working?
Did you build fabric.js from source? because distribuited file is some
commit older.

2014-08-11 11:22 GMT+02:00 Antonio Facciolo notifications@github.com:

I noticed one issue with the import.
I can't set the opacity of the imported image.
If I change the opacity in the svg file, it works as expected.


Reply to this email directly or view it on GitHub
#1554 (comment).

@afdev82
Copy link
Author

afdev82 commented Aug 11, 2014

No, I'm still using the version 1.4.10.

I have this svg:

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 2834.6 2834.6" enable-background="new 0 0 2834.6 2834.6" xml:space="preserve" width="2834.6" height="2834.6">
<g id="Side_Glow_Effect_Glas_RIGHT">
    <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-1030.5988" y1="-4.5913" x2="411.2924" y2="7.9919" gradientTransform="matrix(-1 0 0 -1 1811.5063 1419.2163)">
        <stop  offset="0" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
        <stop  offset="4.070239e-02" style="stop-color:#FFFFFF;stop-opacity:0"/>
    </linearGradient>
    <path opacity="1" fill="url(#SVGID_4_)" d="M2829.8,2829.8c0,0,0-1373.6,0-2824.5l-1417.4,0v2824.5H2829.8z"/>
</g>
</svg>

I imported the svg as you suggested and everything was fine with the gradient.
This morning I tried to change the opacity of the imported image in the callback function, but it didn't work. I see a different opacity value in the fabric object, but it renders the same of the svg file.
I "solved" using two files with a different opacity of the path, but of course, it's not a real solution :P

@asturur
Copy link
Member

asturur commented Aug 11, 2014

Build the latest, it should have opacity fixed for images also.
"node build.js modules=ALL exclude=cufon,json,gestures" ( exclude what you
want, nothing eventually)

2014-08-11 12:33 GMT+02:00 Antonio Facciolo notifications@github.com:

No, I'm still using the version 1.4.10.

I have this svg:






I imported the svg as you suggested and everything was fine with the
gradient.
This morning I tried to change the opacity of the imported image in the
callback function, but it didn't work. I see a different value in the
object, but it renders the same.
I "solved" using two files with a different opacity of the path, but of
course, it's not a real solution :P


Reply to this email directly or view it on GitHub
#1554 (comment).

@afdev82
Copy link
Author

afdev82 commented Aug 11, 2014

I tried with the latest version (built from source), but it doesn't work.
I tried the same thing with a png (to change the opacity of the imported image in the callback function) and it works.
It seems not to work with svg files imported using the "fromURL" method of the Image class.

@asturur
Copy link
Member

asturur commented Aug 17, 2014

@afdev82 would you try now to build from source and import and export those
gtadient effects as svgs? just to know if now they work as expected.
Il 11/ago/2014 12:59 "Antonio Facciolo" notifications@github.com ha
scritto:

I tried with the latest version (built from source), but it doesn't work.
I tried the same thing with a png (to change the opacity of the imported
image in the callback function) and it works.
It seems not to work with svg files imported using the "fromURL" method of
the Image class.


Reply to this email directly or view it on GitHub
#1554 (comment).

@afdev82
Copy link
Author

afdev82 commented Aug 18, 2014

Great! The first gradient and the "complex" one work as espected. Also the svg export looks correct.
The second gradient (it should be on the right of the square) doesn't render in the right position in the canvas, but when I export it in svg, it's right. I rewrite its svg code below:

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 2834.6 2834.6" enable-background="new 0 0 2834.6 2834.6" xml:space="preserve">
<g id="Side_Glow_Effect_Glas_RIGHT">
  <rect fill="#000" width="2834.6" height="2834.6" />
    <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-1030.5988" y1="-4.5913" x2="411.2924" y2="7.9919" gradientTransform="matrix(-1 0 0 -1 1811.5063 1419.2163)">
        <stop  offset="0" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
        <stop  offset="4.070239e-02" style="stop-color:#FFFFFF;stop-opacity:0"/>
    </linearGradient>
    <path opacity="0.9" fill="url(#SVGID_4_)" d="M2829.8,2829.8c0,0,0-1373.6,0-2824.5l-1417.4,0v2824.5H2829.8z"/>
</g>
</svg>

Also the following gradient (it should be on the left of the square) renders correctly in the svg exported file, but not in the canvas (I don't see it at all). Perhaps is it a positioning issue?

<svg version="1.0" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 2834.6 2834.6" enable-background="new 0 0 2834.6 2834.6" xml:space="preserve">
<g id="Side_Glow_Effect_Glas_LEFT">
  <rect fill="#000" width="2834.6" height="2834.6" />
  <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-112.0861" y1="1343.9581" x2="1539.774" y2="1488.4772">
    <stop  offset="0" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
    <stop  offset="0.1087" style="stop-color:#FFFFFF;stop-opacity:0"/>
  </linearGradient>
    <path opacity="0.6" fill="url(#SVGID_3_)" d="M5.2,3.7c0,0,0,1374,0,2825h1417.4V3.7H5.2z"/>
</g>
</svg>

Anyway, it's a great job! ;)

@spearsear
Copy link

I also have the requirement to fill a rect with radialgradient with transformGradient. I have the definition of the svg radialgradient defined in an object like this:

{center: {x: 0.5, y:0.5},
 focal: {x: 0.4, y: 0.3},
 radius: 0.3,
 transform: {
   rotate: 0,
   translate: {x: 0.02,y: 0.21},
   scale: {x: 1,y: 1}
},
opacity: 0.6,
stops: [
  {offset: '0', color: "rgb(255,255,255)", opacity: 1},
  {offset: '1', color: "rgb(0,0,0)", opacity: 0}
] 
}

I use fabric.js to dynamicly create radialgradient and fill the rectangle like this:

var rg = {
    type: 'radial',
    x1: center.x,
    y1: center.y,
    r1: 0,
    x2: focal.x,
    y2: focal.y,
    r2: radius * w,

    colorStops: (function(){
        var color_stops = {};
        for(var i=0;i<stops.length;i++){
            color_stops[stops[i].offset] = stops[i].color;
        }
        return color_stops;
    })()
}
var rect = new fabric.Rect({left: x,
    top: y,
    width: w,
    height: h,
    opacity: opacity
});

rect.setGradient('fill', rg);

My question is how to create the gradient with what is specified in gradientTransform?

I tried transform center.x,center.y and focal.x and focal.y and use the transformed center and focal to create the radialgradient object (rg), it worked somehow, but the gradient lost its shape. In svg, after gradientTransform, it looked somehow like an ellipse, now in fabric/canvas, it looked like a circle.

Can I create a radialGradient in fabric with a transform matrix?

btw, I also don't understand why canvas createRadialGradient need 2 radius (r1 and r2 parameter), but svg radialgradient only has one radius.

I also noticed canvg also failed to render radialgradient correctly, see issue3 at following link(radialgradient broken)

http://canvg.googlecode.com/svn/trunk/examples/index.htm

Or use the following svg to see the rendered radialgradient:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="640" height="480">
 <defs>
<radialGradient id="shadow" cx="0.5" cy="0.5" r="0.3" fx="0.4" fy="0.3" spreadMethod="pad"  gradientTransform="rotate(0,0.5,0.5) translate(0.02,0.21) scale(1,1)">
  <stop offset="0" stop-color="rgb(255,255,255)" stop-opacity="1"></stop>
  <stop offset="1" stop-color="rgb(0,0,0)" stop-opacity="0"></stop>
</radialGradient>
 </defs>
 <g>
  <title>Layer 1</title>
  <rect fill="url(#shadow)" height="100" width="180" y="107" x="109"/>
 </g>
</svg>

Unfortunately, the solution to load the svg as image does not work for me. I am rendering the canvas from a non svg data structure.

Please help.

@asturur
Copy link
Member

asturur commented Jul 30, 2015

gradient in canvas requires two radius, while svg just one. This is like that.

FabricJs support gradient transformation trough gradient.transformMatrix attribute.

transform: {
   rotate: 0,
   translate: {x: 0,y: 0},
   scale: {x: 1,y: 1}
},

This is an identity transformation, it should not get elliptical.
Anyway try like this:

var rg = {
    type: 'radial',
    x1: center.x,
    y1: center.y,
    r1: 0,
    x2: focal.x,
    y2: focal.y,
    r2: radius * w,
    transformMatrix: [1,0,0,2,0,0],
    colorStops: (function(){
        var color_stops = {};
        for(var i=0;i<stops.length;i++){
            color_stops[stops[i].offset] = stops[i].color;
        }
        return color_stops;
    })()
}

and it should get elliptical on y axis.
(now i do not really remember if this is trasnformMatrix or gradientTransform attribute sorry.

@asturur
Copy link
Member

asturur commented Jul 30, 2015

in fabricjs there is a function that take this string:

"rotate(0,0.5,0.5) translate(0.02,0.21) scale(1,1)"

and gives you correct matrix.
Search for it in parser.js file.
It is called "parseTransformAttribute" or something similar.

@spearsear
Copy link

seems to be ignoring both gradientTransform and transformMatrix. I am using fabric 1.5.0

@asturur
Copy link
Member

asturur commented Jul 30, 2015

please prepare a jsfiddle i will look into it.

2015-07-30 19:40 GMT+02:00 spearsear notifications@github.com:

seems to be ignoring both gradientTransform and transformMatrix. I am
using fabric 1.5.0


Reply to this email directly or view it on GitHub
#1554 (comment).

@spearsear
Copy link

Pls see the jsfiddle, thanks 😜

https://jsfiddle.net/2v0es4xh/25/

@asturur
Copy link
Member

asturur commented Jul 31, 2015

The problem is that gradientTransform is not evenly supported in all library.
setGradient does not consider it.

After setGradient do this:

your_object.fill.gradientTransform = [a,b,c,d,e,f];

a bug.

@spearsear
Copy link

Yes, this does it, thanks

@asturur
Copy link
Member

asturur commented Jul 31, 2015

i will fix the bug anyway.

@byi-team
Copy link

byi-team commented Aug 2, 2022

Hi @asturur

I did start using the GradientTransform property but whenever I apply the color with stop colors to the respective SVG Path, Gradient gets distorted. Can you help me with what I am doing wrong?

I have set up a demo here. https://onlinelogomaker.indiaondesk.com/gradeintdemo/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants