A mushy lovey-dovey upcoming Valentine's Day, dear readers!
Today we'll be making a Valentine's Day scratch card. A card whose contents will be hidden until you run your mouse cursor over it, revealing the contents of the card bit by bit.
The HTML is really simple, and the majority of the work will be borne by the CSS and JavaScript. Here goes...
<!DOCTYPE html>
<html>
<head>
<title>Scratch!</title>
</head>
<body>
<div id="scratch_wrapper">
</div>
</body>
</html>
I wasn't having you on, was I? The HTML really
is that simple. You have a basic HTML layout and a div with an id of
scratch_wrapper.
OK, let's style
scratch_wrapper. We'll make it a 400 by 400 pixel card, and set the
margin property to
10% auto 0 auto so that it sits in the middle of your screen. Then we'll set the border to a nice
red so you can see what's happening.
<head>
<title>Scratch!</title>
<style>
#scratch_wrapper
{
width:400px;
height:400px;
margin:10% auto 0 auto;
border: 1px solid #FF0000;
}
</style>
</head>
This is your card so far!
OK, time for a bit of JavaScript. Modify your HTML this way...
<body onload="unscratch()">
... and add the
unscratch() function. This causes the scratch card to revert to its full initial state, where the contents are hidden from view. First, you clear the
scratch_wrapper div.
<style>
#scratch_wrapper
{
width:400px;
height:400px;
margin:10% auto 0 auto;
border: 1px solid #FF0000;
}
</style>
<script>
function unscratch()
{
var wrapper=document.getElementById("scratch_wrapper");
wrapper.innerHTML="";
}
</script>
Next, you specify a nested
For loop. 80 times by 80 times. Why 80? We'll get to that in a minute.
function unscratch()
{
var wrapper=document.getElementById("scratch_wrapper");
wrapper.innerHTML="";
for (var i=0;i<80;i++)
{
for (var j=0;j<80;j++)
{
}
}
}
In the inner loop of the nested For loop, add this code. It creates a div element and assigns it the CSS class of
scratch and
full.
function unscratch()
{
var wrapper=document.getElementById("scratch_wrapper");
wrapper.innerHTML="";
for (var i=0;i<80;i++)
{
for (var j=0;j<80;j++)
{
scratch=document.createElement("div");
scratch.className="scratch full";
}
}
}
Then the unique id of this element is created according to its row,
i, and its column,
j.
function unscratch()
{
var wrapper=document.getElementById("scratch_wrapper");
wrapper.innerHTML="";
for (var i=0;i<80;i++)
{
for (var j=0;j<80;j++)
{
scratch=document.createElement("div");
scratch.className="scratch full";
scratch.id="scratch_"+i+"_"+j;
}
}
}
Next, you insert this div element into the
scratch_wrapper div using the
appendChild() method.
function unscratch()
{
var wrapper=document.getElementById("scratch_wrapper");
wrapper.innerHTML="";
for (var i=0;i<80;i++)
{
for (var j=0;j<80;j++)
{
scratch=document.createElement("div");
scratch.className="scratch full";
scratch.id="scratch_"+i+"_"+j;
wrapper.appendChild(scratch);
}
}
}
What's scratch and full?
Aha! I see you've been paying attention. Your objective here is to fill the
scratch_wrapper div with tiny squares.
scratch defines the dimensions of each square, while
full defines the color. You'll see that each square is 5 by 5 pixels. This is
not a coincidence. The scratch_card div is 400 pixels wide and 400 pixels tall. If you have 80 rows of 80 squares, each square 5 by 5 pixels, you have (5 x 80 = 400)! The
float property is set to
left so that when the 400 pixel limit is reached, the next row automatically begins on the left. As for the
full CSS class, it defines a color
red (feel free to change it!) at 100% opacity.
#scratch_wrapper
{
width:400px;
height:400px;
margin:10% auto 0 auto;
border: 1px solid #FF0000;
}
.scratch
{
width:5px;
height:5px;
float:left;
}
.full
{
background-color:rgba(255,0,0,1);
}
Your card is now covered with (80 x 80 = 6400)
red squares!
Our next objective is to make each square reveal part of the underlying card as your mouse cursor goes over it. To that end, modify your JavaScript. This line adds an event to each div created. On a mouse over, it will fire off the
scratchit() function, passing in the
id of the current square as an argument.
for (var i=0;i<80;i++)
{
for (var j=0;j<80;j++)
{
scratch=document.createElement("div");
scratch.className="scratch full";
scratch.id="scratch_"+i+"_"+j;
scratch.onmouseover=function(){scratchit(this.id)};
wrapper.appendChild(scratch);
}
}
And of course, we'll need to create the
scratchit() function, which will take in
scratchid as a string parameter.
<script>
function unscratch()
{
var wrapper=document.getElementById("scratch_wrapper");
wrapper.innerHTML="";
for (var i=0;i<80;i++)
{
for (var j=0;j<80;j++)
{
scratch=document.createElement("div");
scratch.className="scratch full";
scratch.id="scratch_"+i+"_"+j;
scratch.onmouseover=function(){scratchit(this.id)};
wrapper.appendChild(scratch);
}
}
}
function scratchit(scratchid)
{
}
Before we go further...
Let's examine what the code is supposed to do.
The layout of the squares is as per follows... (assuming the first number is the row and the second number is the column. Here, I'll show you the top left corner of your
scratch_wrapper div as an example.
scratch_0_0 |
scratch_0_1 |
scratch_0_2 |
scratch_0_3 |
scratch_0_4 |
scratch_1_0 |
scratch_1_1 |
scratch_1_2 |
scratch_1_3 |
scratch_1_4 |
scratch_2_0 |
scratch_2_1 |
scratch_2_2 |
scratch_2_3 |
scratch_2_4 |
Let's take
x as the row of the current square (the square which the mouse cursor is over) and
y as the column. For this example,
x = 2 and
y = 2. Therefore the
id of the square in the example is
scratch_2_2. When your mouse cursor is over the square, the surrounding squares are affected. Those squares touching the sides and corners of the current square are affected most severely, lowering their opacity to 20%. Squares one square away from the current square, are slightly affected, lowering their opacity to 50%.
scratch_0_0 |
scratch_0_1 |
scratch_0_2 |
scratch_0_3 |
scratch_0_4 |
scratch_0_5 |
scratch_1_0 |
scratch_1_1 |
scratch_1_2 |
scratch_1_3 |
scratch_1_4 |
scratch_1_5 |
scratch_2_0 |
scratch_2_1 |
scratch_2_2 |
scratch_2_3 |
scratch_2_4 |
scratch_2_5 |
scratch_3_0 |
scratch_3_1 |
scratch_3_2 |
scratch_3_3 |
scratch_3_4 |
scratch_3_5 |
scratch_4_0 |
scratch_4_1 |
scratch_4_2 |
scratch_4_3 |
scratch_4_4 |
scratch_4_5 |
Back to the code!
To do this, first, we take the
id of the current square, using the argument
scratchid and the
split() method. From here, we define
x and
y.
function scratchit(scratchid)
{
var x=0;
var y=0;
var xyvalues=scratchid.split("_");
x=parseInt(xyvalues[1]);
y=parseInt(xyvalues[2]);
}
Then we run the
changeopacity() function using the
id and the argument "gone".
function scratchit(scratchid)
{
var x=0;
var y=0;
var xyvalues=scratchid.split("_");
x=parseInt(xyvalues[1]);
y=parseInt(xyvalues[2]);
var id="";
changeopacity(scratchid,"gone");
}
Next, we extrapolate the ids of the surrounding squares, and run the
changeopacity() function using these ids and the argument "almostgone". They're more severely affected, remember?
function scratchit(scratchid)
{
var x=0;
var y=0;
var xyvalues=scratchid.split("_");
x=parseInt(xyvalues[1]);
y=parseInt(xyvalues[2]);
var id="";
changeopacity(scratchid,"gone");
id="scratch_"+(x-1)+"_"+y;
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+y;
changeopacity(id,"almostgone");
id="scratch_"+x+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+x+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x-1)+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+(x-1)+"_"+(y-1);
changeopacity(id,"almostgone");
}
Then we do the same for the squares one square away from the current square, but this time using the argument "half".
function scratchit(scratchid)
{
var x=0;
var y=0;
var xyvalues=scratchid.split("_");
x=parseInt(xyvalues[1]);
y=parseInt(xyvalues[2]);
var id="";
changeopacity(scratchid,"gone");
id="scratch_"+(x-1)+"_"+y;
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+y;
changeopacity(id,"almostgone");
id="scratch_"+x+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+x+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x-1)+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+(x-1)+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+(x-2)+"_"+y;
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+y;
changeopacity(id,"half");
id="scratch_"+x+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+x+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x+1)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x-1)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x+1)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x-1)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y+1);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y+1);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y-1);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y-1);
changeopacity(id,"half");
}
Next, we write the
changeopacity() function.
function scratchit(scratchid)
{
var x=0;
var y=0;
var xyvalues=scratchid.split("_");
x=parseInt(xyvalues[1]);
y=parseInt(xyvalues[2]);
var id="";
changeopacity(scratchid,"gone");
id="scratch_"+(x-1)+"_"+y;
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+y;
changeopacity(id,"almostgone");
id="scratch_"+x+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+x+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x-1)+"_"+(y+1);
changeopacity(id,"almostgone");
id="scratch_"+(x+1)+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+(x-1)+"_"+(y-1);
changeopacity(id,"almostgone");
id="scratch_"+(x-2)+"_"+y;
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+y;
changeopacity(id,"half");
id="scratch_"+x+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+x+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x+1)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x-1)+"_"+(y+2);
changeopacity(id,"half");
id="scratch_"+(x+1)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x-1)+"_"+(y-2);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y+1);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y+1);
changeopacity(id,"half");
id="scratch_"+(x+2)+"_"+(y-1);
changeopacity(id,"half");
id="scratch_"+(x-2)+"_"+(y-1);
changeopacity(id,"half");
}
function changeopacity(scratchid,opacity)
{
}
What we're going to do first, is create three more CSS classes,
half,
almostgone and
gone. They're almost identical to the
full CSS class, except that
half has 50% opacity while
almostgone has 20%.
gone is completely transparent.
.full
{
background-color:rgba(255,0,0,1);
}
.half
{
background-color:rgba(255,0,0,0.5);
}
.almostgone
{
background-color:rgba(255,0,0,0.2);
}
.gone
{
background-color:rgba(255,0,0,0);
}
So here we obtain the actual square via the id passed in, and perform the operation if the object returned is not a
null. It will be
null if the object does not exist, which can happen if your current square is near the edges of the
scratch_wrapper div and you're trying to get its surrounding squares (which, by definition, don't exist).
Also, declare the variable
currentopacity. It'll be useful soon,
function changeopacity(scratchid,opacity)
{
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
The objective is to set the class of the current square to
gone, which we've already done, and the surrounding squares to lower opacity, depending on their distance from the current square. But what if the squares affected already have lowered opacity due to an earlier mouse over?
So we find out what their current opacity is, using the
getopacity() function. This merely takes the object and obtains its class. It removes the "scratch " (with the space) and returns the resulting string.
function changeopacity(scratchid,opacity)
{
function getopacity(div)
{
var bg=div.className;
bg=bg.replace("scratch ","");
return bg;
}
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
If you're trying to set the class to "half", first, obtain the current opacity and set the value of
currentopacity to this.
function changeopacity(scratchid,opacity)
{
function getopacity(div)
{
var bg=div.className;
bg=bg.replace("scratch ","");
return bg;
}
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="half")
{
currentopacity=getopacity(scratched);
}
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
If
currentopacity is "full", proceed as planned.
function changeopacity(scratchid,opacity)
{
function getopacity(div)
{
var bg=div.className;
bg=bg.replace("scratch ","");
return bg;
}
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="half")
{
currentopacity=getopacity(scratched);
if (currentopacity=="full")
{
scratched.className="scratch half";
}
}
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
Now, if
currentopacity is already "half", set the class to "almostgone" instead.
function changeopacity(scratchid,opacity)
{
function getopacity(div)
{
var bg=div.className;
bg=bg.replace("scratch ","");
return bg;
}
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="half")
{
currentopacity=getopacity(scratched);
if (currentopacity=="full")
{
scratched.className="scratch half";
}
if (currentopacity=="half")
{
scratched.className="scratch almostgone";
}
}
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
If
currentopacity is "almostgone", set it to "gone"!
function changeopacity(scratchid,opacity)
{
function getopacity(div)
{
var bg=div.className;
bg=bg.replace("scratch ","");
return bg;
}
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="half")
{
currentopacity=getopacity(scratched);
if (currentopacity=="full")
{
scratched.className="scratch half";
}
if (currentopacity=="half")
{
scratched.className="scratch almostgone";
}
if (currentopacity=="almostgone")
{
scratched.className="scratch gone";
}
}
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
Now do the same for the case of "almostgone"!
function changeopacity(scratchid,opacity)
{
function getopacity(div)
{
var bg=div.className;
bg=bg.replace("scratch ","");
return bg;
}
var currentopacity="";
var scratched=document.getElementById(scratchid);
if (scratched!=null)
{
if (opacity=="half")
{
currentopacity=getopacity(scratched);
if (currentopacity=="full")
{
scratched.className="scratch half";
}
if (currentopacity=="half")
{
scratched.className="scratch almostgone";
}
if (currentopacity=="almostgone")
{
scratched.className="scratch gone";
}
}
if (opacity=="almostgone")
{
currentopacity=getopacity(scratched);
if (currentopacity=="full")
{
scratched.className="scratch almostgone";
}
if (currentopacity=="half")
{
scratched.className="scratch gone";
}
if (currentopacity=="almostgone")
{
scratched.className="scratch gone";
}
}
if (opacity=="gone")
{
document.getElementById(scratchid).className="scratch gone";
}
}
}
Run your code. Are you getting this effect?
OK, let's make a slight improvement. Alter the scratch CSS class.
Now try this again. The transition will make the effect look way better.
.scratch
{
width:5px;
height:5px;
float:left;
transition:all 1s;
}
Finishing touches
Modify your
scratch_wrapper div's CSS to use this image as background.
#scratch_wrapper
{
width:400px;
height:400px;
margin:10% auto 0 auto;
border: 1px solid #FF0000;
background-image: url(bg.jpg);
background-size:cover;
background-repeat:no-repeat;
background-position:50% 50%;
}
Niiiiiice.
Till next Valentine's, remember that beauty is only skin-deep!
T___T