define("dojox/mobile/pageTurningUtils", [ "dojo/_base/kernel", "dojo/_base/array", "dojo/_base/connect", "dojo/_base/event", "dojo/dom-class", "dojo/dom-construct", "dojo/dom-style" ], function(kernel, array, connect, event, domClass, domConstruct, domStyle){ // module: // dojox/mobile/pageTurningUtils kernel.experimental("dojox.mobile.pageTurningUtils"); return function(){ // summary: // Utilities to provide page turning effects just like turning a real book. // example: // | require([ // | "dojo/ready", // | "dojox/mobile/pageTurningUtils" // | ], function(ready, pageTurningUtils){ // | var utils = new pageTurningUtils(); // | ready(function(){ // | utils.init(300, 400); // Specify width and height by pixels // | utils.initCatalog(document.getElementById("catalog")); // | }); // | ); // |
// |
// |
// |
// |
// |
// |
// |
// |
// |
// |
// |
// |
// |
this.w = 0; this.h = 0; this.turnfrom = "top"; this.page = 1; this.dogear = 1.0; this.duration = 2; this.alwaysDogeared = false; /* Internal properties */ this._styleParams = {}; this._catalogNode = null; this._currentPageNode = null; this._transitionEndHandle = null; this.init = function(/*int*/w, /*int*/h, /*String?*/turnfrom, /*int?*/page, /*Number?*/dogear, /*Number?*/duration, /*Boolean?*/alwaysDogeared){ // summary: // Sets property values necessary for calculating the style parameters // for page positioning and page turning effects. // w: int // The width of each page by pixels. You cannot specify it by percentage. // h: int // The height of each page by pixels. You cannot specify it by percentage. // turnfrom: String? // Specifies from which side/corner the page turning starts. // You can choose from "top", "bottom" or "left". Defaults to "top". // If "top", each page is turned from top-right corner of the page. // If "bottom", each page is turned from bottom-right corner of the page. // And if "left", each page is turned from top-left corner of the page. // The page is shown as dog-eared except the case of "bottom". // page: int? // The number of pages shown in the screen at a time. // This parameter should be either of 1 or 2. Defaults to 1. // If 1, the only one side of two facing pages are shown. // If 2, the two facing pages are shown at a time. // dogear: Number? // The ratio of actual dog-ear width to the maximum dog-ear width which is // 11 percent of the page width (= 0.11 * w). // This parameter should be a float number between 0 and 1. Defaults to 1. // The actual dog-ear width is calculated by the following formula: // | 0.11 * w * dogear. // This parameter is ignored if "bottom" is specified to turnfrom parameter. // duration: Number? // The duration of page turning animations by seconds. (ex. 1.5, 3, etc) // Defaults to 2. // alwaysDogeared: Boolean? // Specifies whether all pages are always dog-eared or not. // If true, all pages are always dog-eared. // If false, only the current page is dog-eared while the others are not. // This parameter is ignored if "bottom" is specified to turnfrom parameter. // Set property values this.w = w; this.h = h; this.turnfrom = turnfrom ? turnfrom : this.turnfrom; this.page = page ? page : this.page; this.dogear = typeof dogear !== 'undefined' ? dogear : this.dogear; this.duration = typeof duration !== 'undefined' ? duration : this.duration; this.alwaysDogeared = typeof alwaysDogeared !== 'undefined' ? alwaysDogeared : this.alwaysDogeared if(this.turnfrom === "bottom"){ // dog-ear is not supported if using "bottom" this.alwaysDogeared = true; } // Calculate style parameters for page positioning and page turning effects this._calcStyleParams(); }; this._calcStyleParams = function(){ // tags: // private var tan58 = Math.tan(58 * Math.PI/180), cos32 = Math.cos(32 * Math.PI/180), sin32 = Math.sin(32 * Math.PI/180), tan32 = Math.tan(32 * Math.PI/180), w = this.w, h = this.h, page = this.page, turnfrom = this.turnfrom, params = this._styleParams; // Calculate each div size and position based on the page turning algorithm // fw: frontWidth, fh: frontHeight, dw: dogear, cx: posX, cy: posY, // dx: dogearX, dy: dogearY, fy:actualPagePos var Q = fold = w * tan58, fw = Q * sin32 + Q * cos32 * tan58, fh = fold + w + w/tan58, dw = w * 0.11 * this.dogear, pw = w - dw, base = pw * cos32, cx, cy, dx, dy, fy; // These params depend on the types of "turnfrom" parameter switch(this.turnfrom){ // TODO Should separate each case as a plugin? case "top": cx = fw - base; cy = base * tan58; dx = fw - dw; dy = cy + pw/tan58 - 7; fy = cy/cos32; params.init = { page: { top: -fy + "px", left: (-fw + (page === 2 ? w : 0)) + "px", width: fw + "px", height: fh + "px", webkitTransformOrigin: "100% 0%" }, front: { width: w + "px", height: h + "px", webkitBoxShadow: "0 0" }, back: { width: w + "px", height: h + "px", webkitBoxShadow: "0 0" }, shadow: { display: "", left: fw + "px", height: h * 1.5 + "px" } }; params.turnForward = { page: { webkitTransform: "rotate(0deg)" }, front: { webkitTransform: "translate("+ fw + "px," + fy +"px) rotate(0deg)", webkitTransformOrigin: "-110px -18px" }, back: { webkitTransform: "translate("+ (fw - w) + "px," + fy +"px) rotate(0deg)", webkitTransformOrigin: "0px 0px" } }; params.turnBackward = { page: { webkitTransform: "rotate(-32deg)" }, front: { webkitTransform: "translate("+ cx + "px," + cy +"px) rotate(32deg)", webkitTransformOrigin: "0px 0px" }, back: { webkitTransform: "translate("+ dx + "px," + dy +"px) rotate(-32deg)", webkitTransformOrigin: "0px 0px" } }; break; case "bottom": cx = fw - (h * sin32 + w * cos32) - 2; cy = fh - (h + w/tan32) * cos32; dx = fw; dy = fh - w/sin32 - h; fy = fh - w/tan32 - h; params.init = { page: { top: (-fy + 50) + "px", left: (-fw + (page === 2 ? w : 0)) + "px", width: fw + "px", height: fh + "px", webkitTransformOrigin: "100% 100%" }, front: { width: w + "px", height: h + "px", webkitBoxShadow: "0 0" }, back: { width: w + "px", height: h + "px", webkitBoxShadow: "0 0" }, shadow: { display: "none" } }; params.turnForward = { page: { webkitTransform: "rotate(0deg)" }, front: { webkitTransform: "translate("+ fw + "px," + fy +"px) rotate(0deg)", webkitTransformOrigin: "-220px 35px" }, back: { webkitTransform: "translate("+ (w * 2) + "px," + fy +"px) rotate(0deg)", webkitTransformOrigin: "0px 0px" } }; params.turnBackward = { page: { webkitTransform: "rotate(32deg)" }, front: { webkitTransform: "translate("+ cx + "px," + cy +"px) rotate(-32deg)", webkitTransformOrigin: "0px 0px" }, back: { webkitTransform: "translate("+ dx + "px," + dy +"px) rotate(0deg)", webkitTransformOrigin: "0px 0px" } }; break; case "left": cx = -w; cy = pw/tan32 - 2; dx = -pw; dy = fy = pw/sin32 + dw * sin32; params.init = { page: { top: -cy + "px", left: w + "px", width: fw + "px", height: fh + "px", webkitTransformOrigin: "0% 0%" }, front: { width: w + "px", height: h + "px", webkitBoxShadow: "0 0" }, back: { width: w + "px", height: h + "px", webkitBoxShadow: "0 0" }, shadow: { display: "", left: "-4px", height: ((page === 2 ? h * 1.5 : h) + 50) + "px" } }; params.turnForward = { page: { webkitTransform: "rotate(0deg)" }, front: { webkitTransform: "translate("+ cx + "px," + cy +"px) rotate(0deg)", webkitTransformOrigin: "160px 68px" }, back: { webkitTransform: "translate(0px," + cy +"px) rotate(0deg)", webkitTransformOrigin: "0px 0px" } }; params.turnBackward = { page: { webkitTransform: "rotate(32deg)" }, front: { webkitTransform: "translate("+ (-dw) + "px," + dy +"px) rotate(-32deg)", webkitTransformOrigin: "0px 0px" }, back: { webkitTransform: "translate("+ dx + "px," + dy +"px) rotate(32deg)", webkitTransformOrigin: "top right" } }; break; } params.init.catalog = { // catalogNode width: (page === 2 ? w * 2 : w) + "px", height: ((page === 2 ? h * 1.5 : h) + (turnfrom == "top" ? 0 : 50)) + "px" }; }; this.getChildren = function(/*DomNode*/node){ return array.filter(node.childNodes, function(n){ return n.nodeType === 1; }); }; this.getPages = function(){ // summary: // Get the array of all page nodes. return this._catalogNode ? this.getChildren(this._catalogNode) : null; }; this.getCurrentPage = function(){ // summary: // Get the current page node. return this._currentPageNode; }; this.getIndexOfPage = function(/*DomNode*/pageNode, /*DomNode[]?*/pages){ if(!pages){ pages = this.getPages(); } for(var i=0; i=0; i--){ var pageNode = pages[i]; this.showDogear(pageNode); if(this.isPageTurned(pageNode)){ pageNode.style.zIndex = base + len + 1; }else{ pageNode.style.zIndex = base + len - i; !this.alwaysDogeared && this.hideDogear(pageNode); this._currentPageNode = pageNode; } } if(!this.alwaysDogeared && this._currentPageNode != pages[len - 1]){ this.showDogear(this._currentPageNode); } }; this.initPage = function(/*DomNode*/pageNode, /*int?*/dir){ // summary: // Initializes the specified page. // description: // Apply style class and other various styles to the specified page node // for initialization. // This function must be called when you add a new page after initialization. // pageNode: DOMNode // The page node to be initialized. // dir: int? // Specified whether the page is turned or not at initialization. // This parameter should be either of 1 or -1. // If 1, the page is turned at initialization. If -1, it is not turned. // Create shadow node if not exist var childNodes = this.getChildren(pageNode); while(childNodes.length < 3){ pageNode.appendChild(domConstruct.create("div", null)); childNodes = this.getChildren(pageNode); } // Check if it is the first time to initialize this page node or not var isFirst = !domClass.contains(pageNode, "mblPageTurningPage"); // Apply style class domClass.add(pageNode, "mblPageTurningPage"); domClass.add(childNodes[0], "mblPageTurningFront"); // frontNode domClass.add(childNodes[1], "mblPageTurningBack"); // backNode domClass.add(childNodes[2], "mblPageTurningShadow"); // shadowNode // Apply styles var p = this._styleParams.init; domStyle.set(pageNode, p.page); domStyle.set(childNodes[0], p.front); // frontNode domStyle.set(childNodes[1], p.back); // backNode p.shadow && domStyle.set(childNodes[2], p.shadow); // shadowNode if(!dir){ // Determine whether to turn this page or not if(isFirst && this._currentPageNode){ var pages = this.getPages(); dir = this.getIndexOfPage(pageNode) < this.getIndexOfPage(this._currentPageNode) ? 1 : -1; }else{ dir = this.isPageTurned(pageNode) ? 1 : -1; } } this._turnPage(pageNode, dir, 0); }; this.turnToNext = function(/*Number?*/duration){ // summary: // Turns a page forward. // description: // The current page is turned forward if it is not the last page of the book. // duration: Number? // The duration of page turning animations by seconds. (ex. 1.5, 3, etc) // If this parameter is omitted, this.duration property value is used. var nextPage = this.getNextPage(this._currentPageNode); if(nextPage){ this._turnPage(this._currentPageNode, 1, duration); this._currentPageNode = nextPage; } }; this.turnToPrev = function(/*Number?*/duration){ // summary: // Turns a page backward. // description: // The current page is turned backward if it is not the first page of the book. // duration: Number? // The duration of page turning animations by seconds. (ex. 1.5, 3, etc) // If this parameter is omitted, this.duration property value is used. var prevPage = this.getPreviousPage(this._currentPageNode); if(prevPage){ this._turnPage(prevPage, -1, duration); this._currentPageNode = prevPage; } }; this.goTo = function(/*int*/index){ // summary: // Jumps to the specified page without page turning animations. // index: int // The index of the page you want to jump to. var pages = this.getPages(); if(this._currentPageNode === pages[index] || pages.length <= index) { return; } var goBackward = index < this.getIndexOfPage(this._currentPageNode, pages); while(this._currentPageNode !== pages[index]) { goBackward ? this.turnToPrev(0) : this.turnToNext(0); } }; this._turnPage = function(/*DomNode*/pageNode, /*int*/dir, /*Number?*/duration){ // tags: // private var childNodes = this.getChildren(pageNode), d = ((typeof duration !== 'undefined') ? duration : this.duration) + "s", p = (dir === 1) ? this._styleParams.turnForward : this._styleParams.turnBackward; // Apply styles for page turning animations p.page.webkitTransitionDuration = d; domStyle.set(pageNode, p.page); p.front.webkitTransitionDuration = d; domStyle.set(childNodes[0], p.front); // frontNode p.back.webkitTransitionDuration = d; domStyle.set(childNodes[1], p.back); // backNode // Adjust z-index and dog-ear var pages = this.getPages(), nextPage = this.getNextPage(pageNode), len = pages.length, base = this._getBaseZIndex(); if(dir === 1){ pageNode.style.zIndex = base + len + 1; if(!this.alwaysDogeared && nextPage && this.getNextPage(nextPage)){ this.showDogear(nextPage); } }else{ if(nextPage){ nextPage.style.zIndex = base + len - this.getIndexOfPage(nextPage, pages); !this.alwaysDogeared && this.hideDogear(nextPage); } } }; this.showDogear = function(/*DomNode*/pageNode){ // summary: // Shows the dog-ear. var childNodes = this.getChildren(pageNode);; domStyle.set(pageNode, "overflow", ""); childNodes[1] && domStyle.set(childNodes[1], "display", ""); // backNode childNodes[2] && domStyle.set(childNodes[2], "display", this.turnfrom === "bottom" ? "none" : ""); // shadowNode }; this.hideDogear = function(/*DomNode*/pageNode){ // summary: // Hides the dog-ear. if(this.turnfrom === "bottom"){ return; } var childNodes = this.getChildren(pageNode); domStyle.set(pageNode, "overflow", "visible"); childNodes[1] && domStyle.set(childNodes[1], "display", "none"); // backNode childNodes[2] && domStyle.set(childNodes[2], "display", "none"); // shadowNode }; }; });