5

For an ebook-reader webapp, im trying to make a fancy book-like viewer for the ebooks, like iBooks for iPad and iPhone.

I found that excellent jQuery Plugin which is exactly what im trying to reach. The only problem is, its a jQuery Plugin, so i cant use that. How could something like that'be done in pure Javascript?

Link to the jQuery page flip plugin: Turn JS jQuery Plugin

David Fariña
  • 1,536
  • 1
  • 18
  • 28
  • 2
    Stackoverflow users are more responsive to answer when they can see the effort you tried to put into making something. Have you tried something and got stuck? Do you have code you're stuck on? We generally don't program what other people want with out them trying first. – John Riselvato Apr 09 '13 at 14:27
  • 2
    We also demand $$ if you don't prove to us that you tried. :) – Sanchit Apr 09 '13 at 14:33
  • I don't think you'll find one to be honest. Seems like 90% of plugins are made for jQuery now. Not to plug jQuery, but if you can code javascript, you could learn jQuery really quick. – dmgig Apr 09 '13 at 14:35
  • 3
    Okey, the reason why ive tried nothing until now is, that i really have no clue how to start making the physics of a sheet and so on.. I thought maybe someone knows a good tutorial or a hint where to start... – David Fariña Apr 09 '13 at 14:37
  • @user399696 And i know jQuery, actually i started developing client-side, with jQuery until i realized, that it has more negative points for me than positive ones. One main point is performance. Thats the reason i dont want to use it... – David Fariña Apr 09 '13 at 14:39
  • 1
    jQuery doesnt make your code less performant ,that's bullsh*t. Whenever possible jquery falls back to the native api. Further more even modern browsers have a ton of bugs that make the use of native api a nightmare if your requirement is cross-browser compatibility. Maybe you dont care but library developers do. – mpm Apr 09 '13 at 14:52
  • Maybe you dont know what youre saying or you love jquery so much that YOU dont care. I care a lot of browser compatibility, and i care a lot about performance. And maybe you dont know, but everytime you call $ the whole jquery object gets initialized. Thats because jQuery uses a wrapper around their prototypal Methods, to avoid conflicts, whats making it significantly slower. For a simple selector it first goes to almost 80 lines of code, even for a #id selector, what makes it a hundred times slower. Sure, someone is not just using jquery for an ID selector, but just as example. – David Fariña Apr 09 '13 at 15:08
  • Even if you dont want to use 90% of functionality in jQuery, it gets initialized, if you use em or not... – David Fariña Apr 09 '13 at 15:10
  • @DavidFariña, be careful of focusing on that level of performance too early. Premature optimization. ;-) If you do actually care about the difference in performance of a pure JavaScript based solution and a jQuery based solution, then you shouldn't be performing a page flip animation to begin with. – Travis Watson Apr 09 '13 at 15:43
  • Why not? My whole application will be build in javascript for the frontend. The ebook datas will be loaded with play framework in the backend developed in scala. But since the whole user interface will be built in javascript i have to care about performance that deep. My only question was, where to start. I know javascript very good, and i know jquery very good, only thing i lack is math, when it goes too deep. If nobody wants to give me just a hint where to start, i think i really waste my time here. – David Fariña Apr 09 '13 at 16:05
  • Did you ever build this out? – John Riselvato Oct 31 '13 at 16:40
  • I have tried it with your example, and worked, but unfortunately (or fortunately for me) my company cancelled the project – David Fariña Nov 05 '13 at 11:29

2 Answers2

14

As much as you might think this is better to do with JavaScript alone, you should understand that it's might be very difficult and without the proper math understanding and all it's probably going to take you more time to figure out then the plug in you have shown above. In any case with Google I have found a couple jsfiddles that can possible get you in the right direction.

Pure JavaScript flip effect: http://jsfiddle.net/maitrekaio/nuUNd/

// Dimensions of the whole book
var BOOK_WIDTH = 630;
var BOOK_HEIGHT = 260;

// Dimensions of one page in the book
var PAGE_WIDTH = 300;
var PAGE_HEIGHT = 250;

// Vertical spacing between the top edge of the book and the papers
var PAGE_Y = (BOOK_HEIGHT - PAGE_HEIGHT) / 2;

// The canvas size equals to the book dimensions + this padding
var CANVAS_PADDING = 30;

var progress = 1;
var target = -1;

var canvas = document.getElementById("pageflip-canvas");
var context = canvas.getContext("2d");
var fillElt = document.getElementById("fill");
var droppedShadowElt = document.getElementById("droppedShadow");
var sharpShadowElt = document.getElementById("sharpShadow");
var progressElt = document.getElementById("progress");
var foldWidthElt = document.getElementById("foldWidth");
var foldXElt = document.getElementById("foldX");

// Resize the canvas to match the book size
canvas.width = BOOK_WIDTH;
canvas.height = BOOK_HEIGHT + (CANVAS_PADDING * 2);

progressElt.addEventListener("change", render, false);

