Wednesday 21 December 2022

Web Tutorial: The Christmas Flipbook (Part 3/4)

Now that we have the book object and have created the views property, let's add another property, currentView. This is an integer that tells you what view in views is active at the moment. The default is 0.
let book =
{
    views:
    [
        {
            right:
            {
                class: "cover",
                content: "<p style='text-align:center'><img src='flipbook_cover.png'/></p>"
            }
        },
        {
            left:
            {
                class: "cover",
                content: "<p style='margin-top:10%;color:rgba(10,0,0,0.5);text-align:center'><b>A flipbook project by<br /><i>TeochewThunder</i></b></p><p style='text-align:center'><img src='tt.png' /></p>"                    
            },
            right:
            {
                class: "page",
                pagenumber: 1,
                content: "<p style='text-align:justify'>Christmas is an annual festival commemorating the birth of Jesus Christ, observed primarily on December 25 as a religious and cultural celebration among billions of people around the world.</p><p style='text-align:justify'>A feast central to the Christian liturgical year, it is preceded by the season of Advent or the Nativity Fast and initiates the season of Christmastide, which historically in the West lasts twelve days and culminates on Twelfth Night. Christmas Day is a public holiday in many countries, is celebrated religiously by a majority of Christians, as well as culturally by many non-Christians, and forms an integral part of the holiday season organized around it.</p>"    
            }
        },
        {
            left:
            {
                class: "page",
                pagenumber: 2,
                content: "<img align='left' style='padding:5px' src='flipbook00.jpg'/><p style='text-align:justify'>A special Christmas family meal is traditionally an important part of the holiday's celebration, and the food that is served varies greatly from country to country. Some regions have special meals for Christmas Eve, such as Sicily, where 12 kinds of fish are served. In the United Kingdom and countries influenced by its traditions, a standard Christmas meal includes turkey, goose or other large bird, gravy, potatoes, vegetables, sometimes bread and cider.</p>"                
            },
            right:
            {
                class: "page",
                pagenumber: 3,
                content: "<p><i>A Christmas Carol</i></p><iframe width='200' height='200' src='https://www.youtube.com/embed/iN6IMZFwY50'></iframe>"    
            }
        },
        {
            left:
            {
                class: "page",
                pagenumber: 4,
                content: "<p><i>Joy to the world<br /><br />The Lord is come<br /><br />Let Earth receive her King!<br /><br />Let every heart<br /><br />Prepare him room<br /><br />And heaven and nature sing<br /><br />And heaven and nature sing<br /><br />And heaven, heaven and nature sing</i></p>"                
            },
            right:
            {
                class: "page",
                pagenumber: 5,
                content: "<div style='margin-top:10%;width:200px;height:150px;background: url(flipbook01.jpg) left top no-repeat;filter:opacity(0.5);outline:5px solid rgba(200,200,0,0.5)'></div>"    
            }
        },
        {
            left:
            {
                class: "page",
                content: "<p style='margin-top:10%;text-align:center'><i>Merry Christmas<br />and Happy New Year!</i></p>"                
            },
            right:
            {
                class: "cover",
                content: ""    
            }
        },
        {
            left:
            {
                class: "cover",
                content: ""                
            }
        }
    ],
    currentView: 0

}


After this, we define three methods - flip(), renderView() and renderPage().
currentView: 0,
flip: function()
{

},
renderView: function()
{

},
renderPage: function()
{

}


flip() has a parameter, dir. dir is an integer that defines what direction the book is being flipped - forwards or backwards.
flip: function(dir)
{

},
renderView: function()
{

},
renderPage: function()
{

}


renderPage() has two parameters. The first is id, which is the id of the placeholder to be filled with content. The second is obj, which is an object that houses the various content to populate aforementioned placeholder with.
flip: function(dir)
{

},
renderView: function()
{

},
renderPage: function(id, obj)
{

}


The simplest method of all is flip(). We aren't going to do much for now, just have it perform in a really basic way. dir has two possible values; 1 or -1. currentViews plus the value of dir will determine the resultant view. So naturally, we have guard clauses that ensure that the result can never be below 0 or above the size of views, less 1.
flip: function(dir)
{
    if (this.currentView + dir < 0) return;
    if (this.currentView + dir > this.views.length -1) return;

},


If, at this point, the method has not exited, add the value of dir to currentViews.
flip: function(dir)
{
    if (this.currentView + dir < 0) return;
    if (this.currentView + dir > this.views.length -1) return;
    
    this.currentView = this.currentView + dir;
},


Then follow up with a call to renderView() now that the value of currentView has changed.
flip: function(dir)
{
    if (this.currentView + dir < 0) return;
    if (this.currentView + dir > this.views.length -1) return;
    
    this.currentView = this.currentView + dir;
    
    this.renderView();
},


And since we're on this, make the buttons call this method. The left button calls flip() with an argument of -1, and the right button calls flip() with an argument of 1.
<div id="dashboard">
    <button onclick="book.flip(-1)">&#9664;</button>
    <button onclick="book.flip(1)">&#9654;</button>
</div>


Great! Now let's do renderView(). Begin by clearing all divs that are styled using the placeholder CSS class, of content.
renderView: function()
{
    $(".pageholder").html("");
},


Now things might get slightly crazy. There are cases where you want to populate the left placeholders, cases where you want to populate the right placeholders, and cases where you want to do both. So if currentView is greater than 0, then there is content for the left (see the views array). If currentView is less than the size of views minus 1, then there is content for the right side.
renderView: function()
{
    $(".pageholder").html("");

    if (this.currentView > 0)
    {

    }

    if (this.currentView < this.views.length - 1)
    {

    }

},


