Sunday, 30 August 2015

Spot The Bug: The Undisplayable Array

Hello and welcome to another round of Spot The Bug!

Bring it on!


If the last one caused you to facepalm, this one might induce you to blow your brains out. I call it a monumental example of were-you-even-thinking-at-all. So here goes!

I had a gallery of thumbnails that I wanted to test some CSS on. My intention was to store them in a PHP array and then output them via a For loop. The array was for my convenience so I could just add and delete the thumbnails in the array as and when I needed a bigger or smaller number of thumbnails. I used different colors to represent different pictures. The code:
<?php
$thumbails=array();
$thumbnails[]["label"]="Sir Lancelot";
$thumbnails[]["border"]="#FF99FF";
$thumbnails[]["bg"]="#AA4488";

$thumbails=array();
$thumbnails[]["label"]="Sir Tristan";
$thumbnails[]["border"]="#336699";
$thumbnails[]["bg"]="#00FFFF";

$thumbails=array();
$thumbnails[]["label"]="Sir Percival";
$thumbnails[]["border"]="#EF7700";
$thumbnails[]["bg"]="#1509FF";

$thumbails=array();
$thumbnails[]["label"]="Sir Lionel";
$thumbnails[]["border"]="#3F4F5F";
$thumbnails[]["bg"]="#119955";

$thumbails=array();
$thumbnails[]["label"]="Sir Galahad";
$thumbnails[]["border"]="#FFFF44";
$thumbnails[]["bg"]="#008899";

$thumbails=array();
$thumbnails[]["label"]="Sir Gaheris";
$thumbnails[]["border"]="#003586";
$thumbnails[]["bg"]="#DD8799";

echo "<h1>Knights of the Round Table</h1>";

foreach ($thumbnails as $thumb)
{
    echo "<div style=\"width:100px;height:100px;padding:10px;margin:10px;float:left;font-weight:bold;text-align:center;border:1px solid ".$thumb["border"].";color:".$thumb["border"].";background-color:".$thumb["bg"]."\">";
    echo $thumb["label"];
    echo "</div>";
}
?>

Knights of the Round Table

Sir Gaheris

What went wrong 

The entire gallery of thumbnails had only one div shown, no matter how many array elements I copied and pasted into the code!

Why it went wrong 

It was precisely because of all the blind copy-pasting. Note the following highlighted in red. I'd included the array initialization with every array element declaration. So each time I added an array element, before that, I re-initialized the array, causing it to be wiped clean! So the only array element left would always be the most recent one.

<?php
$thumbails=array();
$thumbnails[]["label"]="Sir Lancelot";
$thumbnails[]["border"]="#FF99FF";
$thumbnails[]["bg"]="#AA4488";

$thumbails=array();
$thumbnails[]["label"]="Sir Tristan";
$thumbnails[]["border"]="#336699";
$thumbnails[]["bg"]="#00FFFF";

$thumbails=array();
$thumbnails[]["label"]="Sir Percival";
$thumbnails[]["border"]="#EF7700";
$thumbnails[]["bg"]="#1509FF";

$thumbails=array();
$thumbnails[]["label"]="Sir Lionel";
$thumbnails[]["border"]="#3F4F5F";
$thumbnails[]["bg"]="#119955";

$thumbails=array();
$thumbnails[]["label"]="Sir Galahad";
$thumbnails[]["border"]="#FFFF44";
$thumbnails[]["bg"]="#008899";

$thumbails=array();
$thumbnails[]["label"]="Sir Gaheris";
$thumbnails[]["border"]="#003586";
$thumbnails[]["bg"]="#DD8799";

echo "<h1>Knights of the Round Table</h1>";

foreach ($thumbnails as $thumb)
{
    echo "<div style=\"width:100px;height:100px;padding:10px;margin:10px;float:left;font-weight:bold;text-align:center;border:1px solid ".$thumb["border"].";color:".$thumb["border"].";background-color:".$thumb["bg"]."\">";
    echo $thumb["label"];
    echo "</div>";
}
?>

How I fixed it 

I removed all those lines save for the first instance. Instant gallery!
Sir Lancelot
Sir Tristan
Sir Percival
Sir Lionel
Sir Galahad
Sir Gaheris

Moral of the story

Had to sternly remind myself to go easy on the copy-pasting. That actually is one of the most popular causes of errors.

Thanks for reading. Good knight!
T___T

Tuesday, 25 August 2015

Web Tutorial: Wheel of Fortune (Part 4/4)

Finally, we're down to the last bit. We have a functioning Wheel of Fortune that spins with the application of some real-world physics. What else could we possibly want?

Well, what's the entire purpose of spinning a Wheel of Fortune? Why, to see what cool prizes we won, of course!

