Sunday, 11 February 2024

Web Tutorial: Valentine's Day jQuery Animation (Part 2/2)

Welcome back, and let's get back to creating the transition() method!

We begin by declaring contents as an array of all elements styled using the CSS class square_contents. And imgURL as the string returned by getRandomImage().
transition: function(ms, squareSize)
{
  var contents = $(".square_contents");
  var imgUrl = this.getRandomImage();

}


animationType and sequenceType are defined as random numbers between 0 and 3 and 5, respectively.
transition: function(ms, squareSize)
{
  var contents = $(".square_contents");
  var imgUrl = this.getRandomImage();

  var animationType = Math.floor(Math.random() * 3);
  var sequenceType = Math.floor(Math.random() * 5);

}


We then want to iterate through contents using a For loop. We could do a Foreach loop, but we'll be using the value of i, so...
transition: function(ms, squareSize)
{
  var contents = $(".square_contents");
  var imgUrl = this.getRandomImage();

  var animationType = Math.floor(Math.random() * 3);
  var sequenceType = Math.floor(Math.random() * 5);

  for(var i = 0; i < contents.length; i++)
  {

  }

}


Each element in contents will have a data-row and data-col attribute. Define row and col so that they take on those values.
for(var i = 0; i < contents.length; i++)
{
  var row = $(contents[i]).attr("data-row");
  var col = $(contents[i]).attr("data-col");

}


Then use a Switch statement on animationType. Let's set the default case first.
for(var i = 0; i < contents.length; i++)
{
  var row = $(contents[i]).attr("data-row");
  var col = $(contents[i]).attr("data-col");

  switch (animationType)
  {
    default:
  }

}


Now we will declare transitionTime as a random integer between 0 and ms.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
}


We then declare sequenceTime and set its value using the getSequenceTime() method, passing in the values of i, transitionTime and sequenceType as arguments.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);
}


We will, of course, have to declare the getSequenceTime() method. It will have the parameters index, ms and type.
getRandomImage: function()
{
  var index = Math.floor(Math.random() * 9);

  return "'img" + index + ".jpg'";
},
getSequenceTime: function(index, ms, type)
{

},   
   
fillContainer: function()
{


In here, we use a Switch statement on type. Again, we declare the default case first. Since we're returning a value in every case, we don't need break statements. In this case, we return a formula based on index and ms. Basically, the higher the value of index, the greater the returned value. Adding ms to the end is just a way of randomizing the result.
getRandomImage: function()
{
  var index = Math.floor(Math.random() * 9);

  return "'img" + index + ".jpg'";
},
getSequenceTime: function(index, ms, type)
{
  switch (type)
  {
    default: return (index * 1.5) + ms;
  }

},      
fillContainer: function()
{


Where were we? Ah, yes. Inside the transition() method, fulfilling the first (and only, so far) case for animationType. We will run the animate() method on the current element of contents. Make sure to put that element in a jQuery wrapper so that animate() can be run on it.
switch (animationType)
{
  default:
  var transitionTime = Math.floor(Math.random() * ms);
  var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

  $(contents[i])
  .animate()

}


We want to animate the opacity attribute, to 0.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

    $(contents[i])
    .animate(
       {
          opacity: 0
       }
    )
}


We want it to use sequenceTime as the duration of the animation.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime
    )
}


Once the animation is done, we want to replace the background with the next image, represented by imgUrl. We then manipulate the background-position property using the data-col and data-row attributes of the current element, along with squareSize. squareSize also determines the width and height. We will also make sure the opacity property is still at 0.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime,
      function() {
        $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
    )
}


Once that's done, we want a slight delay. I recommend double ms.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime,
      function() {
        $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
     )
    .delay(ms * 2)
}


And after the delay, we want to animate the opacity property back to full opacity.
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime,
      function() {
        $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
    )
    .delay(ms * 2)
    .animate(
      {
        opacity: 1
      },
      sequenceTime
    );
}


Don't forget the break statement!
switch (animationType)
{
  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);

    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime,
      function() {
      $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
    )
    .delay(ms * 2)
    .animate(
      {
        opacity: 1
      },
      sequenceTime
    );

    break;
}


So now, we first see the image appear.



Then it starts fading away in bits starting from the top.



Then it starts appearing again, in bits, from the top.



And you see that it's a different picture that is being assembled.



Viola!



More animations!

Now that you've set one up, let's experiment with more. The first one was merely fidding with the opacity property. Let's try something else! Make a copy of the code in the transition() method, and paste it as the first case, like so.
switch (animationType)
{
  case 0:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);  
    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime,
      function() {
        $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
    )
    .delay(ms * 2)
    .animate(
      {
        opacity: 1
      },
      sequenceTime
    );

    break;

  default:
    var transitionTime = Math.floor(Math.random() * ms);
    var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);  
    $(contents[i])
    .animate(
      {
        opacity: 0
      },
      sequenceTime,
      function() {
        $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
    )
    .delay(ms * 2)
    .animate(
      {
        opacity: 1
      },
      sequenceTime
    );

    break;
}


