A freelance client wanted to create a shirt generator for her website that would allow the user to select type and size of shirt, the color, and a prefabricated low poly art image to put on it. They also wanted the user to be able to manipulate image’s color scheme. But the color selection is meant to be independent for each individual triangle (or in this demonstration, each square), meaning the user should be able to select a portion of the image, be able to pick from the color options, and only change the color of that selected section. Like so:
The client also wanted the user to be able to select as many colors as they want, in any order. If the user clicks on a color that has not previously been clicked, the selected div’s background becomes that color. If the user clicks on a color that has already been clicked but is not the most recently chosen color, it makes the selected div the clicked color. If, however, the color was the most recently clicked color (like if the user clicked green, then green again right after), the div should default back to the previous color. If there is not previous color to default to, the div’s background color is blank.
Essentially, only if the selected div currently has the background color that matches of the most recently chosen color will the div default back to the previous color. Any other scenario, the div’s background color is assigned the same value as the clicked color (this conditional thinking comes in handy during development).
Originally, I developed a demo for the client using three divs: left sleeve, mid-section, and right sleeve. Once the demo was working correctly and the logic was written, I decided to have a little more fun with it on my own. I wanted to test out how robust the script would be when given many sections to pick from (low poly art images tend to be extensive, and, depending on detail, can consist of hundreds of individual triangles-potentially more!).
Upon experimentation, I came across something else I had not previously (at least not consciously) encountered in web development: geometry. To see exactly what geometry I encountered and how I addressed it, check out my previous Medium post on Canvas 2D Context.
Now, onto all the colors.
While my initial attempt worked, the logic was difficult to scale (if not completely unsustainable) with the demand of hundreds of sections of the low-poly images. The goal was to differentiate the divs with class names, like so…
They all share the same initial class, tshirt. This makes it easy to apply the on click event listener to all of the sections that make up the overall figure. Each square, as well as the hexagon, in the kaleidoscopic figure as a tshirt element. All of the thsirt elements are contained within a container, designated with the class shirt-container. This assignment logic is applied to all of the color elements in the color picker container. Each of these elements share the class name color.
During my initial development, I decided that in order to allow the user to toggle between colors the way the client wanted, the selected colors should be stored in arrays. And in order for these selected colors (and their order) to remain independent from the other sections and their color collections/order, I had to make separate arrays for each of the sections of the figure. Like so…
Each of these empty arrays will be filled accordingly with the selected colors to correspond with the selected tshirt element.
I know, it’s gross. Don’t worry, the pain is temporary.
This procedure is simply not sustainable. To create arrays for every single section of a figure (remember, the images we’ll be using in the future are low-poly images consisting of hundreds, potentially more, individual triangles), is too memory intensive and it’s definitely not DRY or dynamic. As you can see, I’m initializing 6 empty arrays to be used for the six sections of the figure. I’m also initializing a variable to represent the section of the figure. While this is relatively efficient because I will be reassigning an existing variable(s) as opposed to assigning new ones throughout the logic, there must be a way to accomplish this logic without having to initialize so many variables. Here’s how many I initialize in my second attempt:
Though my first attempt’s data management was not sustainable, the functional logic was solid, it just needed a bit of refinement. I also separated more of the functionality into smaller helper functions which helped to separate the logic and make the functions more manageable.
The logic can be broken down like so:
A function to capture the section of the figure that is to be assigned the selected colors.
This function is assigned to all tshirt elements. When the document is ready.
The second function, selectColor, is assigned to all of the color elements. This determines which color the selected portion of the figure will assume.
This function utilizes one of the helper functions I was describing earlier, checkColor.
checkColor takes two arguments: the selected section of the figure and the selected color. A key difference between how this function works in my first attempt versus how it works not is class assignment. In my first attempt, I was adding a clicked class. So essentially this function went through all of the figure sections to find the one with the class clicked. Then it worked to applied the right color. In my current function, the selected colors are actually assigned as classes, and are stored in the element’s classList.
The first if statement consists of three conditional statements. The first conditional determines if the selected portion’s classList already contains the selected color. The second conditional checks to make sure that the color does not equal the second to last element in the classList array. Here is what classList looks like in the console after selecting a few colors:
The second to last element in this context represents the most recently selected color. This conditional is included in the if statement because it evaluates whether or not the color needs to be moved to the front of all the other colors or removed from the list completely. If the selected color is already the most recently chosen color (second to last element) then remove it from the classList (using the image above, consider indigo). If it is in the list but it is not the most recently chosen color (using the image above, consider blue), then remove it from the list and add it immediately after. This ensures the color is placed at the second to last index.
The third conditional simply checks the length of the classList array. If the classList array has a length of 2, that means it only contains one class (tshirt) and then the classList value. This means that there are no colors in the array.
If any of the three conditionals are not met, then we hit the second if statement, which evaluates whether or not the selected portion contains the color in it’s classList already. If it does, remove the color. If it doesn’t, we hit our else statement, which simply adds the color to the element’s classList.
You can also see another helper function being executed at the end of checkColor, applyColor.
This function, as the name implies, applies the correct color (determined by selectColor and evaluated by checkColor) to the appropriate section of the figure (determined by selectPortion). You can ignore the hex conditional statements, as those apply to the canvas element covered in my previous blog. For now, just know the hex part of the function determines whether or not to apply color to the canvas element. This is done differently than simply changing background color, which is done to the other tshirt elements. applyColor first checks if classList.length is greater than three. This means that there is a color class in the array classList. If the length is less than or equal to 3, that means that only the tshirt class, the section class (ex: sleeve-left), and the classList value are present in the array, both of which do not designate a specific color.
If classList.length is greater than two then we apply the second to last element in the array (most recently selected color) to the selected portion’s background color. If it is not greater than two, we make the background color nothing.
And the result:
A final note, while this still is only being applied to six separate elements, if you added more elements, it will still work the same. There is no need for arrays or excess memory being occupied. As long as all the separate sections have the tshirt class and all of the color options have the color class, The two initialized variable are still all you need. The only reason I have the different tshirt section classes (sleeve-left, sleeve-right, mid-section, etc.) is to give them different rotations. This creates the kaleidoscopic (or star/sun-looking) figure.
You can even take it a step further and store all colors that you want in an array.
Then, on load, simply iterate over array element (color).
And now you will have more colors to choose from.
Please note, you have to use the color names. If you use HEX codes or RGB values, you will get an error like this:
Now for the final touch: clearAll.
This function has one job: remove all the selected styling and portions of the image. To do this, we simply iterate over all of the elements, and reassign their classes to their original class names (removing colors). We also have to re-initialize the selected portion of the image to an empty string, essentially unassigning it. See below:
We are reassigning the classList of each element to just their original class name (in this case, the value that is the second element in the classList array). And since all of the elements have differing second class names (such as ‘sleeve-left’, ‘sleeve-right’ and ‘mid-section’), we use string interpolation to grab the unique second value of the classList array for each unique element. So no matter what the class name is it’s assigned correctly with respect to the specific .tshirt element that is the current index in the iteration.
The GitHub repo for this code is in my References.
If you enjoyed the post or need clarification on anything mentioned, let me know in the comments. Feel free to share and leave claps!
The Shapes of CSS
All of the below use only a single HTML element. Any kind of CSS goes, as long as it's supported in at least one…
Introduction to Canvas 2D Context
This is a prelude to a future blog post which will cover and break down how I was making a simple color picker for a…
Color Names - HTML Color Codes
HTML color names rule. Modern browsers support 140 HTML color names which we've listed here along with their Hex color…