9
\$\begingroup\$

I wrote my Sierpinski Triangle fractal animation using HTML canvas with JavaScript: JsFiddle

What do you think about it? How I can optimize it, if you see a need for that?

Code:

requestAnimationFrame(update);
    
const canvas = document.getElementById("c");
const context = canvas.getContext('2d');

var iterations = 0; //how many iterations to perform in current frame, count of fractal func invokes grows exponentially and can be calculated from sum: E 3^k, k = 0 to iterations
const maxIterations = 8; //used when animating, it's better to set 'gradientAnimation' to false, when setting 'maxIterations' to bigger values 
const center = { x: canvas.width * 0.5, y: canvas.height * 0.5 };
const halfTriangleSide = 250;

//color animation
const gradientAnimation = true;
var t = 0;
var speed = 0.1;

//division animation
var divideAnim = true;
var divide = true; //loop flag
var mod = 0.0; //color modificator

function drawTriangle(A, B, C)
{
    context.beginPath();
    context.moveTo(A.x, A.y);
    context.lineTo(B.x, B.y);
    context.lineTo(C.x, C.y);
    context.lineTo(A.x, A.y);

    if(gradientAnimation)
    {
        context.fillStyle = 
        "rgb(" + 
            ((A.x + A.y) * 0.19 * mod) + "," + 
            ((B.x + B.y) * 0.15 * mod) + "," + 
            ((C.x + C.y) * 0.22 * mod) + 
        ")";
    }
    
    context.fill();
}

function fractal(center, halfSideLen, i)
{
    i++;
    var quaterSide = halfSideLen * 0.5;
    
    //calc new triangle coords
    var A = { 
        x: center.x - quaterSide, 
        y: center.y
    };
    
    var B = {
        x: center.x + quaterSide, 
        y: center.y
    };
    
    var C = {
        x: center.x, 
        y: center.y + halfSideLen   
    };
    
    drawTriangle(A, B, C);
    if(i < iterations)
    {       
        fractal({x: A.x, y: A.y + quaterSide}, quaterSide, i);
        fractal({x: B.x, y: B.y + quaterSide}, quaterSide, i);
        fractal({x: C.x, y: C.y - (halfSideLen + quaterSide)}, quaterSide, i);
    }
}

function update()
{
    context.clearRect(0, 0, canvas.width, canvas.height);
    
    //init coords
    var A = { 
        x: center.x - halfTriangleSide, 
        y: center.y + halfTriangleSide
    };
    
    var B = {
        x: center.x + halfTriangleSide, 
        y: center.y + halfTriangleSide
    };
    
    var C = {
        x: center.x, 
        y: center.y - halfTriangleSide      
    };
    
    context.fillStyle = "#F00";
    drawTriangle(A, B, C);  
    context.fillStyle = "#000";
    
    if(iterations != 0) fractal(center, halfTriangleSide, 0);   
    
    t += speed;
    if(t > Math.PI * 2)
    {           
        t = 0;
    
        if(divideAnim)
        {
            if(iterations == maxIterations) divide = false;
            else if(iterations == 0) divide = true;
            
            if(divide) iterations++;
            else iterations--;
        }
    }
    
    if(gradientAnimation)
    {
        mod = 1.25 - Math.sin(t) * 0.5;
    }
    
    requestAnimationFrame(update);
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Good things:

  • const is used for variables that shouldn't be re-assigned
  • requestAnimationFrame() is used for animating
  • constants and global variables are declared at the top of the script.
  • only three functions are used

Suggestions

  • add a space after control structures like if for readability: this is recommended in many style guides (e.g. AirBnB, Google)
    Instead of

    if(gradientAnimation)
    

    Add a space:

    if (gradientAnimation)
    
  • Also recommended by some popular style guides: avoid var unless a global variable is needed. Default to using const to avoid accidental re-assignment (which seems to be a topic of this discussion on your other JS post). If re-assignment is needed then use let.

  • Use equality operators that don’t coerce types when not needed:

    Instead of:

    if(iterations == maxIterations)
    

    Use ===

     if (iterations === maxIterations)
    
  • A common convention in many languages (including C-based and others) is to have constants that truly never change named using ALL_CAPS - e.g. MAX_ITERATIONS instead of maxIterations, HALF_TRIANGLE_SIDE instead of halfTriangleSide, etc.

  • the lone i++ at the start of fractal() can be moved down to conditional after the call to drawTriangle() and converted to a pre-increment:

        if (++i < iterations)
    
  • Most of the (idiomatic) JS code and JS Style guides I have seen have opening braces on the same line as the function signature or control structure instead of a new line. Perhaps having them on a new line is common in another style guide (e.g. C++, C#, etc.).

\$\endgroup\$
2
  • \$\begingroup\$ In C# brackets starting blocks are always in a new line, maybe that's where it's coming from. \$\endgroup\$
    – potato
    Jul 9, 2020 at 13:50
  • \$\begingroup\$ @potato- thanks for the insight - I have taken it into account as I updated that section \$\endgroup\$ Jul 9, 2020 at 15:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.