Then let's work on the default case. We want to alter the size this time. So in the first object inside the first call to animate(), we specify that width and height are both set to 0 pixels. In the second call to animate(), we want to set width and height back to squareSize pixels.
default:
  var transitionTime = Math.floor(Math.random() * ms);
  var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);  
  $(contents[i])
  .animate(
    {
      width: "0px",
      height: "0px"
    },
    sequenceTime,
    function() {
      $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:0;width:" + squareSize + "px;height:" + squareSize + "px")
      }
   )
   .delay(ms * 2)
   .animate(
    {
      width: squareSize + "px",
      height: squareSize + "px"
    },
    sequenceTime
  );

  break;


And we need to make sure that at the part where the picture changes, opacity is full (because it's not opacity we're fiddling with this time) and width and height are both 0 pixels.
default:
  var transitionTime = Math.floor(Math.random() * ms);
  var sequenceTime = this.getSequenceTime(i, transitionTime, sequenceType);  
  $(contents[i])
  .animate(
  {
    width: "0px",
    height: "0px"
  },
  sequenceTime,
  function() {
    $(this).attr("style", "background-image:url(" + imgUrl + ");background-position:-" + ($(this).attr("data-col") * squareSize) + "px -" + ($(this).attr("data-row") * squareSize) + "px;opacity:1;width:0px;height:0px")
  }
  )
  .delay(ms * 2)
  .animate(
    {
      width: squareSize + "px",
      height: squareSize + "px"
    },
    sequenceTime
  );

  break;


We might want to do this for the time being, to ensure that the first case never gets triggered.
var animationType = Math.floor(Math.random() * 3) + 10;
var sequenceType = Math.floor(Math.random() * 5);


Again, the picture animates from the top, the squares all growing smaller...



...till it disappears...



...and reappears from the top.



Gradually, all the way down.



Till it forms another, different picture.



Right, so there's another animation. But what if you don't want it to always start from the top? Well, we've made a provision for it, so go to your getSequenceTime() method. Make a copy of the default case, and set it as the case for value 0.
getSequenceTime: function(index, ms, type)
{
  switch (type)
  {
    case 0: return (index * 1.5) + ms;
    default: return (index * 1.5) + ms;
  }
},


Then change the formula to this.
getSequenceTime: function(index, ms, type)
{
  switch (type)
  {
    case 0: return (index * 1.5) + ms;
    default: return ((index % 10) * 10) + ms;
  }
},


Again, for testing purposes, change the value of sequenceType in the transition() method so that the Switch statement in getSequenceTime() always hits the default case, which is currently anything but 0.
var animationType = Math.floor(Math.random() * 3) + 10;
var sequenceType = Math.floor(Math.random() * 5) + 10;


You see it starts off like this.



And the squares animate almost spread evenly.



They start reappearing, again almost evenly.



Until they almost form a picture...



..and there it is!



Now let's remove the temp stuff we did.
var animationType = Math.floor(Math.random() * 3); // + 10;
var sequenceType = Math.floor(Math.random() * 5); // + 10;


In the startTransition() method, we make sure that transition() is also called every ms milliseconds. Now you should have a varied animation sequence, depending on random animation and sequence type combinations! Of course, you may went to build on it as I have here.
startTransition: function(ms)
{
  this.transition(ms / 10, this.squareSize);

  setInterval(
    ()=>{
      this.transition(ms / 10, this.squareSize);
    },
    ms
  );
},


In your heart of hearts,
T___T

Thursday, 8 February 2024

Web Tutorial: Valentine's Day jQuery Animation (Part 1/2)

Hi and welcome, lovebirds!

Valentine's Day is coming up, and I'd like to play around with some jQuery animation. There will be no color animations, so no links to jQuery UI are required. Our objective today is to take a series of AI-generated Valentine-themed images, and transition through them randomly using a fancy animation. Sound good?

These are the images. Each is 500 by 750 pixels.

img0.jpg

img1.jpg

img2.jpg

img3.jpg

img4.jpg

img5.jpg

img6.jpg

img7.jpg

img8.jpg

img9.jpg

Here's some starting HTML with a link to jQuery. Background of the screen has been set to white.
<!DOCTYPE html>
<html>
  <head>
  <title>Happy Valentine's Day!</title>

  <style>
    body
    {
      background-color: rgb(255, 255, 255);
    }
  </style>

  <script src="https://code.jquery.com/jquery-3.7.0.js"></script>

  <script>

  </script>
  </head>

  <body>

  </body>
