Winwheel.js tutorial: #10 Prize Pointers and Wheel Backgrounds


In this tutorial we will cover how to create a pointer to indicate the prize, how to add things in the background of the wheel, and how to position the pointer and a segment under the pointer.


There is no built in functionality to create a pointer to indicate the prize when the spinning of the wheel has stopped; it is up to you to create and display a pointer, there are a few ways, with varying levels of complexity, to do this...



Background image


One of the easiest ways to display a pointer is to contain the canvas within a div and give that div a background image using the CSS background-image property. You can pop a triangle, arrow, or anything you like on the image to indicate the prize and even include other things on the image such as legs etc to make it appear the prize wheel is on some sort of stand.


In this example the image on the left is set as the background image of a div the canvas on the right is located in.


Canvas not supported, please user another browser.

You can use the centerX and centerY properties of the wheel object to position the wheel correctly over the background, and the outerRadius property to set the size of the wheel to fit the background.


CSS...
#canvasContainer {
    background-image: url(/images/wheelBackground.png);
    background-repeat: no-repeat;   /* Ensure that background does not repeat */
    background-position: center;    /* Ensure image is centred */
    width: 400px;                   /* Width and height should at least be the same as the canvas */
    height: 400px;
}

HTML...
<div id="canvasContainer">
    <canvas id="canvas" width="400" height="400">
        Canvas not supported, please user another browser.
    </canvas>
</div>

Javascript...
let wheelWithBackground = new Winwheel({
    'outerRadius' : 146,    // Use these three properties to
    'centerX'     : 200,    // correctly position the wheel
    'centerY'     : 201,    // over the background.
    'lineWidth'   : 2,
    'numSegments' : 4,
    'segments'    :
    [
        {'fillStyle' : '#eae56f', 'text' : 'Prize One'},
        {'fillStyle' : '#89f26e', 'text' : 'Prize Two'},
        {'fillStyle' : '#7de6ef', 'text' : 'Prize Three'},
        {'fillStyle' : '#e7706f', 'text' : 'Prize Four'}
    ]
});

One big advantage of this way of displaying a pointer over ones involving canvas functions, besides it being very easy to implement, is that when it comes time to spin the wheel there is no additional work you need to do to re-draw the pointer, as it is not even part of the canvas.




Image positioned on top


Another way to show a pointer on the wheel is to position an image over the top of the canvas. The advantage of this way of doing it over the background image method is that the pointer can appear over the top of the wheel, which is not possible when the pointer is in the background.


Here the pointer image on the left is placed over the wheel on the canvas on the right...

Canvas not supported, please user another browser. Prize Pointer

This is a little more complex than the background method as more styles are required to absolutely position the image at the desired location over the top of the canvas. A z-index should also be applied to the canvas and the pointer image to ensure that it appears on top of the canvas.


CSS...

#canvasContainer {
    position: relative;
    width: 300px;
}

#canvas {
    z-index: 1;
}

#prizePointer {
    position: absolute;
    left: 125px;
    top: 10px;
    z-index: 999;
}


HTML and Javascript...

<div id="canvasContainer">
    <canvas id="canvas" width="30" height="300">
        Canvas not supported, please user another browser.
    </canvas>
    <img id="prizePointer" src="basic_pointer.png" alt="V" />
</div>
<script>
    let theWheel = new Winwheel({
        'numSegments' : 4,
        'outerRadius' : 110,
        'textFontSize': 15,
        'segments'    :
        [
            {'fillStyle' : '#eae56f', 'text' : 'Prize One'},
            {'fillStyle' : '#89f26e', 'text' : 'Prize Two'},
            {'fillStyle' : '#7de6ef', 'text' : 'Prize Three'},
            {'fillStyle' : '#e7706f', 'text' : 'Prize Four'}
        ]
    });
</script>



Draw a triangle or other shape on the canvas


A more complex way to display a pointer is to write code that uses canvas lineTo, moveTo, fill and stroke functions to draw a triangle, arrow, or other shape on the canvas itself.


Apart from being more complex, creating the pointer in this way requires you to re-draw the pointer every loop of animation, such as when spinning, after the wheel is rendered because the wheel will overwrite the pointer from the previous loop.


In the following example a triangle is drawn on the first wheel and an arrow on the second...



    // Create wheel objects.
    let tp = new Winwheel({
        'canvasId'    : 'trianglePointer',
        'outerRadius' : 110,
        'fillStyle'   : '#eae56f'
    });

    let ap = new Winwheel({
        'canvasId'    : 'arrowPointer',
        'outerRadius' : 110,
        'fillStyle'   : '#89f26e'
    });

    // Draw triangle on the first canvas by getting the canvas
    // then using its 2d context to draw lines to make a triangle.
    let tcanvas = document.getElementById('trianglePointer');
    let tx = tcanvas.getContext('2d');

    // Ensure that have context before calling function to draw.
    if (tx) {
        drawTriangle();
    }

    // Get second canvas context and use code to draw arrow.
    let acanvas = document.getElementById('arrowPointer');
    let ax = acanvas.getContext('2d');

    if (ax) {
		drawArrow();
    }

    // Put draw code in a function since would have to call this
    // each frame of the animation to re-draw the pointer.
    function drawTriangle()
    {
        tx.strokeStyle = '#000000';     // Set line colour.
        tx.fillStyle   = 'aqua';        // Set fill colour.
        tx.lineWidth   = 2;
        tx.beginPath();                 // Begin path.
        tx.moveTo(175, 20);             // Move to initial position.
        tx.lineTo(235, 20);             // Draw lines to make the shape.
        tx.lineTo(205, 80);
        tx.lineTo(176, 20);
        tx.stroke();                    // Complete the path by stroking (draw lines).
        tx.fill();                      // Then fill with colour.
    }

    function drawArrow()
    {
        ax.strokeStyle = '#000000';
        ax.fillStyle   = 'yellow';
        ax.lineWidth   = 2;
        ax.beginPath();
        ax.moveTo(195, 10);
        ax.lineTo(195, 30);
        ax.lineTo(165, 30);
        ax.lineTo(205, 70);
        ax.lineTo(245, 30);
        ax.lineTo(215, 30);
        ax.lineTo(215, 10);
        ax.lineTo(195, 10);
        ax.stroke();
        ax.fill();
    }