We're going to make use of arrays to store information, so if you need to do some reading up, here's a link. (http://www.w3schools.com/js/js_arrays.asp)

Add the following to your JavaScript code. It's an array declaration. It's a multi-dimensional array too. segments is the array, and each element of segments is also an array. There are twelve segments in the wheel.
        <script>
        var segments=[];

        for (i=12;i>=1;i--)
        {
            segments[i]=[];
            segments[i][0]=(i-1)*30;
            segments[i][1]=segments[i][0]+29;
        }


        document.onmousedown=mousedown;
        document.onmouseup=mouseup;

We've done all this with a For loop. This generates twelve segments twelve elements in the array segments. Each element in segments is an array consisting of two (for now) elements - the lowest degree and highest degree of that segment, respectively. So, for example, the first element in segments will be an array with the lowest degree 0 and the highest degree 29. The second element will be an array with the lowest degree 30 and the highest degree 69. That's because each segment in the wheel is 30 degrees.

Now add this to your code. It defines the third element in each array element of segments. This defines the prizes! Have fun with this! I've added random prizes to each of the twelve segments, and left the value blank where there is no prize.
        <script>
        var segments=[];

        for (i=12;i>=1;i--)
        {
            segments[i]=[];
            segments[i][0]=(i-1)*30;
            segments[i][1]=segments[i][0]+29;
        }

        segments[1][2]="";
        segments[2][2]="a plush Teddy Bear";
        segments[3][2]="$50 IKEA voucher";
        segments[4][2]="";
        segments[5][2]="";
        segments[6][2]="$1,000";
        segments[7][2]="a Sony vacuum Cleaner";
        segments[8][2]="a Brietlings Watch";
        segments[9][2]="an all-expenses paid trip to Tokyo";
        segments[10][2]="";
        segments[11][2]="a one-year gym membership";
        segments[12][2]="";


        document.onmousedown=mousedown;
        document.onmouseup=mouseup;


Now add this For loop in your rotatewheel_instant() function.
        function rotatewheel_instant()
        {
            var currentpos=document.getElementById("hidPos");

            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="";
            wheel.style.transitionDuration="0s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="";
            wheel.style.WebkitTransitionDuration="0s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Stopped";
          
            for (i=1;i<=12;i++)
            {
                if (parseInt(currentpos.value)>=segments[i][0]&&parseInt(currentpos.value)<=segments[i][1])
                {
                    if (segments[i][2]=="")
                    {
                        alert("Sorry, you didn't win anything. Better luck next time!");
                    }
                    else
                    {
                        alert("Congratulations! You've won "+segments[i][2]+"!");
                    }
                }
            }  
       
        }


This runs a comparison once your wheel stops spinning. The code compares the last value in hidPos with the values in the array segments, to see if the value in hidPos falls between any of the lowest and highest values of each element. Then, it checks if the third element is a blank value, in which case it pops up a concilatory message, and if not, it pops up a congratulatory message.

There you go, we're done!

You have a spinning Wheel of Fortune that responds to your mouse drag, and then spits out a result upon stopping. There might be a better way of doing this. In the older days, I would have done it using Flash. But as it is, we did this using nothing but a text editor. Now that's awesome.


Down on your luck? Hey... where there's a wheel, there's a way.
T___T

Saturday, 22 August 2015

Web Tutorial: Wheel of Fortune (Part 3/4)

We've got a wheel, and it spins!

Now we're talking.

For an additional layer of interactivity, we'll add mouse events. So right now the wheel rotates according to what value is in the hidPos text box. Our mouse events will put those values in the hidPos text box without having to modify the value manually.

For further reading on JavaScript mouse events, follow this link! (http://www.w3schools.com/jsref/dom_obj_event.asp)

What we're going to do here, is simulate a mouse drag and drop. Make these changes to your JavaScript.
        <script>
        document.onmousedown=mousedown;
        document.onmouseup=mouseup;

        function mousedown(e)
        {

        }          

        function mouseup(e)
        {

        }


        function rotatewheel()
        {
            var currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Spinning...";

            var currentpos=document.getElementById("hidPos");
            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="ease-out";
            wheel.style.transitionDuration="10s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="ease-out";
            wheel.style.WebkitTransitionDuration="10s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentpos.value=parseInt(currentpos.value)%360;

            setTimeout(function(){rotatewheel_instant()},10000);
        }

        function rotatewheel_instant()
        {
            var currentpos=document.getElementById("hidPos");

            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="";
            wheel.style.transitionDuration="0s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="";
            wheel.style.WebkitTransitionDuration="0s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Stopped";          
        }
        </script>


So we've defined two JavaScript functions, mouseup() and mousedown(). And we've set mousedown() to fire off when the mouse button is pressed, and mouseup() to fire off when the mouse button is released. Let's work on mousedown() first.
        <script>
        document.onmousedown=mousedown;
        document.onmouseup=mouseup;

        function mousedown(e)
        {
            if (!e)
            {
                var e=window.event;
            }

            if (document.getElementById("txtStatus").innerHTML=="Stopped")
            {
                document.getElementById("hidMousedownX").value=e.clientX;
                document.getElementById("hidMousedownY").value=e.clientY;
            }
        }          

        function mouseup(e)
        {

        }

        function rotatewheel()
        {
            var currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Spinning...";

            var currentpos=document.getElementById("hidPos");
            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="ease-out";
            wheel.style.transitionDuration="10s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="ease-out";
            wheel.style.WebkitTransitionDuration="10s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentpos.value=parseInt(currentpos.value)%360;

            setTimeout(function(){rotatewheel_instant()},10000);
        }

        function rotatewheel_instant()
        {
            var currentpos=document.getElementById("hidPos");

            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="";
            wheel.style.transitionDuration="0s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="";
            wheel.style.WebkitTransitionDuration="0s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Stopped";        
        }
        </script>


The If-else block here ensures that the wheel cannot be manipulated while the wheel is spinning. It can only be spun when the status is at "Stopped.". The function then captures the x and y co-ordinates of your mouse pointer and sets them as values in the hidMousedownX and hidMousedownY textboxes, respectively.

Try it. Do the values in those textboxes change when you click the mouse?

Now, we're going to work on the mouseup() function.
        <script>
        document.onmousedown=mousedown;
        document.onmouseup=mouseup;

        function mousedown(e)
        {
            if (!e)
            {
                var e=window.event;
            }

            if (document.getElementById("txtStatus").innerHTML=="Stopped")
            {
                document.getElementById("hidMousedownX").value=e.clientX;
                document.getElementById("hidMousedownY").value=e.clientY;
            }
        }          

        function mouseup(e)
        {
            if (!e)
            {
                var e=window.event;
            }

            if (document.getElementById("txtStatus").innerHTML=="Stopped")
            {
                var mousedownx=document.getElementById("hidMousedownX");
                var mousedowny=document.getElementById("hidMousedownY");

                var diffx;
                var diffy;

                var currentpos=document.getElementById("hidPos");

                if (parseInt(mousedownx.value)!=-1&&parseInt(mousedownx.value)!=-1)
                {
                    diffx=Math.abs(e.clientX-parseInt(mousedownx.value));
                    diffy=Math.abs(e.clientY-parseInt(mousedowny.value));

                    if (diffx+diffy>0)
                    {
                        if (diffx>diffy)
                        {
                            currentpos.value=parseInt(currentpos.value)+diffx;
                        }
                        else
                        {
                            currentpos.value=parseInt(currentpos.value)+diffy;
                        }



                        mousedownx.value="-1";
                        mousedowny.value="-1";
                        rotatewheel();
                    }
                }

            }
        }

        function rotatewheel()
        {
            var currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Spinning...";

            var currentpos=document.getElementById("hidPos");
            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="ease-out";
            wheel.style.transitionDuration="10s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="ease-out";
            wheel.style.WebkitTransitionDuration="10s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentpos.value=parseInt(currentpos.value)%360;

            setTimeout(function(){rotatewheel_instant()},10000);
        }

        function rotatewheel_instant()
        {
            var currentpos=document.getElementById("hidPos");

            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="";
            wheel.style.transitionDuration="0s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="";
            wheel.style.WebkitTransitionDuration="0s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Stopped";        
        }
        </script>


Again, the x and y co-ordinates of your mouse pointer are captured. First, we check if the values of hidMousedownX and hidMousedownY are not "-1", and only proceeds if so. This prevents the problem of the user clicking the button outside of the browser window, then releasing the mouse button in the browser window.

And then the difference between the new co-ordinates and the old ones is taken. The x-difference and the y-difference may turn out to be negative, so we use the absolute value. Then we compare the x-difference and the y-difference, and use the bigger value. That value is added to the current value of hidPos.

And then rotatewheel() is run using the new value of hidPos. This ensures that the new value of hidPos is always greater than the old value, and therefore the wheel will always rotate clockwise!

Lastly, the values of  hidMousedownX and hidMousedownY are reset to "-1".

Try it. Click and drag your mouse, then release. The wheel spins faster or slower depending on how great the drag was!

Now do this to your HTML code...
        <div id="info_wrapper">
            <div id="txtStatus">Stopped</div>
            <input type="hidden" id="hidPos" value="0" onchange="rotatewheel();">
            <input type="hidden" id="hidMousedownX" value="-1">
            <input type="hidden" id="hidMousedownY" value="-1">
            <div id="triangle"></div>
        </div>


This hides your textboxes. You no longer need them to be visible.

Next

Your wheel spins in response to the mouse click and drag! But what's the end game here? We need to capture and display the result. That's what we'll be handling next.




Wednesday, 19 August 2015

Web Tutorial: Wheel of Fortune (Part 2/4)

Now that we've got that wheel, it's time to write some code to spin it. I won't lie to you, there's going to be a teeny bit of math in here, but nothing fancy, I promise.

However, before anything else, let's just add one more thing to your wheel. I mean, what's a Wheel of Fortune without a pointer, right?

Just after the body tag, add a triangle div.
    <body>
        <div id="triangle"></div>
        <div id="wheel_wrapper">


Then style the triangle div like so. The point of this part of the tutorial is about JavaScript and I don't want to digress too much. So I just conveniently lifted the CSS code for the triangle from this site. (https://css-tricks.com/examples/ShapesOfCSS/). I'll get to explaining this code another day, in another tutorial. Maybe.
        <style type="text/css">
            #triangle
             {
                width:0px;
                height:0px;
                border-left:10px solid transparent;
                border-right:10px solid transparent;
                border-top: 20px solid #000000;
                margin-left:auto;
                margin-right:auto;
                margin-top:50px;
            }  
 

            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }


And this is what you're going to get. A pointer, so it's more obvious, after spinning, what the result is. It's where the pointer is pointing!


Now for some serious business!

We need to add some displays for your reference, so you can test the results of your spinning. Modify your code as follows, with some placeholders for the data you'll be manipulating.
        <div id="info_wrapper">
            <div id="txtStatus">Stopped</div>
            <input type="text" id="hidPos" value="0">
            <input type="text" id="hidMousedownX" value="-1">
            <input type="text" id="hidMousedownY" value="-1">

            <div id="triangle"></div>
        </div>


You;ve just encased your triangle div in another div info_wrapper, and added a txtStatus div and three textboxes within that div.

txtStatus is meant to display the current status of the wheel. Status is initialized at "Stopped".
hidPos is supposed to show the current rotation of the wheel. Initialized at "0".

hidMouseDownX is supposed to show the x position of your mouse pointer after clicking. Initialized at "-1".

hidMouseDownY is supposed to show the y position of your mouse pointer after clicking. Initialized at "-1".

And  a new CSS class for info_wrapper. Basically sets everything within that div to be in the center.
             #info_wrapper
            {
                width:100%;
                text-align:center;
            }


            #triangle
             {
                width:0px;
                height:0px;
                border-left:10px solid transparent;
                border-right:10px solid transparent;
                border-top: 20px solid #000000;
                margin-left:auto;
                margin-right:auto;
                margin-top:50px;
            }


Here's what it should look like now.


Now for some JavaScript!

Add the following into your head tag.
        <script>
        function rotatewheel()
        {
            var currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Spinning...";

            var currentpos=document.getElementById("hidPos");
            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="ease-out";
            wheel.style.transitionDuration="10s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="ease-out";
            wheel.style.WebkitTransitionDuration="10s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";
        }
        </script>


The rotatewheel() function takes the value in hidPos and rotates the wheel by that many degrees.

First, it changes the status to "Spinning...". Then it changes the wheel_wrapper div's transitionTimingFunction and transitionDuration properties to give it a smooth gradual rotation. Note that all rotations, no matter how big, take 10 seconds! I'll explain why later. Lastly, the value of hidPos is taken and the transform property is set to this value. So the wheel will spin that many degrees, using 10 seconds, and do an "ease-out", meaning it will come to a gradual stop.

Test your rotation!

Modify your code as follows:
        <div id="info_wrapper">
            <div id="txtStatus">Stopped</div>
            <input type="text" id="hidPos" value="0" onchange="rotatewheel();">
            <input type="text" id="hidMousedownX" value="-1">
            <input type="text" id="hidMousedownY" value="-1">
            <div id="triangle"></div>
        </div>


Now when you change the value of hidPos, it runs the rotatewheel() function.

Try it. Change the value of hidPos to, say, 90. Does it rotate 90 degrees? Good. Now change it to 120. What happens? Now change it to 420. You'll notice that the wheel spins again. Now, change it to 160. What happens? Why, the wheel rotates backwards!

Why is this? Well, any lower value from the wheel's current rotation entered into hidPos will cause the wheel to rotate the other way, because it's the most direct way. The wheel was trying to go from 420 degrees to 160 degrees, which is an anti-clockwise rotation.

No worries, we're going to fix this next. Modify your code as follows:
        function rotatewheel()
        {
            var currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Spinning...";

            var currentpos=document.getElementById("hidPos");
            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="ease-out";
            wheel.style.transitionDuration="10s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="ease-out";
            wheel.style.WebkitTransitionDuration="10s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentpos.value=parseInt(currentpos.value)%360;

            setTimeout(function(){rotatewheel_instant()},10000);

        }


The first line sets the value of hidPos to the remainder of the wheel's current rotation after diving by 360. So if you rotate the wheel 420 degrees, the value of hidPos will be changed to 60 (420%360).

The second line calls the rotatewheel_instant() function exactly 10 seconds after the wheel starts spinning. Since the wheel takes exactly 10 seconds to stop, the effect is almost invisible.

Let's write the rotatewheel_instant() function next.
        <script>
        function rotatewheel()
        {
            var currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Spinning...";

            var currentpos=document.getElementById("hidPos");
            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="ease-out";
            wheel.style.transitionDuration="10s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";
            wheel.style.WebkitTransitionTimingFunction="ease-out";
            wheel.style.WebkitTransitionDuration="10s";
            wheel.style.WebkitTransform="rotate("+currentpos.value+"deg)";

            currentpos.value=parseInt(currentpos.value)%360;

            setTimeout(function(){rotatewheel_instant()},10000);
        }

        function rotatewheel_instant()
        {
            var currentpos=document.getElementById("hidPos");
            

            var wheel=document.getElementById("wheel_wrapper");
            wheel.style.transitionTimingFunction="";
            wheel.style.transitionDuration="0s";
            wheel.style.transform="rotate("+currentpos.value+"deg)";

            wheel.style.WebkitTransitionTimingFunction="";
            wheel.style.
WebkitTransitionDuration="0s";
            wheel.style.
WebkitTransform="rotate("+currentpos.value+"deg)";

            currentstatus=document.getElementById("txtStatus");
            currentstatus.innerHTML="Stopped";         
        }


The rotatewheel_instant() function does pretty much the same thing as the rotatewheel() function, with three notable differences.

Firstly, it rotates the wheel, but instead of the full value of hidPos, it takes the value of hidPos, Modulus 360. This much is inferred in the final few lines of rotatewheel() function before calling rotatewheel_instant().

Secondly, it sets the wheel_wrapper div's transitionTimingFunction property to nothing and transitionDuration property 0s to make the transition instant.

Thirdly, it sets the status to "Stopped".

Try your code aga.in. Set hidPos to 420. Watch the wheel spin, and watch the value "reset" itself to 60. (420%360), Now set the value of hidPos to 160. Does it go anti-clockwise anymore? It doesn't! Because the rotation of wheel_wrapper is now 60 instead of 420. Only the rotatewheel_instant() function made the change instantaneously as soon as the wheel stopped spinning, so you never saw it. But if you deliberately use a value lower than 60, such as 20, it will rotate anti-clockwise. The trick here is to ensure that the value is never lower than the value in hidPos. This will be covered in the next part of the web tutorial.

Any other problems?

Well, you may have noticed some jarring jerkiness when the wheel rotates. But that really depends on how large your screen is. That's because you're rotating wheel_wrapper. And wheel_wrapper is actually a square, even if it looks like a circle. So when the corner of the square goes beyond the bottom edge of the screen, what happens? A scroll bar appears on the right side of your window, and this causes the jarring effect!

This is illustrated with a visible border for wheel_wrapper. See how the scroll bar appears when the corner of wheel_wrapper goes way past the bottom edge of the screen?



We're going to fix that, by ensuring that there is a scroll bar right from the get-go. Make this adjustment to the wheel_wrapper CSS specification.
            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 400px auto;
            }