</html>


We will need a container div inside the HTML. Its id will be container. We will also add the images to the HTML for pre-loading.
<body>
  <div id="container">

  </div>

  <img src="img0.jpg" />
  <img src="img1.jpg" />
  <img src="img2.jpg" />
  <img src="img3.jpg" />
  <img src="img4.jpg" />
  <img src="img5.jpg" />
  <img src="img6.jpg" />
  <img src="img7.jpg" />
  <img src="img8.jpg" />
  <img src="img9.jpg" />

</body>


Here's the CSS. Width and height are the same as the image, and we use the margin property to align it to the middle of the screen. img tags are set to not display.
<style>
  body
  {
    background-color: rgb(255, 255, 255);
  }

  #container
  {
    width: 500px;
    height: 750px;
    margin: 10px auto 0 auto;
  }

  img
  {
    display: none;
  }

</style>


We want to fill the grid with smaller squares. So set the page to run the fillContainer() method of the vday object using the onload attribute.
<body onload="vday.fillContainer();">


And create the vday object in the script tag, with the fillContainer() method. While you're there, also create maxTransitionTime and squareSize properties. These are configuration settings that can be tweaked for performance.
<script>
  let vday =
  {
    maxTransitionTime: 10000,
    squareSize: 25,      
    fillContainer: function()
    {

    }
  }
</script>


We start by declaring the string imgURL, and calling getRandomImage() to assign a value to it.
fillContainer: function()
{
  var imgUrl = this.getRandomImage();
}


We'll then define the method getRandomImage(). All it really does is declare index as a random number between 0 and 9, and then return a string based on index. So it will be any one of the image file names we have.
<script>
  let vday =
  {
    maxTransitionTime: 10000,
    squareSize: 25,  
    getRandomImage: function()
    {
      var index = Math.floor(Math.random() * 9);

      return "'img" + index + ".jpg'";
    },
    fillContainer: function()
    {
      var imgUrl = this.getRandomImage();
    }
  }
</script>


We create a For loop to traverse through the number of rows in the container div, which is basically the height of container divided by squareSize. So what's going to happen here is that we create row as a div, style it using the CSS class row and set the style to have the height property at squareSize pixels. Append it inside container.
fillContainer: function()
{
  var imgUrl = this.getRandomImage();

  for(var i = 0; i < 750 / this.squareSize; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");
    row.attr("style", "height:" + this.squareSize + "px");

    $("#container").append(row);
  }
}


Here's the CSS class row. Full width, and a red outline temporarily, for visibility.
#container
{
  width: 500px;
  height: 750px;
  margin: 10px auto 0 auto;
}

.row
{
  width: 100%;
  outline: 1px solid red;
}


img
{
  display: none;
}


So far so good...



Now we are going to fill in squares. Have another For loop within the first one, this time traversing the number of squares per row. This will be the width of container (500 pixels) divided by squareSize.
fillContainer: function()
{
  var imgUrl = this.getRandomImage();

  for(var i = 0; i < 750 / this.squareSize; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");
    row.attr("style", "height:" + this.squareSize + "px");

    for(var j = 0; j < 500 / this.squareSize; j++)
    {

    }

    $("#container").append(row);
  }
}


In here, create a div, style it using the square CSS class, and make sure both height and width are squareSize pixels. And then append it to row.
for(var j = 0; j < 500 / this.squareSize; j++)
{
  var square = $("<div></div>");
  square.addClass("square");
  square.attr("style", "height:" + this.squareSize + "px;width:" + this.squareSize + "px");

  row.append(square);

}


In the CSS, square is floated left and outline set to a solid red.
.row
{
  width: 100%;
  outline: 1px solid red;
}

.square
{
  float: left;
  outline: 1px solid red;
}


img
{
  display: none;
}


And here's a nice grid!



Next, we fill in those square divs using another div, styled with the CSS class square_contents.
for(var j = 0; j < 500 / this.squareSize; j++)
{
  var square = $("<div></div>");
  square.addClass("square");
  square.attr("style", "height:" + this.squareSize + "px;width:" + this.squareSize + "px");

  var content = $("<div></div>");
  content.addClass("square_contents");

  square.append(content);

  row.append(square);
}


Here's the CSS. It's mostly background-related CSS. You'll see why in a bit.
.square
{
  float: left;
  outline: 1px solid red;
}

.square_contents
{
  background-repeat: no-repeat;
  background-size: 500px 750px;
  margin: 0 auto 0 auto;
}


img
{
  display: none;
}


