PageSourceSearch

https://www.afreektech.com.br/assets/js/mesh-gradient.js

js afreektech.com.br collected 2026-10-09 17:40:42 UTC 17,138 bytes, 431 lines download raw bytes

1/*
2  Mesh gradient do hero da página de Formações.
3  O shader (setup/render) é o código exportado pelo Figma (efeito "Mesh gradient",
4  CodeComponentId_3f03519cd6fb9dd0902b984c6179893f45bedc85_760), sem alterações.
5  Roda via WebGPU. Em navegadores sem WebGPU, o elemento mantém o gradiente CSS de reserva
6  definido em .hero-f__bg (ver formacoes.html).
7
8  Uso: <canvas data-mesh-gradient></canvas> dentro de um elemento com tamanho definido.
9*/
10(function () {
11  'use strict';
12
13  // ---------- Código do Figma (verbatim) ----------
14  function setup(device, frame) {
15    var meshShader = `
16diagnostic(off,derivative_uniformity);
17
18struct Uniforms {
19  values: array<vec4f, 32>,
20};
21
22@group(0) @binding(0) var<uniform> uniforms: Uniforms;
23
24struct VertexInput {
25  @location(0) parameter: vec2f,
26};
27
28struct VertexOutput {
29  @builtin(position) position: vec4f,
30  @location(0) color: vec4f,
31};
32
33fn catmull2(a: vec2f, b: vec2f, c: vec2f, d: vec2f, t: f32) -> vec2f {
34  let t2 = t * t;
35  let t3 = t2 * t;
36  return 0.5 * ((2.0 * b) + (-a + c) * t + (2.0 * a - 5.0 * b + 4.0 * c - d) * t2 + (-a + 3.0 * b - 3.0 * c + d) * t3);
37}
38
39fn catmull4(a: vec4f, b: vec4f, c: vec4f, d: vec4f, t: f32) -> vec4f {
40  let t2 = t * t;
41  let t3 = t2 * t;
42  return 0.5 * ((2.0 * b) + (-a + c) * t + (2.0 * a - 5.0 * b + 4.0 * c - d) * t2 + (-a + 3.0 * b - 3.0 * c + d) * t3);
43}
44
45fn srgbToLinearChannel(value: f32) -> f32 {
46  let low = value / 12.92;
47  let high = pow((value + 0.055) / 1.055, 2.4);
48  return select(high, low, value <= 0.04045);
49}
50
51fn srgbToLinear(color: vec4f) -> vec4f {
52  return vec4f(
53    srgbToLinearChannel(clamp(color.r, 0.0, 1.0)),
54    srgbToLinearChannel(clamp(color.g, 0.0, 1.0)),
55    srgbToLinearChannel(clamp(color.b, 0.0, 1.0)),
56    clamp(color.a, 0.0, 1.0)
57  );
58}
59
60fn segment(value: f32) -> vec2f {
61  let scaled = clamp(value, 0.0, 1.0) * 3.0;
62  let index = min(floor(scaled), 2.0);
63  return vec2f(index, scaled - index);
64}
65
66fn pointIndex(column: i32, row: i32) -> i32 {
67  return row * 4 + column;
68}
69
70fn meshPosition(parameter: vec2f) -> vec2f {
71  let sx = segment(parameter.x);
72  let sy = segment(parameter.y);
73  let ix = i32(sx.x);
74  let iy = i32(sy.x);
75  let x0 = max(ix - 1, 0);
76  let x1 = ix;
77  let x2 = ix + 1;
78  let x3 = min(ix + 2, 3);
79  let y0 = max(iy - 1, 0);
80  let y1 = iy;
81  let y2 = iy + 1;
82  let y3 = min(iy + 2, 3);
83
84  let row0 = catmull2(uniforms.values[pointIndex(x0, y0)].xy, uniforms.values[pointIndex(x1, y0)].xy, uniforms.values[pointIndex(x2, y0)].xy, uniforms.values[pointIndex(x3, y0)].xy, sx.y);
85  let row1 = catmull2(uniforms.values[pointIndex(x0, y1)].xy, uniforms.values[pointIndex(x1, y1)].xy, uniforms.values[pointIndex(x2, y1)].xy, uniforms.values[pointIndex(x3, y1)].xy, sx.y);
86  let row2 = catmull2(uniforms.values[pointIndex(x0, y2)].xy, uniforms.values[pointIndex(x1, y2)].xy, uniforms.values[pointIndex(x2, y2)].xy, uniforms.values[pointIndex(x3, y2)].xy, sx.y);
87  let row3 = catmull2(uniforms.values[pointIndex(x0, y3)].xy, uniforms.values[pointIndex(x1, y3)].xy, uniforms.values[pointIndex(x2, y3)].xy, uniforms.values[pointIndex(x3, y3)].xy, sx.y);
88  return catmull2(row0, row1, row2, row3, sy.y);
89}
90
91fn meshColor(parameter: vec2f) -> vec4f {
92  let sx = segment(parameter.x);
93  let sy = segment(parameter.y);
94  let ix = i32(sx.x);
95  let iy = i32(sy.x);
96  let x0 = max(ix - 1, 0);
97  let x1 = ix;
98  let x2 = ix + 1;
99  let x3 = min(ix + 2, 3);
100  let y0 = max(iy - 1, 0);
101  let y1 = iy;
102  let y2 = iy + 1;
103  let y3 = min(iy + 2, 3);
104  let offset = 16;
105
106  let row0 = catmull4(srgbToLinear(uniforms.values[offset + pointIndex(x0, y0)]), srgbToLinear(uniforms.values[offset + pointIndex(x1, y0)]), srgbToLinear(uniforms.values[offset + pointIndex(x2, y0)]), srgbToLinear(uniforms.values[offset + pointIndex(x3, y0)]), sx.y);
107  let row1 = catmull4(srgbToLinear(uniforms.values[offset + pointIndex(x0, y1)]), srgbToLinear(uniforms.values[offset + pointIndex(x1, y1)]), srgbToLinear(uniforms.values[offset + pointIndex(x2, y1)]), srgbToLinear(uniforms.values[offset + pointIndex(x3, y1)]), sx.y);
108  let row2 = catmull4(srgbToLinear(uniforms.values[offset + pointIndex(x0, y2)]), srgbToLinear(uniforms.values[offset + pointIndex(x1, y2)]), srgbToLinear(uniforms.values[offset + pointIndex(x2, y2)]), srgbToLinear(uniforms.values[offset + pointIndex(x3, y2)]), sx.y);
109  let row3 = catmull4(srgbToLinear(uniforms.values[offset + pointIndex(x0, y3)]), srgbToLinear(uniforms.values[offset + pointIndex(x1, y3)]), srgbToLinear(uniforms.values[offset + pointIndex(x2, y3)]), srgbToLinear(uniforms.values[offset + pointIndex(x3, y3)]), sx.y);
110  return clamp(catmull4(row0, row1, row2, row3, sy.y), vec4f(0.0), vec4f(1.0));
111}
112
113@vertex
114fn vs_main(input: VertexInput) -> VertexOutput {
115  let position = meshPosition(input.parameter);
116  var output: VertexOutput;
117  output.position = vec4f(position.x * 2.0 - 1.0, 1.0 - position.y * 2.0, 0.0, 1.0);
118  output.color = meshColor(input.parameter);
119  return output;
120}
121
122@fragment
123fn fs_main(input: VertexOutput) -> @location(0) vec4f {
124  return input.color;
125}
126`;
127    var resolveShader = `
128diagnostic(off,derivative_uniformity);
129
130@group(0) @binding(0) var source: texture_multisampled_2d<f32>;
131
132struct VertexInput {
133  @location(0) position: vec2f,
134  @location(1) uv: vec2f,
135};
136
137struct VertexOutput {
138  @builtin(position) position: vec4f,
139  @location(0) uv: vec2f,
140};
141
142@vertex
143fn vs_main(input: VertexInput) -> VertexOutput {
144  var output: VertexOutput;
145  output.position = vec4f(input.position, 0.0, 1.0);
146  output.uv = input.uv;
147  return output;
148}
149
150fn linearToSrgbChannel(value: f32) -> f32 {
151  let safeValue = max(value, 0.0);
152  let low = safeValue * 12.92;
153  let high = 1.055 * pow(safeValue, 1.0 / 2.4) - 0.055;
154  return select(high, low, safeValue <= 0.0031308);
155}
156
157fn linearToSrgb(color: vec3f) -> vec3f {
158  return vec3f(
159    linearToSrgbChannel(color.r),
160    linearToSrgbChannel(color.g),
161    linearToSrgbChannel(color.b)
162  );
163}
164
165@fragment
166fn fs_main(input: VertexOutput) -> @location(0) vec4f {
167  let dimensions = textureDimensions(source);
168  let pixel = clamp(vec2i(input.position.xy), vec2i(0), vec2i(dimensions) - vec2i(1));
169  let c0 = textureLoad(source, pixel, 0);
170  let c1 = textureLoad(source, pixel, 1);
171  let c2 = textureLoad(source, pixel, 2);
172  let c3 = textureLoad(source, pixel, 3);
173  let alphaSum = c0.a + c1.a + c2.a + c3.a;
174  let weighted = c0.rgb * c0.a + c1.rgb * c1.a + c2.rgb * c2.a + c3.rgb * c3.a;
175  let linearRgb = weighted / max(alphaSum, 0.000001);
176  let covered = alphaSum > 0.0;
177  let rgb = select(vec3f(0.0), clamp(linearToSrgb(linearRgb), vec3f(0.0), vec3f(1.0)), covered);
178  return vec4f(rgb, alphaSum * 0.25);
179}
180`;
181    frame.state.meshModule = device.createShaderModule({ code: meshShader });
182    frame.state.resolveModule = device.createShaderModule({ code: resolveShader });
183    frame.state.uniformBuffer = device.createBuffer({
184        size: 512,
185        usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
186    });
187    frame.state.quad = device.createBuffer({
188        size: 6 * 4 * 4,
189        usage: GPUBufferUsage.VERTEX,
190        mappedAtCreation: true,
191    });
192    new Float32Array(frame.state.quad.getMappedRange()).set([
193        -1, -1, 0, 1,
194        1, -1, 1, 1,
195        -1, 1, 0, 0,
196        -1, 1, 0, 0,
197        1, -1, 1, 1,
198        1, 1, 1, 0,
199    ]);
200    frame.state.quad.unmap();
201  }
202
203  function render(device, frame) {
204    var clampNumber = function (value, low, high) {
205        return Math.min(Math.max(value, low), high);
206    };
207    var pointNames = [
208        "p00", "p10", "p20", "p30",
209        "p01", "p11", "p21", "p31",
210        "p02", "p12", "p22", "p32",
211        "p03", "p13", "p23", "p33",
212    ];
213    var uniformValues = new Float32Array(128);
214    var index = 0;
215    for (index = 0; index < 16; index += 1) {
216        var point = frame.params[pointNames[index]];
217        uniformValues[index * 4] = clampNumber(point.x, 0, 100) / 100;
218        uniformValues[index * 4 + 1] = clampNumber(point.y, 0, 100) / 100;
219        uniformValues[index * 4 + 2] = 0;
220        uniformValues[index * 4 + 3] = 0;
221        uniformValues[(16 + index) * 4] = clampNumber(point.color.r, 0, 1);
222        uniformValues[(16 + index) * 4 + 1] = clampNumber(point.color.g, 0, 1);
223        uniformValues[(16 + index) * 4 + 2] = clampNumber(point.color.b, 0, 1);
224        uniformValues[(16 + index) * 4 + 3] = clampNumber(point.color.a, 0, 1);
225    }
226    device.queue.writeBuffer(frame.state.uniformBuffer, 0, uniformValues);
227    var tessellation = clampNumber(Math.round(frame.params.tessellation), 8, 256);
228    if (frame.state.tessellation !== tessellation) {
229        if (frame.state.gridVertices) {
230            frame.state.gridVertices.destroy();
231        }
232        if (frame.state.gridIndices) {
233            frame.state.gridIndices.destroy();
234        }
235        var side = tessellation + 1;
236        var vertices = new Float32Array(side * side * 2);
237        var vertexOffset = 0;
238        var row = 0;
239        var column = 0;
240        for (row = 0; row < side; row += 1) {
241            for (column = 0; column < side; column += 1) {
242                vertices[vertexOffset] = column / tessellation;
243                vertices[vertexOffset + 1] = row / tessellation;
244                vertexOffset += 2;
245            }
246        }
247        var indices = new Uint32Array(tessellation * tessellation * 6);
248        var indexOffset = 0;
249        for (row = 0; row < tessellation; row += 1) {
250            for (column = 0; column < tessellation; column += 1) {
251                var topLeft = row * side + column;
252                var topRight = topLeft + 1;
253                var bottomLeft = topLeft + side;
254                var bottomRight = bottomLeft + 1;
255                indices[indexOffset] = topLeft;
256                indices[indexOffset + 1] = bottomLeft;
257                indices[indexOffset + 2] = topRight;
258                indices[indexOffset + 3] = topRight;
259                indices[indexOffset + 4] = bottomLeft;
260                indices[indexOffset + 5] = bottomRight;
261                indexOffset += 6;
262            }
263        }
264        frame.state.gridVertices = device.createBuffer({
265            size: vertices.byteLength,
266            usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
267        });
268        frame.state.gridIndices = device.createBuffer({
269            size: indices.byteLength,
270            usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
271        });
272        device.queue.writeBuffer(frame.state.gridVertices, 0, vertices);
273        device.queue.writeBuffer(frame.state.gridIndices, 0, indices);
274        frame.state.indexCount = indices.length;
275        frame.state.tessellation = tessellation;
276    }
277    if (frame.state.pipelineFormat !== frame.output.format) {
278        frame.state.meshPipeline = device.createRenderPipeline({
279            layout: "auto",
280            vertex: {
281                module: frame.state.meshModule,
282                entryPoint: "vs_main",
283                buffers: [{
284                        arrayStride: 8,
285                        attributes: [{ shaderLocation: 0, format: "float32x2", offset: 0 }],
286                    }],
287            },
288            fragment: {
289                module: frame.state.meshModule,
290                entryPoint: "fs_main",
291                targets: [{ format: frame.output.format }],
292            },
293            primitive: { topology: "triangle-list" },
294            multisample: { count: 4 },
295        });
296        frame.state.resolvePipeline = device.createRenderPipeline({
297            layout: "auto",
298            vertex: {
299                module: frame.state.resolveModule,
300                entryPoint: "vs_main",
301                buffers: [{
302                        arrayStride: 16,
303                        attributes: [
304                            { shaderLocation: 0, format: "float32x2", offset: 0 },
305                            { shaderLocation: 1, format: "float32x2", offset: 8 },
306                        ],
307                    }],
308            },
309            fragment: {
310                module: frame.state.resolveModule,
311                entryPoint: "fs_main",
312                targets: [{ format: frame.output.format }],
313            },
314            primitive: { topology: "triangle-list" },
315        });
316        frame.state.pipelineFormat = frame.output.format;
317    }
318    if (frame.state.msaaWidth !== frame.output.width ||
319        frame.state.msaaHeight !== frame.output.height ||
320        frame.state.msaaFormat !== frame.output.format) {
321        if (frame.state.msaaTexture) {
322            frame.state.msaaTexture.destroy();
323        }
324        frame.state.msaaTexture = device.createTexture({
325            size: [frame.output.width, frame.output.height, 1],
326            format: frame.output.format,
327            sampleCount: 4,
328            usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING,
329        });
330        frame.state.msaaWidth = frame.output.width;
331        frame.state.msaaHeight = frame.output.height;
332        frame.state.msaaFormat = frame.output.format;
333    }
334    var meshBindGroup = device.createBindGroup({
335        layout: frame.state.meshPipeline.getBindGroupLayout(0),
336        entries: [{ binding: 0, resource: { buffer: frame.state.uniformBuffer } }],
337    });
338    var resolveBindGroup = device.createBindGroup({
339        layout: frame.state.resolvePipeline.getBindGroupLayout(0),
340        entries: [{ binding: 0, resource: frame.state.msaaTexture.createView() }],
341    });
342    var encoder = device.createCommandEncoder();
343    var meshPass = encoder.beginRenderPass({
344        colorAttachments: [{
345                view: frame.state.msaaTexture.createView(),
346                loadOp: "clear",
347                clearValue: { r: 0, g: 0, b: 0, a: 0 },
348                storeOp: "store",
349            }],
350    });
351    meshPass.setPipeline(frame.state.meshPipeline);
352    meshPass.setBindGroup(0, meshBindGroup);
353    meshPass.setVertexBuffer(0, frame.state.gridVertices);
354    meshPass.setIndexBuffer(frame.state.gridIndices, "uint32");
355    meshPass.drawIndexed(frame.state.indexCount);
356    meshPass.end();
357    var resolvePass = encoder.beginRenderPass({
358        colorAttachments: [{
359                view: frame.output.createView(),
360                loadOp: "clear",
361                clearValue: { r: 0, g: 0, b: 0, a: 0 },
362                storeOp: "store",
363            }],
364    });
365    resolvePass.setPipeline(frame.state.resolvePipeline);
366    resolvePass.setBindGroup(0, resolveBindGroup);
367    resolvePass.setVertexBuffer(0, frame.state.quad);
368    resolvePass.draw(6);
369    resolvePass.end();
370    device.queue.submit([encoder.finish()]);
371  }
372
373  // ---------- Parâmetros do Figma (node 4978:1413) ----------
374  var NAVY = { r: 0.007843137718737125, g: 0.07450980693101883, b: 0.12156862765550613, a: 1 };
375  var ORANGE = { r: 0.9137254953384399, g: 0.40392157435417175, b: 0.1921568661928177, a: 1 };
376  var LIME = { r: 0.8352941274642944, g: 0.9607843160629272, b: 0.47843137383461, a: 1 };
377  var PARAMS = {
378    p00: { x: 0, y: 0, color: NAVY },
379    p10: { x: 33, y: 0, color: NAVY },
380    p20: { x: 69.08238983154297, y: -2.6197876930236816, color: NAVY },
381    p30: { x: 106.66366577148438, y: -14.106548309326172, color: NAVY },
382    p01: { x: 0, y: 33, color: NAVY },
383    p11: { x: 40.080142974853516, y: 38.441097259521484, color: ORANGE },
384    p21: { x: 60.891639709472656, y: 31.79086685180664, color: LIME },
385    p31: { x: 100, y: 33, color: LIME },
386    p02: { x: 0, y: 67, color: NAVY },
387    p12: { x: 29.717578887939453, y: 54.05290985107422, color: NAVY },
388    p22: { x: 67.97178649902344, y: 53.69953918457031, color: NAVY },
389    p32: { x: 100, y: 67, color: NAVY },
390    p03: { x: 0, y: 100, color: NAVY },
391    p13: { x: 33, y: 100, color: NAVY },
392    p23: { x: 67, y: 100, color: NAVY },
393    p33: { x: 100, y: 100, color: NAVY },
394    tessellation: 168
395  };
396
397  // ---------- Host mínimo: desenha num <canvas> com WebGPU ----------
398  async function mount(canvas) {
399    if (!navigator.gpu) return false;
400    var adapter = await navigator.gpu.requestAdapter();
401    if (!adapter) return false;
402    var device = await adapter.requestDevice();
403    var context = canvas.getContext('webgpu');
404    if (!context) return false;
405    var format = navigator.gpu.getPreferredCanvasFormat();
406    context.configure({ device: device, format: format, alphaMode: 'premultiplied' });
407
408    var frame = { state: {}, params: PARAMS, output: null };
409    setup(device, frame);
410
411    var draw = function () {
412      var dpr = Math.min(window.devicePixelRatio || 1, 2);
413      var w = Math.max(1, Math.round(canvas.clientWidth * dpr));
414      var h = Math.max(1, Math.round(canvas.clientHeight * dpr));
415      if (canvas.width !== w) canvas.width = w;
416      if (canvas.height !== h) canvas.height = h;
417      frame.output = context.getCurrentTexture();
418      render(device, frame);
419    };
420    draw();
421    var t;
422    new ResizeObserver(function () { clearTimeout(t); t = setTimeout(draw, 60); }).observe(canvas);
423    return true;
424  }
425
426  document.querySelectorAll('canvas[data-mesh-gradient]').forEach(function (canvas) {
427    mount(canvas).then(function (ok) {
428      if (ok) canvas.classList.add('is-ready');
429    }).catch(function () { /* fica o gradiente CSS de reserva */ });
430  });
431})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.