Here, we set the bottom margin to 400px so the scroll bar always appears, because the screen isn't large enough to accommodate a 500 by 500 pixel div and a bottom margin of 400 pixels. Clever, eh? Run your code again, see if there's still jerking!

Finally...

I said I'd explain why the transition of the rotation is set to 10 seconds no matter how large the rotation. See, the larger the rotation, the faster the wheel should spin, right? After all, if there's a larger rotation, that means more force was applied on the wheel. So what's the formula for speed? Some basic Physics here...

Velocity = Distance / Time


Now assuming Time is a constant of 10 seconds, and Distance is the number of degrees the wheel has to rotate, it makes sense that the wheel will spin faster or slower if we vary the number of degrees rotated!

Next

Thanks very much for your attention. The next part should be slightly less math-y. Right now, all we've done is cause the wheel to rotate based on what values you manually enter into the hidPos text box. But no self-respecting user is going to go through that. We're going to write functions that allow you to accomplish the rotation with a mouse click and drag!


Saturday, 15 August 2015

Web Tutorial: Wheel of Fortune (Part 1/4)

Are ya feeling lucky?!

Let's do something fun today. Let's make a Wheel of Fortune using HTML, CSS and JavaScript. It'll be able to spin when the mouse cursor is dragged over it, and when it stops, the prize won will be displayed!