Back to the For loop... add in data-row and data-col values using i and j. Those will be useful later.
for(var j = 0; j < 500 / this.squareSize; j++)
{
  var square = $("<div></div>");
  square.addClass("square");
  square.attr("style", "height:" + this.squareSize + "px;width:" + this.squareSize + "px");

  var content = $("<div></div>");
  content.addClass("square_contents");
  content.attr("data-row", i);
  content.attr("data-col", j);


  square.append(content);
  row.append(square);
}


And then load the current image (represented by imgURL) in the background-image property. Use background-position to ensure that the background of each square div's content is offset by the appropriate number of pixels, using the values of i, j and squareSize.
for(var j = 0; j < 500 / this.squareSize; j++)
{
  var square = $("<div></div>");
  square.addClass("square");
  square.attr("style", "height:" + this.squareSize + "px;width:" + this.squareSize + "px");

  var content = $("<div></div>");
  content.addClass("square_contents");
  content.attr("data-row", i);
  content.attr("data-col", j);
  content.attr("style", "background-image:url(" + imgUrl + ");background-position:-" + (j * this.squareSize) + "px -" + (i * this.squareSize) + "px;opacity:1;width:" +  this.squareSize + "px;height:" + this.squareSize + "px");

  square.append(content);
  row.append(square);
}


And you see the image! Each square has its image offset, but put it all together, it still forms a coherent picture!



Now, let's remove the red lines. We won't be needing them.
.row
{
  width: 100%;
  outline: 0px solid red;
}

.square
{
  float: left;
  outline: 0px solid red;
}


After that, run the startTransition() method. Pass in the maxTransitionTime property as an argument.
fillContainer: function()
{
  var imgUrl = this.getRandomImage();

  for(var i = 0; i < 750 / this.squareSize; i++)
  {
  var row = $("<div></div>");
  row.addClass("row");
  row.attr("style", "height:" + this.squareSize + "px");

  for(var j = 0; j < 500 / this.squareSize; j++)
  {
    var square = $("<div></div>");
    square.addClass("square");
    square.attr("style", "height:" + this.squareSize + "px;width:" + this.squareSize + "px");

    var content = $("<div></div>");
    content.addClass("square_contents");
    content.attr("data-row", i);
    content.attr("data-col", j);
    content.attr("style", "background-image:url(" + imgUrl + ");background-position:-" + (j * this.squareSize) + "px -" + (i * this.squareSize) + "px;opacity:1;width:" +  this.squareSize + "px;height:" + this.squareSize + "px");

    square.append(content);
    row.append(square);
  }

  $("#container").append(row);
  }

  this.startTransition(this.maxTransitionTime);
}


We then create startTransition(). It has ms as a parameter. In it, we run the transition() method, passing in a tenth of ms and the squareSize property as arguments.
    
fillContainer: function()
{
  var imgUrl = this.getRandomImage();

  for(var i = 0; i < 750 / this.squareSize; i++)
  {
  var row = $("<div></div>");
  row.addClass("row");
  row.attr("style", "height:" + this.squareSize + "px");

  for(var j = 0; j < 500 / this.squareSize; j++)
  {
    var square = $("<div></div>");
    square.addClass("square");
    square.attr("style", "height:" + this.squareSize + "px;width:" + this.squareSize + "px");

    var content = $("<div></div>");
    content.addClass("square_contents");
    content.attr("data-row", i);
    content.attr("data-col", j);
    content.attr("style", "background-image:url(" + imgUrl + ");background-position:-" + (j * this.squareSize) + "px -" + (i * this.squareSize) + "px;opacity:1;width:" +  this.squareSize + "px;height:" + this.squareSize + "px");

    square.append(content);
    row.append(square);
  }

  $("#container").append(row);
  }

  this.startTransition(this.maxTransitionTime);
},
startTransition: function(ms)
{
  this.transition(ms / 10, this.squareSize);
}


Then create transition(). It will have ms and squareSize as parameters.
startTransition: function(ms)
{
  this.transition(ms / 10, this.squareSize);
},
transition: function(ms, squareSize)
{

}


We should stop here for now. It's about to get fun, but also a little complicated.

Next

Animating the display.

Tuesday, 30 January 2024

Web Tutorial: Year of the Dragon Animation

The Dragon roars in 2024, and we have a web tutorial for it!

Today, we're going to use jQuery UI to create a simple CNY-themed animation. For this, we will use the antique Chinese character for "dragon", shown below, pronounced "long". This will only be used as a template, and you can get rid of it later.

dragon.png


For this, we have some HTML that will have a remote link to the appropriate jQuery libraries. The body's background is set to white for now, in the CSS.
<!DOCTYPE html>
<html>
  <head>
    <title>Year of the Dragon</title>

    <style>
      body
      {
        background-color: rgb(255, 255, 255);
      }
    </style>

    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

    <script>

    </script>
  </head>

  <body>

  </body>
</html>