Next, we call renderPage() in the If blocks to render the pages. The arguments to pass in are, the ids - left_front for the left and right_front for the right - and the object containing the content. In the case of the latter, we want the current element of views pointed to by currentView, and then we want the left or right property depending on whether we are rendering for the left or right.
renderView: function()
{
    $(".pageholder").html("");

    if (this.currentView > 0)
    {
        this.renderPage("left_front", this.views[this.currentView].left);
    }

    if (this.currentView < this.views.length - 1)
    {
        this.renderPage("right_front", this.views[this.currentView].right);
    }
},


The next series of If blocks are nested. We check if rendering covers as a background (with pages as the foreground) are needed. They are needed for the left side if currentView is 2 or more. They are also needed for the right side if currentView is less than the second last view in views.
renderView: function()
{
    $(".pageholder").html("");

    if (this.currentView > 0)
    {
        if (this.currentView > 2)
        {
            
        }

        
        this.renderPage("left_front", this.views[this.currentView].left);
    }

    if (this.currentView < this.views.length - 1)
    {
        if (this.currentView < this.views.length - 3)
        {
            
        }

        
        this.renderPage("right_front", this.views[this.currentView].right);
    }
},


In these cases, we call renderPage(). We pass in the ids - left_back for the left and right_back for the right - and the object containing the inside covers. You will need to check your views array for this. But the logic is sound!
renderView: function()
{
    $(".pageholder").html("");

    if (this.currentView > 0)
    {
        if (this.currentView > 2)
        {
            this.renderPage("left_back", this.views[1].left);
        }
        
        this.renderPage("left_front", this.views[this.currentView].left);
    }

    if (this.currentView < this.views.length - 1)
    {
        if (this.currentView < this.views.length - 3)
        {
            this.renderPage("right_back", this.views[this.views.length - 2].right);
        }
        
        this.renderPage("right_front", this.views[this.currentView].right);
    }
},


Got all that? Cool. We'll work on renderPage() next. First, create a div. Then use the addClass() method to add the class to the div. obj should have that data, thus div will be styled using either cover or page.
renderPage: function(id, obj)
{
    var div = $("<div></div>");
    div.addClass(obj.class);
}


Declare pagenumber as another div.
renderPage: function(id, obj)
{
    var div = $("<div></div>");
    div.addClass(obj.class);
    
    var pagenumber = $("<div></div>");
}


If pagenumber is a property of obj (remember some of these objects don't have page numbers), use the addClass() method to style pagenumber using the pagenumber CSS Class, and use the html() method to add the pagenumber property into the div. Finally, append the entire thing into div.
renderPage: function(id, obj)
{
    var div = $("<div></div>");
    div.addClass(obj.class);
    
    var pagenumber = $("<div></div>");
    
    if (obj.pagenumber)
    {
        pagenumber.addClass("pagenumber");
        pagenumber.html(obj.pagenumber);
        div.append(pagenumber);
    }

}


Then declare content as another div. Insert the content property of obj using the html() method. And finally, use the append() method to slip content into div. This happens whether or not there's a page number, so we will have this outside of the If block.
renderPage: function(id, obj)
{
    var div = $("<div></div>");
    div.addClass(obj.class);
    
    var pagenumber = $("<div></div>");
    
    if (obj.pagenumber)
    {
        pagenumber.addClass("pagenumber");
        pagenumber.html(obj.pagenumber);
        div.append(pagenumber);
    }
    
    var content = $("<div></div>");
    content.html(obj.content);
    div.append(content);

}


And then use append() to populate the contents of the placeholder with id as its id, with div.
renderPage: function(id, obj)
{
    var div = $("<div></div>");
    div.addClass(obj.class);

    if (id == "left_front" || id == "right_front")
    {
        div.addClass("rotatable");
    }

    var pagenumber = $("<div></div>");

    if (obj.pagenumber)
    {
        pagenumber.addClass("pagenumber");
        pagenumber.html(obj.pagenumber);
        div.append(pagenumber);
    }

    var content = $("<div></div>");
    content.html(obj.content);
    div.append(content);

    $("#" + id).append(div);
}


At this point, you should be running renderView() on page load.
        var content = $("<div></div>");
        content.html(obj.content);
        div.append(content);

        $("#" + id).append(div);
    }
}

$(document).ready
(
    function() { book.renderView(); }
);


See what happens? On page load, renderView() is called and currentView is 0. So the left side of the "book" is not rendered, only the right side. Because the first element of views only has the right property. The CSS class is cover and this is the content!




Click on the left and right buttons. Things should change!



 
Here.


And again.







And right at the end...




...you may notice that clicking the buttons beyond this point does nothing, because we added those guard clauses earlier. Same if you're at the front cover and try to go "back". Nope, not happening.

Also, you may have noticed that in between pages, there appears some kind of gap at the left side. That's because the "page" is slightly smaller than the "cover". Here's how we fix that...
#left .page
{

    background: -moz-linear-gradient(left,  rgb(200, 200, 150) 0%, rgb(200, 200, 150) 25%, rgb(150, 150, 50) 70%, rgb(50, 50, 10) 100%);
    background: -webkit-linear-gradient(left,  rgb(200, 200, 150) 0%, rgb(200, 200, 150) 25%, rgb(150, 150, 50) 70%, rgb(50, 50, 10) 100%);
    background: linear-gradient(to right,  rgb(200, 200, 150) 0%, rgb(200, 200, 150) 25%, rgb(150, 150, 50) 70%, rgb(50, 50, 10) 100%);
    float: right;
}


There, no more gap!




What happened to left_middle and right_middle?

You're probably wondering why we bothered with those, right? Never fear. That's in the next part.

Next

Some cool CSS animation magic!

No comments:

Post a Comment