For starters, we'll need a wheel. (well, duh!) And the obvious question here is...

How does one make a multi-segmented wheel in CSS?

I'm going to dedicate the first part of this web tutorial, to showing you just how you pull off this nifty little trick.

Starting HTML:
<!DOCTYPE html>
<html>
    <head>
        <title>Wheel</title>
    </head>

    <body>
        <div id="wheel_wrapper">

        </div>
    </body>
</html>


The wheel_wrapper div is meant to hold all the separate parts of the wheel together. Because in the later parts of the tutorial, this div is what we're going to spin.

Next, we're going to style wheel_wrapper, like so. I've made it 500 by 500 pixels, and centered it in the middle of the screen by setting the margin property to 0 auto 0 auto. That's shorthand for: no margin for top and bottom, auto for left and right.
<!DOCTYPE html>
<html>
    <head>
        <title>Wheel</title>

        <style type="text/css">
            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }
        </style>

    </head>

    <body>
        <div id="wheel_wrapper">

        </div>
    </body>
</html>


Now, I want you to do yourself a favor and add this to your CSS. This line basically ensures that all divs will have a nice red outline so you can easily visualize what we're trying to accomplish here.

        <style type="text/css">
            div {border:1px solid #ff0000;}

            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }
        </style>


See what I mean? Things could get a little complicated from here on, and I don't want you to get confused. We can delete that line later. But, for now, you have a visual aid.