Here we add a div with the id container, within the body. In the CSS, we style container with a 500 pixel width and 650 pixels height (the exact size of the image), and some margins. Most importantly, we set the background image.
<!DOCTYPE html>
<html>
  <head>
    <title>Year of the Dragon</title>

    <style>
      body
      {
        background-color: rgb(255, 255, 255);
      }

      #container
      {
        background: url(dragon.png);
        width: 500px;
height: 650px;
        margin: 10px auto 0 auto;
      }

    </style>

    <script src="https://code.jquery.com/jquery-1.12.4.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

    <script>

    </script>
  </head>

  <body>
    <div id="container">

    </div>

  </body>
</html>


There, an immediate impact.


We are now going to fill this div with tiny square divs, using JavaScript. In the script tag, create cny as an object.
<script>
  let cny =
  {

  };

</script>


From here, we create the grid array. At some point, it will be a two-dimensional array. But let's leave it as an empty array for now.
<script>
  let cny =
  {
    grid: []
  };
</script>


And then create the fillGrid() method.
<script>
  let cny =
  {
    grid: [],
    fillGrid: function()
    {

    }

  };
</script>


In here, we traverse the length of grid.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {

  }

}


We create a div, give it a class of row, and then append it to container. Pretty straightforward so far.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");

    $("#container").append(row);

  }
}


In the CSS, we want row to be 10 pixels in height and fill the entire width. For now, let's also give it a red outline.
#container
{
  background: url(dragon.png);
  width: 500px;
  height: 650px;
  margin: 10px auto 0 auto;
}

.row
{
  height: 10px;
  width: 100%;
  outline: 1px red;
}


Now we're going to give grid 65 sub-arrays. The height of its parent is 650 pixels, and each div styled using row is 10 pixels... do the math!
grid:
[
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  []

],


And change the body tag to include an onload attribute that calls fillGrid().
<body onload = "cny.fillGrid();">


The code executes! Now you have 65 divs, styled using the CSS class row, within container!


Now we will fill the rows with squares. In the For loop, add another For loop that traverses the length of each current element. Right now all of them are empty arrays, of course, but we will change that soon.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");

    for(var j = 0; j < this.grid[i].length; j++)
    {

    }


    $("#container").append(row);
  }
}


In here, again we create a div and assign it to the variable square. We then give square the CSS class of, well, square. And append square to row.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");

    for(var j = 0; j < this.grid[i].length; j++)
    {
      var square = $("<div></div>");
      square.addClass("square");

      row.append(square);

    }

    $("#container").append(row);
  }
}


Now this is what the CSS class for square looks like. It has both height and width properties set to 10 pixels, is floated left, and we'll give it a red outline also.
#container
{
  background: url(dragon.png);
  width: 500px;
  height: 650px;
  margin: 10px auto 0 auto;
}

.row
{
  height: 10px;
  width: 100%;
  outline: 1px solid red;
}

.square
{
  width: 10px;
  height: 10px;
  outline: 1px solid red;
  float: left;
}


We change grid this way. Make the first element an array of 50 zeroes.
grid:
[
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  [],
  []
]


See the top row? It has 50 squares!


Now do the same for all of grid.
grid:
[
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
],


And this is what you end up with.


EDITOR'S NOTE: Due to my laptop finally giving up the ghost while I was in the middle of writing this web tutorial, subsequent screenshots will be taken from a MacBook.

We will next have more CSS classes! They will be shade0, shade1 and shade2. All of these have an orange background color, but with different opacity. shade3 will be almost transparent, shade2 will be half-opaque, and shade1 will be fully opaque.
.square
{
  width: 10px;
  height: 10px;
  outline: 1px solid red;
  float: left;
}

.shade1
{
  background-color: rgba(255, 155, 0, 1);
}

.shade2
{
  background-color: rgba(255, 155, 0, 0.5);
}

.shade3
{
  background-color: rgba(255, 155, 0, 0.2);
}


Add this line. Depending on the value inside grid (it's all 0 right now), either shade0, shade1, shade2 or shade3 will be the CSS class added to the div. If it's shade0, there'll be no change because there is no such class.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");

    for(var j = 0; j < this.grid[i].length; j++)
    {
      var square = $("<div></div>");
      square.addClass("square");
      square.addClass("shade" + this.grid[i][j]);

      row.append(square);
    }

    $("#container").append(row);
  }
},


Make this change to the second array of the grid array.
grid:
[
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],


You should see the faint orange fill in the second row from the top of the grid.


Great, and now we fill up a couple more rows.
grid:
[
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,1,1,1,1,1,1,1,1,2,0,0,0,0,0,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],


Here you should have a pretty good idea where the 1s, 2s and 3s go.