function render() {

    // Reset all pixels in the canvas
    context.clearRect(0, 0, canvas.width, canvas.height);

    // Ease progress towards the target value 
    progress = progressElt.value;
    // Strength of the fold is strongest in the middle of the book
    var strength = 1 - Math.abs(progress);

    // Width of the folded paper
    var foldWidth = (PAGE_WIDTH * 0.5) * (1 - progress);

    // X position of the folded paper
    var foldX = PAGE_WIDTH * progress + foldWidth;

    // How far the page should outdent vertically due to perspective
    var verticalOutdent = 20 * strength;

    foldWidthElt.value = foldWidth;
    foldXElt.value = foldX;

    context.save();
    context.translate((BOOK_WIDTH / 2), PAGE_Y + CANVAS_PADDING);

    drawFoldedPaper(foldX, foldWidth, verticalOutdent, fillElt.checked);


    if (sharpShadowElt.checked) {
        drawSharpShadow(foldX, foldWidth, verticalOutdent, strength);
    }

    if (droppedShadowElt.checked) {
        drawDroppedShadow(foldX, foldWidth, strength);
    }

    context.restore();
}


// Draw the folded piece of paper
function drawFoldedPaper(foldX, foldWidth, verticalOutdent, fill) {
    context.beginPath();
    context.moveTo(foldX, 0);
    context.lineTo(foldX, PAGE_HEIGHT);
    context.quadraticCurveTo(foldX, PAGE_HEIGHT + (verticalOutdent * 2), foldX - foldWidth, PAGE_HEIGHT + verticalOutdent);
    context.lineTo(foldX - foldWidth, -verticalOutdent);
    context.quadraticCurveTo(foldX, -verticalOutdent * 2, foldX, 0);

    if (fill) {
        // Gradient applied to the folded paper (highlights & shadows)
        var paperShadowWidth = (PAGE_WIDTH * 0.5) * Math.max(Math.min(1 - progress, 0.5), 0);
        var foldGradient = context.createLinearGradient(foldX - paperShadowWidth, 0, foldX, 0);
        foldGradient.addColorStop(0.35, '#fafafa');
        foldGradient.addColorStop(0.73, '#eeeeee');
        foldGradient.addColorStop(0.9, '#fafafa');
        foldGradient.addColorStop(1.0, '#e2e2e2');
        context.fillStyle = foldGradient;
        context.fill();
    }
    context.strokeStyle = 'rgba(0,0,0,0.06)';
    context.lineWidth = 2;
    context.stroke();
}

// Draw a sharp shadow on the left side of the page
function drawSharpShadow(foldX, foldWidth, verticalOutdent, strength) {
    context.strokeStyle = 'rgba(0,0,0,'+(0.05 * strength)+')';
    context.lineWidth = 30 * strength;
    context.beginPath();
    context.moveTo(foldX - foldWidth, -verticalOutdent * 0.5);
    context.lineTo(foldX - foldWidth, PAGE_HEIGHT + (verticalOutdent * 0.5));
    context.stroke();
}

function drawDroppedShadow(foldX, foldWidth, strength) {    
    // Right side drop shadow
    var rightShadowWidth = (PAGE_WIDTH * 0.5) * Math.max(Math.min(strength, 0.5), 0);
    var rightShadowGradient = context.createLinearGradient(foldX, 0, foldX + rightShadowWidth, 0);
    rightShadowGradient.addColorStop(0, 'rgba(0,0,0,'+(strength*0.2)+')');
    rightShadowGradient.addColorStop(0.8, 'rgba(0,0,0,0.0)');

    context.fillStyle = rightShadowGradient;
    context.beginPath();
    context.moveTo(foldX, 0);
    context.lineTo(foldX + rightShadowWidth, 0);
    context.lineTo(foldX + rightShadowWidth, PAGE_HEIGHT);
    context.lineTo(foldX, PAGE_HEIGHT);
    context.fill();


    // Left side drop shadow
    var leftShadowWidth = (PAGE_WIDTH * 0.5) * Math.max(Math.min(strength, 0.5), 0);
    var leftShadowGradient = context.createLinearGradient(foldX - foldWidth - leftShadowWidth, 0, foldX - foldWidth, 0);
    leftShadowGradient.addColorStop(0, 'rgba(0,0,0,0.0)');
    leftShadowGradient.addColorStop(1, 'rgba(0,0,0,'+(strength*0.15)+')');

    context.fillStyle = leftShadowGradient;
    context.beginPath();
    context.moveTo(foldX - foldWidth - leftShadowWidth, 0);
    context.lineTo(foldX - foldWidth, 0);
    context.lineTo(foldX - foldWidth, PAGE_HEIGHT);
    context.lineTo(foldX - foldWidth - leftShadowWidth, PAGE_HEIGHT);
    context.fill();    
}

jQuery flip effect (that shouldn't be an issue converting to pure JavaScript): http://jsfiddle.net/pixelass/TW43V/61/

Another jQuery flip effect: http://jsfiddle.net/wdm954/DkZsY/2/

because you want pure JavaScript know you'll need to figure out how to do this on your own. Using these examples as guidelines will be helpful even if they are not pure js.

j08691
  • 204,283
  • 31
  • 260
  • 272
John Riselvato
  • 12,854
  • 5
  • 62
  • 89
7

This question is rather old but I'll place a reference where idea for approved answer has been taken from - http://www.html5rocks.com/en/tutorials/casestudies/20things_pageflip/. Good tutorial with explanation and examples.

eduard.dudar
  • 111
  • 2
  • 5