OK, now. Basic question. What is a circle made of? Two semi-circles, of course! So we're going to create the first semicircle, like so.
<!DOCTYPE html>
<html>
    <head>
        <title>Wheel</title>

        <style type="text/css">
            div {border:1px solid #ff0000;}

            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }

            .semicircle1
            {
                margin-left:-250px;
            }

            .segment_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
            }

        </style>
    </head>

    <body>
        <div id="wheel_wrapper">
            <div class="segment_wrapper semicircle1">

            </div>
        </div>
    </body>
</html>


You'll notice that this particular div has two CSS classes attached to it. While the semicircle1 class is particular to this div, the segment_wrapper class will be recycled for the other semicircle we'll create later on.

So let's take a look at what these classes accomplish.

segment_wrapper is so named because this div is meant to encapsulate all the different segments of the semicircle. These segments will be dealt with soon. It has the full size of the wheel_wrapper div (500 by 500 pixels).

semicircle1 has the margin-left property set to -250 pixels, which means only the right half of the div is inside the wheel_wrapper div.



Next, we'll be placing segments of the semicircle inside that div.Let's begin with the first segment.

    <body>
        <div id="wheel_wrapper">
            <div class="segment_wrapper semicircle1">
                    <div class="segment1 segment">
                        <p>A</p>
                    </div>

            </div>
        </div>
    </body>