Let's fill up more rows.
grid:
[
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,0,0,0,0,0,0,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,3,2,2,2,1,1,1,1,1,1,1,1,2,0,0,0,0,0,1,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0,0,0,0,1,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,1,1,1,2,3,3,2,2,2,2,2,3,0,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,3,0,0,0,0,0,0,0,0],
  [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,2,0,0,0,0,0,0,0,0],


And remove the red outline for the CSS classes row and square.
.row
{
  height: 10px;
  width: 100%;
  outline: 0px solid red;
}

.square
{
  width: 10px;
  height: 10px;
  outline: 0px solid red;
  float: left;
}


I think the picture is getting clearer...


OK, now fill up the rest of grid. You should have a pretty good idea what values to use. Then remove the image.
#container
{
  /*background: url(dragon.png);*/
  width: 500px;
  height: 650px;
  margin: 10px auto 0 auto;
}


And there you are.


Now to animate it!

What we want to do is run a method call when the page loads. Let's call it start(). It will accept a numeric value which determines the speed of the animation. Right now let's set it at 1 second, or 1000 milliseconds.
<body onload = "cny.fillGrid();cny.start(1000);">


Here's the method. The numeric value will be known as the parameter delay.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");

    for(var j = 0; j < this.grid[i].length; j++)
    {
      var square = $("<div></div>");
      square.addClass("square");
      square.addClass("shade" + this.grid[i][j]);

      row.append(square);
    }

    $("#container").append(row);
  }
},
start: function(delay)
{

}


In here, we run the method changeColor(), and pass in delay as an argument.
start: function(delay)
{
  cny.changeColor(delay);
}


Create the changeColor() method. Make sure it has the parameter delay.
fillGrid: function()
{
  for(var i = 0; i < this.grid.length; i++)
  {
    var row = $("<div></div>");
    row.addClass("row");

    for(var j = 0; j < this.grid[i].length; j++)
    {
      var square = $("<div></div>");
      square.addClass("square");
      square.addClass("shade" + this.grid[i][j]);

      row.append(square);
    }

    $("#container").append(row);
  }
},
changeColor: function(delay)
{

},

start: function(delay)
{
  cny.changeColor(delay);
}


Now iterate from values 1 to 3 in a For loop.
changeColor: function(delay)
{
  for (var i = 1; i <= 3; i++)
  {
          
  }

},


In it, we use the each() method on all elements that have the CSS class shade1, shade2 or shade3.
changeColor: function(delay)
{
  for (var i = 1; i <= 3; i++)
  {
    $(".shade" + i).each(

    ); 
           
  }
},


Here, we'll create a value randomly smaller than delay, and call it newDelay.
changeColor: function(delay)
{
  for (var i = 1; i <= 3; i++)
  {
    $(".shade" + i).each(
      function( index )
      {
        var newDelay = delay - ((Math.floor(Math.random() * delay) + 1) / 2) ;
      }

    );            
  }
},


Then we'll call the changeColorForObj() method, passing in the current object, the value of i and newDelay as arguments.
changeColor: function(delay)
{
  for (var i = 1; i <= 3; i++)
  {
    $(".shade" + i).each(
      function( index )
      {
        var newDelay = delay - ((Math.floor(Math.random() * delay) + 1) / 2) ;
        cny.changeColorForObj(this, i, newDelay);

      }
    );            
  }
},


Create the changeColorForObj() method.
changeColor: function(delay)
{
  for (var i = 1; i <= 3; i++)
  {
    $(".shade" + i).each(
      function( index )
      {
        var newDelay = delay - ((Math.floor(Math.random() * delay) + 1) / 2) ;
        cny.changeColorForObj(this, i, newDelay);
      }
    );            
  }
},
changeColorForObj: function(obj, index, delay)
{

},

start: function(delay)
{
  cny.changeColor(delay);
}


In here, we use the animate() method on obj to change the background color, with an animation speed of delay.
changeColorForObj: function(obj, index, delay)
{
  $(obj).animate
  (
    {
      backgroundColor:
    },
    delay
  );

},


We determine the new background color by running the getColor() method. Pass in the value of index, and false, as arguments.
changeColorForObj: function(obj, index, delay)
{
  $(obj).animate
  (
    {
      backgroundColor: cny.getColor(index, false)
    },
    delay
  );
},


Create the getColor() method. It has two parameters - an integer, transparency, and a Boolean value, original.
changeColorForObj: function(obj, index, delay)
{
  $(obj).animate
  (
    {
      backgroundColor: cny.getColor(index, false)
    },
    delay
  );
},
getColor: function(transparency, original)
{

},

start: function(delay)
{
  cny.changeColor(delay);
}


In here, we define an array, t. It holds the opacity values for shade1, shade2 and shade3. At index position 0, we have 0 because it's required. But we won't ever use it because the value 0 will never be passed into getColor() as the value of transparency. Next, useT is defined as the element in t that transparency points to.
getColor: function(transparency, original)
{
  var t = [0, 1, 0.5, 0.2];
  var useT = t[transparency];

},


