From 4b1bdcce2599a471632c84ad716eaf81256f4f6c Mon Sep 17 00:00:00 2001 From: Cosma Alex Vergari Date: Tue, 23 Jun 2026 11:32:37 +0200 Subject: [PATCH] fix: webgl and canvas linear gradient transparency issues --- src/core/shaders/canvas/LinearGradient.ts | 19 +----- src/core/shaders/webgl/LinearGradient.ts | 71 +++++++---------------- 2 files changed, 22 insertions(+), 68 deletions(-) diff --git a/src/core/shaders/canvas/LinearGradient.ts b/src/core/shaders/canvas/LinearGradient.ts index 2cfdd36..8036d6e 100644 --- a/src/core/shaders/canvas/LinearGradient.ts +++ b/src/core/shaders/canvas/LinearGradient.ts @@ -31,24 +31,7 @@ export const LinearGradient: CanvasShaderType< y0: line * Math.sin(angle) + nHeight * 0.5, x1: line * Math.cos(angle + Math.PI) + nWidth * 0.5, y1: line * Math.sin(angle + Math.PI) + nHeight * 0.5, - colors: this.props!.colors.map((value, i, arr) => { - const alpha = (value >>> 24) & 0xff; - if (alpha === 0) { - let nearestRGB = value & 0x00ffffff; - for (let step = 1; step < arr.length; step++) { - if (i - step >= 0 && ((arr[i - step]! >>> 24) & 0xff) > 0) { - nearestRGB = arr[i - step]! & 0x00ffffff; - break; - } - if (i + step < arr.length && ((arr[i + step]! >>> 24) & 0xff) > 0) { - nearestRGB = arr[i + step]! & 0x00ffffff; - break; - } - } - value = (value & 0xff000000) | nearestRGB; - } - return this.toColorString(value); - }), + colors: this.props!.colors.map((value) => this.toColorString(value)), }; }, render(ctx, node, renderContext) { diff --git a/src/core/shaders/webgl/LinearGradient.ts b/src/core/shaders/webgl/LinearGradient.ts index c27fd15..91938ec 100644 --- a/src/core/shaders/webgl/LinearGradient.ts +++ b/src/core/shaders/webgl/LinearGradient.ts @@ -22,51 +22,6 @@ export const LinearGradient: WebGlShaderType = { getCacheMarkers(props: LinearGradientProps) { return `colors:${props.colors.length}`; }, - vertex: ` - # ifdef GL_FRAGMENT_PRECISION_HIGH - precision highp float; - # else - precision mediump float; - # endif - - attribute vec2 a_position; - attribute vec2 a_textureCoords; - attribute vec4 a_color; - - uniform vec2 u_resolution; - uniform float u_pixelRatio; - uniform vec2 u_dimensions; - uniform float u_angle; - - varying vec4 v_color; - varying vec2 v_textureCoords; - varying float v_dist; - - const float PI = 3.14159265359; - - vec2 calcPoint(float d, float angle) { - return d * vec2(cos(angle), sin(angle)) + (u_dimensions * 0.5); - } - - void main() { - vec2 normalized = a_position * u_pixelRatio / u_resolution; - vec2 zero_two = normalized * 2.0; - vec2 clip_space = zero_two - 1.0; - - gl_Position = vec4(clip_space * vec2(1.0, -1.0), 0, 1); - - v_color = a_color; - v_textureCoords = a_textureCoords; - - float a = u_angle; - float lineDist = abs(u_dimensions.x * cos(a)) + abs(u_dimensions.y * sin(a)); - vec2 f = calcPoint(lineDist * 0.5, a); - vec2 t = calcPoint(lineDist * 0.5, a + PI); - vec2 gradVec = t - f; - float dist = dot(a_textureCoords * u_dimensions - f, gradVec) / dot(gradVec, gradVec); - v_dist = dist; - } - `, fragment(renderer: WebGlRenderer, props: LinearGradientProps) { return ` # ifdef GL_FRAGMENT_PRECISION_HIGH @@ -75,16 +30,25 @@ export const LinearGradient: WebGlShaderType = { precision mediump float; # endif + #define PI 3.14159265359 #define MAX_STOPS ${props.colors.length} #define LAST_STOP ${props.colors.length - 1} + uniform float u_alpha; + uniform vec2 u_dimensions; + uniform sampler2D u_texture; + + uniform float u_angle; uniform float u_stops[MAX_STOPS]; uniform vec4 u_colors[MAX_STOPS]; varying vec4 v_color; varying vec2 v_textureCoords; - varying float v_dist; + + vec2 calcPoint(float d, float angle) { + return d * vec2(cos(angle), sin(angle)) + (u_dimensions * 0.5); + } vec4 getGradientColor(float dist) { dist = clamp(dist, 0.0, 1.0); @@ -105,14 +69,21 @@ export const LinearGradient: WebGlShaderType = { return mix(u_colors[i], u_colors[i + 1], lDist); } } - return u_colors[LAST_STOP]; } void main() { vec4 color = texture2D(u_texture, v_textureCoords) * v_color; - vec4 colorOut = getGradientColor(v_dist); - vec3 blendedRGB = mix(color.rgb, colorOut.rgb, clamp(colorOut.a, 0.0, 1.0)); - gl_FragColor = vec4(blendedRGB, color.a); + float a = u_angle; + float lineDist = abs(u_dimensions.x * cos(a)) + abs(u_dimensions.y * sin(a)); + vec2 f = calcPoint(lineDist * 0.5, a); + vec2 t = calcPoint(lineDist * 0.5, a + PI); + // Scale to avoid mediump float overflow on platforms without highp + float s = max(u_dimensions.x, u_dimensions.y); + vec2 gradVec = (t - f) / s; + float dist = dot((v_textureCoords.xy * u_dimensions - f) / s, gradVec) / dot(gradVec, gradVec); + vec4 colorOut = getGradientColor(dist); + color = mix(color, colorOut, clamp(colorOut.a, 0.0, 1.0)); + gl_FragColor = color * u_alpha; } `; },