</html>


Again, two CSS classes are attached to it - segment1, and segment. segment is meant to be reused for other segment divs, while segment1 is particular to this one only.

The CSS classes defined as follows:
        <style type="text/css">
            div {border:1px solid #ff0000;}

            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }

            .semicircle1
            {
                margin-left:-250px;
            }

            .segment_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
            }

            .segment
            {
                width:250px;
                height:250px;
                position:absolute;
                margin-left:250px;
                margin-top:0px;
                transform-origin:0% 100%;
                -webkit-transform-origin:0% 100%;
            }

            .segment1
            {
                background-color:#44FF55;
                z-index:10;
                transform: rotate(0deg);
                -webkit-transform: rotate(0deg);
            }

        </style>


Notice that segment's width and height properties are half of the parent div, i.e. 250 pixels? That's not a coincidence. transform-origin is set to the bottom right of the div. This is common for all segments. Now this is the first segment (thus the class segment1),and it will be starting at 0 degrees. I've set the background-color property to a random color, in this case, a nice aqua. z-index is at 10. Take note, this will be relevant soon.

See how it looks now!


Now add the other 6 segments in the semicircle.
    <body>
        <div id="wheel_wrapper">
            <div class="segment_wrapper semicircle1">
                    <div class="segment1 segment">
                        <p>A</p>
                    </div>
                    <div class="segment2 segment">
                        <p>B</p>
                    </div>
                    <div class="segment3 segment">
                        <p>C</p>
                    </div>
                    <div class="segment4 segment">
                        <p>D</p>
                    </div>
                    <div class="segment5 segment">
                        <p>E</p>
                    </div>
                    <div class="segment6 segment">
                        <p>F</p>
                    </div>
            </div>
        </div>
    </body>