At the end of the method, return a CSS color string using the rgba() specification and useT as the opacity. But before that, we check if original is false and useT is greater than 0.
getColor: function(transparency, original)
{
  var t = [0, 1, 0.5, 0.2];
  var useT = t[transparency];

  if (!original)
  {
    if (useT > 0)
    {

    }
  }


  return "rgba(rgba(255, 155, 0, " + useT + ")";
},


If so, define subtract as a random number between 0.1 and 1. Subtract that value from useT, and set useT back to 0 if the value falls below that. What this does, is randomly decrease opacity of that element we are working on!
getColor: function(transparency, original)
{
  var t = [0, 1, 0.5, 0.2];
  var useT = t[transparency];

  if (!original)
  {
    if (useT > 0)
    {
      var subtract = (Math.floor(Math.random() * 10) + 1) / 10;
      useT -= subtract;
      if (useT < 0) useT = 0;

    }
  }

  return "rgba(rgba(255, 155, 0, " + useT + ")";
},


Back to the changeColorForObj() method. We use setTimeout() to introduce a delay using delay, but doubled.
changeColorForObj: function(obj, index, delay)
{
  $(obj).animate
  (
    {
      backgroundColor: cny.getColor(index, false)
    },
    delay
  );

  setTimeout(
    function()
    {

    },
    delay * 2
  );

},


Then we run the animate() method on obj again, this time passing in true as an argument, so that it resets the opacity value back to the original for shade1, shade2 and shade3! Again, we use delay doubled as the animation speed.
changeColorForObj: function(obj, index, delay)
{
  $(obj).animate
  (
    {
      backgroundColor: cny.getColor(index, false)
    },
    delay
  );

  setTimeout(
    function()
    {
      $(obj).animate
      (
        {
          backgroundColor: cny.getColor(index, true)
        },
        delay * 2
      );

    },
    delay * 2
  );
},


Run this! You should see the character appear in all its glory, fade out at random spots, then fade right back in!


To make this continuous, use setInterval() to run changeColor(), with with a triple delay! If you run this again, you'll see that there's a shimmering effect because the opacity is random every time! Brilliant, right?
start: function(delay)
{
  cny.changeColor(delay);

  setInterval(
    function()
    {
      cny.changeColor(delay);
    },
    delay * 3
  )

}


Final touches...

Now change the background to black.
body
{
  background-color: rgb(0, 0, 0);
}


Now the shimmering gets even more obvious!


It's time to add some text. In the body, add another div with id of text. In it, let's have some HTML, maybe a p tag.
<body onload = "cny.fillGrid();cny.start(1000);">
  <div id="container">

  </div>

  <div id="text"><p>Year Of The Dragon<br />2024</p></div>
</body>


Here's the styling for text. We want it to fill the entirety of its parent, which is the body tag. For that, we also need the position property to be absolute, and have the left and top properties set to 0.
.shade1
{
  background-color: rgba(255, 155, 0, 1);
}

.shade2
{
  background-color: rgba(255, 155, 0, 0.5);
}

.shade3
{
  background-color: rgba(255, 155, 0, 0.2);
}

#text
{
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
}


Here's the styling for the p tag within text. We set margin-top to move the p tag down to the middle of the screen, and use text-align to center it. The rest of it is basically aesthetics - to make the text stand out.
#text
{
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
}

#text p
{
  font-family: georgia;
  font-weight: bold;
  font-size: 4em;
  color: rgb(200, 0, 0);
  text-shadow: 2px 2px rgba(200, 200, 200, 0.5);
  font-variant: small-caps;
  margin-top: 500px;
  text-align: center;
}


And that's your animation!


Happy Year of the Dragon!

I hope this bold funky animation is just the first of many good things this year for us. It's been a pleasure!

Live Long and prosper!
T___T

Tuesday, 23 January 2024

Five Phases of Programming I Went Through

As tech evolves, so does the growth of every software developer. Every programmer's evolution varies to a large extent dependent on their stack, their industry experiences and their personality.

My own journey has been no less.

Today I would like to chronicle, in philosophical phases, my own development from way back in 1996 as a student.

1. 1996 to late 2000s

This was me in school. I was a neat freak about code, at least where indentation and spacing were concerned. I tended to put a lot of code on one line, not because I was trying to look clever, but because I was lazy. And I had tons of comments because my lecturers trained me that way. When I got out into professional society, no one reviewed my code, so whatever habits I had, both good and bad, continued well into the mid-2000s.

Being very tidy.