Code-drawn Triangle Pointer
Canvas not supported, please user another browser.
Code-drawn Arrow Pointer
Canvas not supported, please user another browser.

To learn more about the canvas functions such as moveTo() and lineTo() search the net. An excellent book I can recommend about drawing things on HTML canvas is "Core HTML Canvas: Graphics, Animation, and Game Development" by David Geary.


Remember that anything you draw to the canvas will be wiped each iteration of the animation loop when the wheel spins so you will need to re-draw the pointer. See the animation tutorials for more about this.




Draw an image on the canvas


Yet another way to display a pointer, if you want to draw it directly to the canvas, is to load an image and then place the image on the canvas using the drawImage() function. Again you will have to re-draw the image every loop of the animation, such as when the wheel is spinning.


In this example we will draw the fancy hand pointer image directly on the canvas rotating it to point in from top-right corner.



    // Create wheel object.
    let handWheel = new Winwheel({
        'canvasId'    : 'handWheel',
        'fillStyle'   : '#7de6ef',
        'outerRadius' : 150,
        'centerY'     : 230,
        'numSegments' : 10,
        'segments'    :
        [
            {'fillStyle' : '#e7706f'}
        ]
    });

    // Create image in memory.
    let handImage = new Image();

    // Set onload of the image to anonymous function to draw on the canvas once the image has loaded.
    handImage.onload = function()
    {
        let handCanvas = document.getElementById('handWheel');
        let ctx = handCanvas.getContext('2d');

        if (ctx) {
            ctx.save();
            ctx.translate(200, 150);
            ctx.rotate(handWheel.degToRad(-40));  // Here I just rotate the image a bit.
            ctx.translate(-200, -150);
            ctx.drawImage(handImage, 255, 110);   // Draw the image at the specified x and y.
            ctx.restore();
        }
    };

    // Set source of the image. Once loaded the onload callback above will be triggered.
    handImage.src = 'pointing_hand.png';

Canvas not supported, please user another browser.


Pointer Location


Note that the pointer does not have to be at the top of the wheel as shown in the previous examples; it can be anywhere around the wheel you desire from 0 to 360 degrees.


0 degrees is at the top 12 o'clock position, 90 degrees is at the 3 o'clock, and 180 degrees is at the 6 o'clock position.


The only thing needed from a code point of view if you are using the Prize Detection or any other features that use the location of the pointer in their calculations, is to set the location of the pointer using the pointerAngle property of the wheel object to match where you are showing the pointer on screen.


The pointerAngle property is covered in depth in tutorial #13 Getting the segment (prize) pointed to.




Positioning a segment under the prize pointer


Using the rotationAngle property you can change the rotation of the wheel to do things such as seen below where the rotation angle is used to rotate the wheel anti-clockwise slightly so the gold "super prize" segment is evenly half-and-half under the prize pointer at the top of the wheel.


The value of rotationAngle can be any positive or negative value 0-360 degrees (or more). The rotationAngle is actually the property changed during animations to make the wheel spin.


Default 6 segment wheel:
Canvas not supported, please user another browser.
rotationAngle is the default (0 degrees)
rotationAngle adjusted 6 segment wheel:
Canvas not supported, please user another browser.
'rotationAngle' : -30

        // Create the second wheel shown above.
        let adjustedWheel = new Winwheel({
            'numSegments'     : 6,
            'textOrientation' : 'vertical',
            'textFontFamily'  : 'Courier',
            'centerY'         : 220,
            'outerRadius'     : 170,
            'textFontSize'    : 14.5,
            'rotationAngle'   : -30,        // Rotate wheel slightly anti-clockwise.
            'segments'        :
            [
                {'fillStyle' : 'gold', 'Text' : 'SUPER PRIZE'},
                {'text' : '2'},
                {'text' : '3'},
                {'text' : '4'},
                {'text' : '5'},
                {'text' : '6'}
            ]
        });

        // Get canvas context from the wheel object.
        c = adjustedWheel.ctx;

        // Create pointer.
        if (c) {
            c.save();
            c.lineWidth = 2;
            c.strokeStyle = 'black';
            c.fillStyle = 'black';
            c.beginPath();
            c.moveTo(180, 10);
            c.lineTo(220, 10);
            c.lineTo(200, 42);
            c.lineTo(180, 10);
            c.stroke();
            c.fill();
            c.restore();
        }


Previous:
< #9 Creating a Wheel with an Image
Next:
#11: Animation Introduction and Spinning Basics >