Agamnentzar / ag-psd

Javascript library for reading and writing PSD files
Other
501 stars 66 forks source link

PSD export svg <linearGradient> get the wrong result #136

Open zhyyangyang opened 1 year ago

zhyyangyang commented 1 year ago

hello, could you tell me how to concat svg gradient data ?

const { colorStops, opacityStops } = vectorFill;
let rnd = Math.floor(Math.random() * 10000);
gradientStr += `<defs><linearGradient x1="0" x2="0" y1="0" y2="1" gradientTransform="rotate(${vectorFill.angle})" id="linearGradient${rnd}">`;
for(let i=0;i<colorStops.length;i++){
    const {location,color} = colorStops[i];
    const { r, g, b } = color; 
    gradientStr += `<stop offset="${
        location * 100
    }%" stop-color="rgb(${Math.floor(r)},${Math.floor(g)},${Math.floor(b)})" 
    />`;
} 
for(let i=0;i<opacityStops.length;i++){
    const {location,opacity} = opacityStops[i];  
    gradientStr += `<stop offset="${
         location * 100
    }%"  stop-opacity="${opacity}" 
    />`;
} 
gradientStr += `</linearGradient> </defs>`;
svgPath += gradientStr;
colorStr = `fill="url(#linearGradient${rnd})"`;
Agamnentzar commented 1 year ago

I don't know that much about SVG but maybe you can't put opacity and color stops like this, check if you get good result with just color stops first. If you provide screenshots of both gradients I could maybe figure something more about the issue.

zhyyangyang commented 1 year ago

svg7

Agamnentzar commented 1 year ago

It seems that angle in SVG goes clockwise but it goes counter-clockwise in Photoshop, so you need to negate it.

You need to merge the stops so you only have one list, each stop with both color and opacity. If the location of color and opacity stops is the same then you just merge them into on stop, but if you have opacity stop without matching color stop you need to interpolate correct color for it by calculating it based on nearest color stops.

zhyyangyang commented 1 year ago

It seems that angle in SVG goes clockwise but it goes counter-clockwise in Photoshop, so you need to negate it.

You need to merge the stops so you only have one list, each stop with both color and opacity. If the location of color and opacity stops is the same then you just merge them into on stop, but if you have opacity stop without matching color stop you need to interpolate correct color for it by calculating it based on nearest color stops.

Could you give me an example of how to perform the calculation?

Agamnentzar commented 1 year ago

You need to find 2 closes points to the one you need to calculate then then just do linear interpolation of all values.

function lerp(v0, v1, t) {
  return v0 + t * (v1 - v0);
}

var prevColorStop = ...
var nextColorStop = ...
var opacityStop = ...
var t = (opacityStop.location - prevColorStop.location) / (nextColorStop.location - prevColorStop.location);
var r = lerp(prevColorStop.r, nextColorStop.r, t);
var g = lerp(prevColorStop.g, nextColorStop.g, t);
var b = lerp(prevColorStop.b, nextColorStop.b, t);
...
zhyyangyang commented 1 year ago

You need to find 2 closes points to the one you need to calculate then then just do linear interpolation of all values.

function lerp(v0, v1, t) {
  return v0 + t * (v1 - v0);
}

var prevColorStop = ...
var nextColorStop = ...
var opacityStop = ...
var t = (opacityStop.location - prevColorStop.location) / (nextColorStop.location - prevColorStop.location);
var r = lerp(prevColorStop.r, nextColorStop.r, t);
var g = lerp(prevColorStop.g, nextColorStop.g, t);
var b = lerp(prevColorStop.b, nextColorStop.b, t);
...

thank you so mush, I get it ~