Daily Records (日常记录)

website·Indexed 29 days ago

https://zhicheng99.github.io/k/

A collection of technical notes and tutorials covering WebGL (rendering points, triangles, and quads), JavaScript (Promises, CommonJS, UMD, and DOM event handling), and Element UI dynamic table headers.

Tokens
17.4K
Snippets
64
Records
69
Agent score
75%

What's inside Daily Records

  1. Understand the difference between CommonJS and ES Module bindings

    A fundamental difference between CommonJS (CJS) and ES Modules (ESM) is how they handle exported values. CJS exports a value copy (snapshot); if the original value changes after the export, the consumer still holds the old value. ESM uses live bindings (references); if the exported value changes in the source module, the consumer sees the updated value immediately.
    // CommonJS: Exports a snapshot of the value
    let count = 0;
    setTimeout(() => count = 1, 100);
    module.exports = { count };
    // Consumer receives { count: 0 }, it will never become 1
    
    // ESM: Exports a live binding
    export let count = 0;
    setTimeout(() => count = 1, 100);
    // Consumer using 'import { count }' will see count become 1 after 100ms
  2. Use CommonJS (CJS) for Node.js server-side modules

    CommonJS is a synchronous module specification designed for server-side environments (Node.js). It uses require() to load modules and module.exports to expose functionality. Key characteristics include synchronous loading, runtime resolution, value copying (shallow copy), and singleton caching (modules are executed once and cached).
    // math.js — Define module
    const add = (a, b) => a + b;
    const sub = (a, b) => a - b;
    
    module.exports = { add, sub };
    // Alternatively: exports.add = add; exports.sub = sub;
    
    // main.js — Consume module
    const math = require('./math');
    console.log(math.add(1, 2)); // 3
    
    // Or using destructuring
    const { add, sub } = require('./math');
  3. Configure vertex attribute layout with vertexAttribPointer

    WebGL buffers are raw byte arrays with no inherent structure. gl.vertexAttribPointer is used to tell WebGL how to interpret these bytes as specific attribute data (e.g., x, y coordinates).
    gl.vertexAttribPointer(
        a_Position, // location: The attribute variable in the shader
        2,          // size: Number of components per vertex (e.g., 2 for x, y)
        gl.FLOAT,   // type: Data type of each component
        false,      // normalized: Whether integer data should be normalized
        0,          // stride: Byte offset between consecutive vertices (0 = tightly packed)
        0           // offset: Byte offset of the first component in the buffer
    );
  4. Initialize WebGL context and viewport

    The initWebGL function sets up the WebGL rendering context, handles high-DPI displays by adjusting the canvas width and height based on window.devicePixelRatio, and configures the initial viewport and clear color.
    function initWebGL(canvas) {
      const dpr = window.devicePixelRatio || 1;
      canvas.width = 400 * dpr;
      canvas.height = 300 * dpr;
      canvas.style.width = '400px';
      canvas.style.height = '300px';
    
      const gl = canvas.getContext('webgl');
      if (!gl) {
        console.error('Failed to get WebGL context');
        return null;
      }
      gl.viewport(0, 0, canvas.width, canvas.height);
      gl.clearColor(0, 0, 0, 1);
      return gl;
    }
  5. Initialize WebGL shaders and programs

    To render in WebGL, you must create a vertex shader and a fragment shader, compile them, and link them into a GPU program. The initShaders helper function automates the process of creating the program and making it the current active program using gl.useProgram.
    function initShaders(gl, vshader, fshader) {
      var program = createProgram(gl, vshader, fshader);
      if (!program) {
        console.log('Failed to create program');
        return false;
      }
      gl.useProgram(program);
      gl.program = program;
      return true;
    }
    
    function createProgram(gl, vshader, fshader) {
      var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
      var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
      if (!vertexShader || !fragmentShader) return null;
    
      var program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
    
      if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
        console.log('Failed to link program: ' + gl.getProgramInfoLog(program));
        gl.deleteProgram(program);
        return null;
      }
      return program;
    }
    
    function loadShader(gl, type, source) {
      var shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.log('Failed to compile shader: ' + gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
      }
      return shader;
    }
  6. Pass attribute values to WebGL shaders

    To send data to a shader's attribute variables, use gl.getAttribLocation to find the variable's index in the compiled program, then use functions like gl.vertexAttrib3f (for 3 floats) or gl.vertexAttrib1f (for 1 float) to assign values.
    // Vertex Shader Source
    var VSHADER_SOURCE = 
    'attribute vec4 a_Position;\n'+
    'attribute float a_PointSize;\n'+
    'void main(){\n'+
    ' gl_Position = a_Position;\n'+
    ' gl_PointSize = a_PointSize;\n'+
    '}\n';
    
    // 1. Get attribute locations
    var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    var a_PointSize = gl.getAttribLocation(gl.program, 'a_PointSize');
    
    // 2. Pass values to the attributes
    gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
    gl.vertexAttrib1f(a_PointSize, 5.0);
    
    // 3. Draw the point
    gl.drawArrays(gl.POINTS, 0, 1);
  7. Compile and link WebGL shaders

    The process of creating a WebGL program involves loading shader source code, compiling the vertex and fragment shaders, and linking them into a program object. The initShaders helper function automates this and makes the program current using gl.useProgram.
    function initShaders(gl, vshader, fshader) {
      var program = createProgram(gl, vshader, fshader);
      if (!program) {
        console.log('Failed to create program');
        return false;
      }
      gl.useProgram(program);
      gl.program = program;
      return true;
    }
    
    function createProgram(gl, vshader, fshader) {
      var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
      var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
      if (!vertexShader || !fragmentShader) return null;
    
      var program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
    
      var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
      if (!linked) {
        gl.deleteProgram(program);
        return null;
      }
      return program;
    }
    
    function loadShader(gl, type, source) {
      var shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
      if (!compiled) {
        gl.deleteShader(shader);
        return null;
      }
      return shader;
    }
  8. Initialize WebGL shaders and programs

    To render graphics, you must compile a vertex shader and a fragment shader, link them into a WebGL program, and activate that program using gl.useProgram.
    function initShaders(gl, vshader, fshader) {
      var program = createProgram(gl, vshader, fshader);
      if (!program) return false;
      gl.useProgram(program);
      gl.program = program;
      return true;
    }
    
    function createProgram(gl, vshader, fshader) {
      var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
      var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
      var program = gl.createProgram();
      gl.attachShader(program, vertexShader);
      gl.attachShader(program, fragmentShader);
      gl.linkProgram(program);
      return program;
    }
    
    function loadShader(gl, type, source) {
      var shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      return shader;
    }
  9. Pass vertex data to WebGL using buffer objects

    To pass multiple vertices to a vertex shader, follow these five steps:

    1. Create a buffer object using gl.createBuffer().
    2. Bind the buffer to a target (e.g., gl.ARRAY_BUFFER) using gl.bindBuffer().
    3. Write data to the buffer using gl.bufferData().
    4. Assign the buffer to an attribute variable using gl.vertexAttribPointer().
    5. Enable the attribute variable using gl.enableVertexAttribArray().
    function initVertexBuffers(gl) {
        var vertices = new Float32Array([
            0.0, 0.5, 
            -0.5, -0.5, 
            0.5, -0.5
        ]);
    
        // 1. Create buffer
        var vertexBuffer = gl.createBuffer();
        
        // 2. Bind buffer
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    
        // 3. Write data
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    
        // 4. Assign to attribute
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    
        // 5. Enable attribute
        gl.enableVertexAttribArray(a_Position);
    
        return 3; // number of vertices
    }
  10. Pass rotation uniforms to a WebGL program

    To apply rotation, convert the angle from degrees to radians, calculate the cosine and sine values, and upload them to the GPU using gl.uniform1f after the program has been made current with gl.useProgram.
    var ANGLE = 90.0;
    var radian = Math.PI * ANGLE / 180.0;
    var cosB = Math.cos(radian);
    var sinB = Math.sin(radian);
    
    // Get locations of uniforms defined in the shader
    var u_CosB = gl.getUniformLocation(gl.program, 'u_CosB');
    var u_SinB = gl.getUniformLocation(gl.program, 'u_SinB');
    
    // Set the uniform values
    gl.uniform1f(u_CosB, cosB);
    gl.uniform1f(u_SinB, sinB);
  11. Convert screen coordinates to WebGL NDC

    WebGL uses Normalized Device Coordinates (NDC) where the screen center is (0,0) and edges are -1 to 1. To convert a mouse click event (clientX, clientY) to NDC, you must account for the canvas bounding rectangle and the device pixel ratio (DPR).
    function getWebGLCoord(canvas, clientX, clientY) {
      const dpr = window.devicePixelRatio || 1;
      const rect = canvas.getBoundingClientRect();
    
      // CSS pixels to physical pixels
      const x = (clientX - rect.left) * dpr;
      const y = (clientY - rect.top) * dpr;
    
      // Physical pixels to WebGL NDC (-1 to 1)
      return [
        (x / canvas.width) * 2 - 1,
        -((y / canvas.height) * 2 - 1)
      ];
    }
  12. Pass vertex data to shaders using Buffer Objects

    Passing multiple vertices to a vertex shader requires a five-step process using WebGL buffer objects:

    1. Create a buffer object (gl.createBuffer()).
    2. Bind the buffer to a target (gl.bindBuffer()), typically gl.ARRAY_BUFFER for vertex data.
    3. Write data to the bound buffer (gl.bufferData()).
    4. Assign the buffer to an attribute variable (gl.vertexAttribPointer()).
    5. Enable the attribute variable (gl.enableVertexAttribArray()).
    function initVertexBuffers(gl) {
        var vertices = new Float32Array([
            -0.5, 0.5, // v0
             0.5, 0.5, // v1
            -0.5, -0.5, // v2
             0.5, -0.5  // v3
        ]);
        var n = 4;
    
        // 1. Create buffer
        var vertexBuffer = gl.createBuffer();
        
        // 2. Bind buffer
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    
        // 3. Write data
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    
        // Get attribute location from the program
        var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    
        // 4. Assign buffer to attribute (size=2, type=FLOAT)
        gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    
        // 5. Enable attribute
        gl.enableVertexAttribArray(a_Position);
    
        return n;
    }