</html>


And add in the classes segment2 to segment6. For each of these, different random colors have been assigned as background colors. The z-index property increases by 10 for each one, because they are all going to overlap, with the largest numbered segment being at the top.

Also, they have all been rotated a certain number of degrees. Some simple math here: How many degrees are there in a semicircle? 180. So for 6 segments, each one would be rotated by an additional 30 degrees.
        <style type="text/css">
            div {border:1px solid #ff0000;}

            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }

            .semicircle1
            {
                margin-left:-250px;
            }

            .segment_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
            }

            .segment
            {
                width:250px;
                height:250px;
                position:absolute;
                margin-left:250px;
                margin-top:0px;
                transform-origin:0% 100%;
                -webkit-transform-origin:0% 100%;
            }

            .segment1
            {
                background-color:#44FF55;
                z-index:10;
                transform: rotate(0deg);
            }

            .segment2
            {
                background-color:#990033;
                z-index:20;
                transform: rotate(30deg);
                -webkit-transform: rotate(30deg);
            }

            .segment3
            {
                background-color:#AA00AA;
                z-index:30;
                transform: rotate(60deg);
                -webkit-transform: rotate(60deg);
            }

            .segment4
            {
                background-color:#44FFAA;
                z-index:40;
                transform: rotate(90deg);
                -webkit-transform: rotate(90deg);
            }

            .segment5
            {
                background-color:#FF9900;
                z-index:50;
                transform: rotate(120deg);
                -webkit-transform: rotate(120deg);
            }

            .segment6
            {
                background-color:#2299FF;
                z-index:60;
                transform: rotate(150deg);
                -webkit-transform: rotate(150deg);
            }
        </style>


This is how it should look now.


Doesn't look much like a semicircle, does it? We're going to rectify that. Make the following changes to your segment_wrapper class.

            .segment_wrapper
            {
                width:500px;
                height:500px;
                border-radius:50%;
                overflow:hidden;

                position:relative;
            }

border-radius:50% makes the corners of your div perfectly round, making it a circle. overflow:hidden ensures that all the corners of the segments that stick outside of the div, are hidden!

Getting into shape now...


Now what we need to do is trim off the part of the semicircle that sticks out. Enclose your div in an outer div, like so.
    <body>

        <div id="wheel_wrapper">
            <div class="semicircle_wrapper wheel1">
                <div class="segment_wrapper semicircle1">
                    <div class="segment1 segment">
                        <p>A</p>
                    </div>
                    <div class="segment2 segment">
                        <p>B</p>
                    </div>
                    <div class="segment3 segment">
                        <p>C</p>
                    </div>
                    <div class="segment4 segment">
                        <p>D</p>
                    </div>
                    <div class="segment5 segment">
                        <p>E</p>
                    </div>
                    <div class="segment6 segment">
                        <p>F</p>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>


And again, it'll have two CSS classes attached to it - semicircle_wrapper and wheel1. Add those classes below.
        <style type="text/css">
            div {border:1px solid #ff0000;}

            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }

            .semicircle_wrapper
            {
                width:250px;
                height:500px;
                overflow:hidden;
                position:relative;
            }

            .wheel1
            {
                float:right;
            }


            .semicircle1
            {
                margin-left:-250px;
            }

            .segment_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
            }

            .segment
            {
                width:250px;
                height:250px;
                position:absolute;
                margin-left:250px;
                margin-top:0px;
                transform-origin:0% 100%;
                -webkit-transform-origin:0% 100%;
            }

            .segment1
            {
                background-color:#44FF55;
                z-index:10;
                transform: rotate(0deg);
                -webkit-transform: rotate(0deg);
            }

            .segment2
            {
                background-color:#990033;
                z-index:20;
                transform: rotate(30deg);
                -webkit-transform: rotate(30deg);
            }

            .segment3
            {
                background-color:#AA00AA;
                z-index:30;
                transform: rotate(60deg);
                -webkit-transform: rotate(60deg);
            }

            .segment4
            {
                background-color:#44FFAA;
                z-index:40;
                transform: rotate(90deg);
                -webkit-transform: rotate(90deg);
            }

            .segment5
            {
                background-color:#FF9900;
                z-index:50;
                transform: rotate(120deg);
                -webkit-transform: rotate(120deg);
            }

            .segment6
            {
                background-color:#2299FF;
                z-index:60;
                transform: rotate(150deg);
                -webkit-transform: rotate(150deg);
            }
        </style>


