Time for a little fun. There's a meme I encountered years back, where it claims that certain illnesses affect the eyes, and there's a test for it. If you can see what you're reading, you probably don't have that condition. So you'll be tested for serious stuff like "tuberculosis", "stress" or what-have-you, and then there will be one you can't quite make out. When you finally do, it says something embarrassing like "erectile dysfunction".
 |
| Something like this! |
So yeah, let's do something like that today. Totally randomize the text and colors. We'll use VueJS for this because... why the hell not, eh?
We're going to have some boilerplate HTML to start with. We have a div with the id
ctmApp that will be Vue's placeholder. There's also a script remote link to VueJS.
<!DOCTYPE html>
<html>
<head>
<title>Color Troll Meme</title>
<style>
</style>
</head>
<body>
<div id="ctmApp">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.18/vue.min.js"></script>
<script>
</script>
</body>
</html>
Inside the script tag, We declare
app as a call to
Vue(). In it, we pass an object. In the classic VueJS pattern, the object will have
el,
data, methods and
created.
el will be set to
ctmApp.
<script>
var app = new Vue
(
{
el: "#ctmApp",
data:
{
},
methods:
{
},
created: function()
{
}
}
);
</script>
In
data, we want two arrays -
ctms,
inUse and the object
wordTypes.
data:
{
ctms:
[
],
inUse:
[
],
wordTypes:
{
}
},
ctms is an array of objects. You can have as many as you want, though I'd go with 9. Each object has
name,
color and
textColor properties.
data:
{
ctms:
[
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
}
],
inUse:
[
],
wordTypes:
{
}
},
inUse is a placeholder array which defaults to empty. We won't need to worry about it yet.
wordTypes is an objects that has
area,
condition,
condition_prefix and
condition_suffix arrays as properties. These arrays contain strings.
data:
{
ctms:
[
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
}
],
inUse:
[
],
wordTypes:
{
area:
[
],
condition:
[
],
condition_prefix:
[
],
condition_suffix:
[
]
}
},
I've filled these in.
area is supposed to be embarrassing areas of the human body to develop medical conditions in.
condition is an array of medical conditions that range from mild to nasty.
condition_prefix and
condition_suffix are descriptors that are supposed to go with the words in
area.
data:
{
ctms:
[
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
},
{
name: "",
color: "",
textColor: ""
}
],
inUse:
[
],
wordTypes:
{
area:
[
"testicle",
"anus",
"vagina",
"nipple",
"groin",
"penis",
"labia",
"cleavage",
"rectum"
],
condition:
[
"dehydration",
"constipation",
"arthritis",
"bronchitis",
"myopia",
"baldness",
"pinkeye",
"narcolepsy",
"epilepsy",
"insomnia",
"dementia",
"psychosis",
"indigestion",
"heartburn"
],
condition_prefix:
[
"detached",
"bruised",
"inverted",
"ingrown",
"misaligned",
"ruptured"
],
condition_suffix:
[
"lacerations",
"tumor",
"ulcers",
"blisters",
"blockage",
"fracture",
"ulcers",
"inflammation",
"necrosis",
"discharge",
"dysfunction",
"fungus"
]
}
},
For the
methods object, we will put in five methods -
fillInfo(),
getColor(),
getCondition(),
getOne() and
getRandomNo(). In the
created method, we will run the
fillInfo() method.
methods:
{
fillInfo: function()
{
},
getColor: function()
{
},
getCondition: function()
{
},
getOne: function()
{
},
getRandomNo:function()
{
}
},
created: function()
{
this.fillInfo();
}
In the HTML, add this in the
ctmApp div. We use
v-for to dictate that we want a div for each element in the
ctms array, styled using the CSS class
ctm.
<div id="ctmApp">
<div class="ctm" v-for="ctm in ctms">
</div>
</div>
In the CSS, add the CSS class for
ctm. It's basically a
deep grey circle.
<style>
.ctm
{
width: 300px;
height: 300px;
float: left;
margin-left: 10%;
margin-top: 10%;
border-radius: 50%;
background-color: rgb(10, 10, 10);
}
</style>
You'll see 9
deep grey circles. Because I put 9 elements inside the
ctms array. Depending on your screen size, you may need to scroll.
Let's work on the methods now. This is
getRandomNo(). Give it a parameter,
max. It should return a random number from 0 to
max minus 1.
getRandomNo:function(max)
{
return Math.floor(Math.random() * max);
}
For
getOne(), we have the parameter,
arr, which is an array. We return one of the elements of
arr.
getOne: function(arr)
{
return arr[];
},
Which one? Well, we let
getRandomNo() decide.
getOne: function(arr)
{
return arr[this.getRandomNo()];
},
But of course, we need to pass in the size of
arr so that the random number generated is not out of bounds for
arr.
getOne: function(arr)
{
return arr[this.getRandomNo(arr.length)];
},
For
getColor(), there is the parameter
maxRGB.
maxRGB is used to restrict how bright each color component is. In here, we declare
r,
g and
b, and each one is set by using
getRandomNo() to grab any number from 0 to
maxRGB minus 1.
getColor: function(maxRGB)
{
var r = this.getRandomNo(maxRGB);
var b = this.getRandomNo(maxRGB);
var g = this.getRandomNo(maxRGB);
},
Then we return an array containing these three values.
getColor: function(maxRGB)
{
var r = this.getRandomNo(maxRGB);
var b = this.getRandomNo(maxRGB);
var g = this.getRandomNo(maxRGB);
return [r, g, b];
},
And for the last small method, we have
getCondition(). For this, we have the parameters
area and
prefix. Here, we assume that
area has already been taken from the
area array, and
prefix is a Boolean that is
true by default.
getCondition: function(area, prefix = true)
{
},
We declare
cond as an empty string, then use an
If block on
prefix.
getCondition: function(area, prefix = true)
{
var cond = "";
if (prefix)
{
}
else
{
}
},
If
prefix is
true, we set
cond to a random element in
condition_prefix using
getOne(), and return a string of
cond and
area. If not, we set
cond to a random element in
condition_suffix, and return a string of
area and
cond.
getCondition: function(area, prefix = true)
{
var cond = "";
if (prefix)
{
cond = this.getOne(this.wordTypes.condition_prefix);
return cond + " " + area;
}
else
{
cond = this.getOne(this.wordTypes.condition_suffix);
return area + " " + cond;
}
},
Now, time to work on
fillInfo()! It will use all of the methods we've worked on up to now, either directly or otherwise. We begin by iterating through the
ctms array.
fillInfo: function()
{
this.ctms.forEach(
(ctm, index) =>
{
}
);
},
We declare
isLast as a Boolean. If the current value of
index is exactly the length of
ctms minus 1, that means it's the last element. Then we have an
If block based on
isLast.
fillInfo: function()
{
this.ctms.forEach(
(ctm, index) =>
{
var isLast = (index === this.ctms.length - 1);
if (isLast)
{
}
else
{
}
}
);
},
We'll leave the first case along for now. For all other
ctm objects in the
ctms array, we set the name property to a random string from the
condition array.
fillInfo: function()
{
this.ctms.forEach(
(ctm, index) =>
{
var isLast = (index === this.ctms.length - 1);
if (isLast)
{
}
else
{
ctm.name = this.getOne(this.wordTypes.condition);
}
}
);
},
Now, here in the HTML, we have a h1 tag with the value of
name.
<div class="ctm" v-for="ctm in ctms">
<h1>{{ ctm.name }}</h1>
</div>
In the HTML, let's just set the h1 tag to have a certain size, center it, and so on. Most importantly, we set the color to a
light grey so we can see the contrast.
<style>
.ctm
{
width: 300px;
height: 300px;
float: left;
margin-left: 10%;
margin-top: 10%;
border-radius: 50%;
background-color: rgb(10, 10, 10);
}
.ctm h1
{
margin-top: 40%;
font-size: 30px;
text-align: center;
color: rgb(100, 100, 100);
}
</style>
Look at this! Conditions are populated, but "pinkeye" is repeated.
To fix this, check if the value of
name is in the
inUse array. And keep reassigning the
name property until you get a value that
isn't in the
inUse array.
if (isLast)
{
}
else
{
ctm.name = this.getOne(this.wordTypes.condition);
while (this.inUse.indexOf(ctm.name) > -1)
{
ctm.name = this.getOne(this.wordTypes.condition);
}
}
Once that's done, push the value into the
inUse array, so that this value can be one of the values compared against further down.
if (isLast)
{
}
else
{
ctm.name = this.getOne(this.wordTypes.condition);
while (this.inUse.indexOf(ctm.name) > -1)
{
ctm.name = this.getOne(this.wordTypes.condition);
}
this.inUse.push(ctm.name);
}
Now you'll see no repeats! Bear in mind that this only works as long as you have less elements in the
ctms array (OK, not counting the final element) than there are elements in the
condition array. Otherwise you're going to get an infinite loop.
Now to handle the condition for the last element in
ctms. Declare
prefix as a Boolean, and use
getRandomNo() with a argument of 2, to get a random number between 0 and 1. Then declare
area, and use
getOne() to obtain a random value from the
area array, as its value.
if (isLast)
{
var prefix = (this.getRandomNo(2) === 0);
var area = this.getOne(this.wordTypes.area);
}
else
{
ctm.name = this.getOne(this.wordTypes.condition);
while (this.inUse.indexOf(ctm.name) > -1)
{
ctm.name = this.getOne(this.wordTypes.condition);
}
this.inUse.push(ctm.name);
}
Then set the
name property by using
getCondition() and passing in
area and
prefix as arguments.
if (isLast)
{
var prefix = (this.getRandomNo(2) === 0);
var area = this.getOne(this.wordTypes.area);
ctm.name = this.getCondition(area, prefix);
}
else
{
ctm.name = this.getOne(this.wordTypes.condition);
while (this.inUse.indexOf(ctm.name) > -1)
{
ctm.name = this.getOne(this.wordTypes.condition);
}
this.inUse.push(ctm.name);
}
See? The very last circle at the bottom right now reads "misaligned rectum"!
Let's handle colors now. After the
If block, declare
colorArr as the array returned from calling the
getColor() method. Pass in 100 as an argument so that the RGB values won't be greater than 100. That means it'll be a somewhat dark color. After that, formulate a CSS
rgb() string based on
colorArr's elements, and assign the value to the
color property of the current element of
ctms.
if (isLast)
{
var prefix = (this.getRandomNo(2) === 0);
var area = this.getOne(this.wordTypes.area);
ctm.name = this.getCondition(area, prefix);
}
else
{
ctm.name = this.getOne(this.wordTypes.condition);
while (this.inUse.indexOf(ctm.name) > -1)
{
ctm.name = this.getOne(this.wordTypes.condition);
}
this.inUse.push(ctm.name);
}
var colorArr = this.getColor(100);
ctm.color = "rgb(" + colorArr[0] + "," + colorArr[1] + "," + colorArr[2] + ")";
Bake this value into the div's
style attribute.
<div class="ctm" v-for="ctm in ctms" style="background-color:{{ ctm.color }}">
<h1>{{ ctm.name }}</h1>
</div>
You can see that now the circles are colored different. Each one is a different color.
Now for the text! Each circle's text should have the same color as its div, but several shades brighter. Remember we capped the RGB values at 100 earlier? That was deliberate so we could have a buffer between 100 and 255, the actual maximim RGB value. Now declare
opacity and set it to 1. Then set the
textColor property to what we previously set
color as, except that we use rgba() instead and use
opacity as the last argument.
var colorArr = this.getColor(100);
ctm.color = "rgb(" + colorArr[0] + "," + colorArr[1] + "," + colorArr[2] + ")";
var opacity = 1;
ctm.textColor = "rgba(" + colorArr[0] + "," + colorArr[1] + "," + colorArr[2] + "," + opacity + ")";
For each value (except
opacity), we add 150. This makes the text color much brighter, but keeps them at the same proportions.
var colorArr = this.getColor(100);
ctm.color = "rgb(" + colorArr[0] + "," + colorArr[1] + "," + colorArr[2] + ")";
var opacity = 1;
ctm.textColor = "rgba(" + (colorArr[0] + 150) + "," + (colorArr[1] + 150) + "," + (colorArr[2] + 150) + "," + opacity + ")";
Now bake the color of the text into the h1 tag.
<div class="ctm" v-for="ctm in ctms" style="background-color:{{ ctm.color }}">
<h1 style="color:{{ ctm.textColor }}">{{ ctm.name }}</h1>
</div>
There you go. You can see all the text colors are basically brighter versions of their backgrounds.
Finally, the opacity!
Remember the variable
opacity? It's supposed to be very low for the final div, and varying higher levels for the others. The idea here is that the more clearly you can see the text, the less likely that you have that condition. So for the last medical condition, it's super unclear and when the user can finally read it, it says something embarrassing. That's the joke!
So here, if
isLast is
true, we set
opacity at a really low value, such as 0.05.
var opacity = (isLast ? 0.05 : );
And if not, we get a random number from 0 to 7.
var opacity = (isLast ? 0.05 : this.getRandomNo(8));
We add 2, so we get a number from 2 to 9.
var opacity = (isLast ? 0.05 : this.getRandomNo(8) + 2);
Then we divide by 10, so the final result is between 0.2 to 0.9.
var opacity = (isLast ? 0.05 : (this.getRandomNo(8) + 2) / 10);
See that everything else is relatively easy to read, but the last one is almost invisible! It says "ingrown rectum", which sounds painful.
Have fun with this. Refresh. See what results you get. Add more to the arrays!
Better go see a doctor for that testicle necrosis!
T___T