At this point, too, I felt like knowing more programming languages increased my tech cred. That's complete rubbish, of course, but I was young. I was still hungrily learning, though, and that's always a good thing regardless of the source of my motivation.

Most of my expertise revolved around querying data from a database and displaying it on a web page, and perhaps some extremely rudimentary JavaScript manipulation. Nothing very groundbreaking, but it did constitute the majority of most use cases presented to me.

2. Late 2000s to early 2010s

The next phase of my career development manifested itself as a slogfest. I had rediscovered my passion for writing code after several years of wasting my life away in Desktop Support. I prided myself on how much I worked, and how hard. I didn't think of myself as a clever worker. I thought of myself as a hard worker. In fact, grinding became the focus of my existence.

I measured myself by lines of code produced. I was consistent for the sake of being consistent instead of tailoring approaches to individual cases. I wasn't really into code libraries; there was a serious case of Not Invented Here going on, and I really wanted to create every gadget, every component, myself.

Rudimentary building.

Was that wise?

Not really. In fact, in hindsight, it sounds pretty damn stupid. But was that neccessary? I suspect so. It was another phase of my development. Whatever else followed after this, might not have come without getting through this first.

On the other hand, putting that much time into trying to create every component myself, really upped my coding game, simply via repetition and research.

3. Early 2010s to late 2010s

Around this time, I started getting comfortable with the concepts of frameworks and libraries. Sure, it was good to have the skills to create those frameworks and libraries, but realistically, that would simply have the effect of slowing me down when there were so many more exciting things to learn. I came to terms with the fact that I would never be as good as I really wanted to be; simply because my time on this earth is limited.

Getting more education around that point helped me pick up more languages and technologies. This was the Specialist Diploma in Mobile Apps Development. I stopped trying to do everything in Notepad (or a plain text editor) and picked up the tools to make myself current. Why put myself at a disadvantage? Some sense of misplaced pride in being a caveman? This was tech, and I needed to get with the program.

The caveman way.

This was the period I picked up a whole lot of frameworks and programming languages, experimented madly, and dipped my toe in everything exciting about web and mobile development just so I could have a taste. This was probably where I had the most fun. After letting go of the need to excel, and learn everything thoroughly, I started learning just enough to do whatever I envisioned. Sometimes it was less than what was taught in school, but often, it was more. I had to do my own research to be able to accomplish the things I wanted, and it improved me immeasurably.

I learned things just for the fun of it, instead of doing it because I felt I needed to.

4. Late 2010s to early 2020s

A strange period, to be sure. Most of what I learned was in the form of JavaScript frameworks and libraries, but they were very disparate. I learned JavaScript frameworks. At the same time, I picked up data visualization libraries, and animation libraries. Around this time, I learned SVGs as well. Loads of fun.

More advanced construction.

This part seems more like an extension of the earlier phase, except that it had become less about the different number of platforms, but more about the different kinds of functionality. I had reached the point where learning new programming languages was cool and all, but what I really wanted to do was learn to do new things. As opposed to learning to do the same things in new ways. Which is valuable as well, don't get me wrong, but I was eager to move on.

Since I was working with animation and processing-heavy frameworks, much of my efforts were geared towards improving efficiency, as opposed to just getting things to work.

Professionally, as I was doing work for the Singapore Government at that time, I gained a whole new appreciation for REST APIs.

5. Early 2020s

This was the period of my life where I threw out all the stuff I'd obsessed over before, or just felt bad about not doing enough - code extensibility, code cleanliness, tidiness and so on - in favor of expediency. Yes, plenty of developers would turn their noses up at this, but they aren't the ones paying my bills, so, y'know, fuck 'em with an If-else block.

Doesn't have to be pretty,
but it does have to work.

This materialized because I was now working for a non-tech company where my employers didn't care how clean and clever my code was. My KPI was to deliver requirements, and deliver them yesterday. Now, we can argue forever about whether this is right or wrong, but requirements are requirements. And if I have a problem getting paid to deliver what programming purists would call subpar code, I have the absolute discretion to bugger off and work somewhere else.

But I didn't. Because the rise of AI has ensured that no one gives a rat's ass about the quality of your code, or even just your code period, when they can just ask ChatGPT for it.

Also, I wound up writing significantly less code than I was used to. Most of my code connected to REST endpoints that interfaced with other systems that did the bulk of the work. A lot of it was out of my hands. I was more like a facilitator and vendor coordinator than I was a hands-on programmer. There no longer was any one single platform I could reply on. I used whatever I was provided that would do what my employers needed.

Conclusion

This was an awfully simplistic look at my evolution as a software developer through the last couple decades. There is, of course, a whole lot of nuance that did not make it into this listicle because I detest being a windbag. The evolution continues... or at least, I really hope it does. There's so much left to learn, so many ways one could evolve.

Your growing software dev,
T___T