semicircle_wrapper will be reused for the other semicircle. And wheel1 is just for this particular div.

semicircle_wrapper has the full height of the wheel, but only half the width. Just like, well, a semicircle. Again, the overflow property has been set to hidden, to trim off the fat.

wheel1 sets the div to float to the right side of the containing div, like so.


Now, remove the first line of your CSS that says div {border:1px solid #ff0000;}, and you have a beautifully colored semicircle!


Ain't she a beauty? So all we have to do now, is create another semicircle with segments, and this one will be the other half of the wheel. I'll put in the code for you, and the CSS.
<!DOCTYPE html>
<html>
    <head>
        <title>Wheel</title>

        <style type="text/css">
            #wheel_wrapper
            {
                width:500px;
                height:500px;
                position:relative;
                margin:0 auto 0 auto;
            }

            .semicircle_wrapper
            {
                width:250px;
                height:500px;
                overflow:hidden;
                position:relative;
            }

            .wheel1
            {
                float:right;
            }

            .wheel2
            {
                float:left;
            }


            .semicircle1
            {
                margin-left:-250px;
            }

            .semicircle2
            {
                margin-left:0px;
            }


            .segment_wrapper
            {
                width:500px;
                height:500px;
                border-radius:50%;
                overflow:hidden;
                position:relative;
            }

            .segment
            {
                width:250px;
                height:250px;
                position:absolute;
                margin-left:250px;
                margin-top:0px;
                transform-origin:0% 100%;
                -webkit-transform-origin:0% 100%;
            }

            .segment1
            {
                background-color:#44FF55;
                z-index:10;
                transform: rotate(0deg);
                -webkit-transform: rotate(0deg);
            }

            .segment2
            {
                background-color:#990033;
                z-index:20;
                transform: rotate(30deg);
                -webkit-transform: rotate(30deg);
            }

            .segment3
            {
                background-color:#AA00AA;
                z-index:30;
                transform: rotate(60deg);
                -webkit-transform: rotate(60deg);
            }

            .segment4
            {
                background-color:#44FFAA;
                z-index:40;
                transform: rotate(90deg);
                -webkit-transform: rotate(90deg);
            }

            .segment5
            {
                background-color:#FF9900;
                z-index:50;
                transform: rotate(120deg);
                -webkit-transform: rotate(120deg);
            }

            .segment6
            {
                background-color:#2299FF;
                z-index:60;
                transform: rotate(150deg);
                -webkit-transform: rotate(150deg);
            }

            .segment7
            {
                background-color:#FF2255;
                z-index:10;
                transform: rotate(180deg);


            }

            .segment8
            {
                background-color:#AAFF22;
                z-index:20;
                    transform: rotate(210deg);
            }

            .segment9
            {
                background-color:#9944EE;
                z-index:30;
                    transform: rotate(240deg);
            }

            .segment10
            {
                background-color:#FF3300;
                z-index:40;
                transform: rotate(270deg);
                -webkit-transform: rotate(270deg);
            }

            .segment11
            {
                background-color:#338800;
                z-index:50;
                transform: rotate(300deg);

                -webkit-transform: rotate(300deg); 
            }

            .segment12
            {
                background-color:#EE33AA;
                z-index:60;
                transform: rotate(330deg);

                -webkit-transform: rotate(330deg);
             }
        </style>
    </head>

    <body>
        <div id="wheel_wrapper">
            <div class="semicircle_wrapper wheel2">
                <div class="segment_wrapper semicircle2">
                    <div class="segment7 segment">
                        <p>G</p>
                    </div>
                    <div class="segment8 segment">
                        <p>H</p>
                    </div>
                    <div class="segment9 segment">
                        <p>I</p>
                    </div>
                    <div class="segment10 segment">
                        <p>J</p>
                    </div>
                    <div class="segment11 segment">
                        <p>K</p>
                    </div>
                    <div class="segment12 segment">
                        <p>L</p>
                    </div>
                </div>
            </div>


            <div class="semicircle_wrapper wheel1">
                <div class="segment_wrapper semicircle1">
                    <div class="segment1 segment">
                        <p>A</p>
                    </div>
                    <div class="segment2 segment">
                        <p>B</p>
                    </div>
                    <div class="segment3 segment">
                        <p>C</p>
                    </div>
                    <div class="segment4 segment">
                        <p>D</p>
                    </div>
                    <div class="segment5 segment">
                        <p>E</p>
                    </div>
                    <div class="segment6 segment">
                        <p>F</p>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>


And this is how it should look now.


Great work! We now have a full wheel of 12 different colored segments, labelled A to L.

Next

We'll need to control this little beauty's spinning. I'll show you the JavaScript codes it takes to set this thing in motion.