page-flip
Version:
Powerful, simple and flexible JS Library for creating realistic and beautiful page turning effect
383 lines (314 loc) • 12.8 kB
text/typescript
import { Orientation, Render } from './Render';
import { PageFlip } from '../PageFlip';
import { FlipDirection } from '../Flip/Flip';
import { PageDensity, PageOrientation } from '../Page/Page';
import { HTMLPage } from '../Page/HTMLPage';
import { Helper } from '../Helper';
import { FlipSetting } from '../Settings';
/**
* Class responsible for rendering the HTML book
*/
export class HTMLRender extends Render {
/** Parent HTML Element */
private readonly element: HTMLElement;
/** Pages List as HTMLElements */
private readonly items: NodeListOf<HTMLElement> | HTMLElement[];
private outerShadow: HTMLElement = null;
private innerShadow: HTMLElement = null;
private hardShadow: HTMLElement = null;
private hardInnerShadow: HTMLElement = null;
/**
* @constructor
*
* @param {PageFlip} app - PageFlip object
* @param {FlipSetting} setting - Configuration object
* @param {HTMLElement} element - Parent HTML Element
*/
constructor(app: PageFlip, setting: FlipSetting, element: HTMLElement) {
super(app, setting);
this.element = element;
this.createShadows();
}
private createShadows(): void {
this.element.insertAdjacentHTML(
'beforeend',
`<div class="stf__outerShadow"></div>
<div class="stf__innerShadow"></div>
<div class="stf__hardShadow"></div>
<div class="stf__hardInnerShadow"></div>`
);
this.outerShadow = this.element.querySelector('.stf__outerShadow');
this.innerShadow = this.element.querySelector('.stf__innerShadow');
this.hardShadow = this.element.querySelector('.stf__hardShadow');
this.hardInnerShadow = this.element.querySelector('.stf__hardInnerShadow');
}
public clearShadow(): void {
super.clearShadow();
this.outerShadow.style.cssText = 'display: none';
this.innerShadow.style.cssText = 'display: none';
this.hardShadow.style.cssText = 'display: none';
this.hardInnerShadow.style.cssText = 'display: none';
}
public reload(): void {
const testShadow = this.element.querySelector('.stf__outerShadow');
if (!testShadow) {
this.createShadows();
}
}
/**
* Draw inner shadow to the hard page
*/
private drawHardInnerShadow(): void {
const rect = this.getRect();
const progress =
this.shadow.progress > 100 ? 200 - this.shadow.progress : this.shadow.progress;
let innerShadowSize = ((100 - progress) * (2.5 * rect.pageWidth)) / 100 + 20;
if (innerShadowSize > rect.pageWidth) innerShadowSize = rect.pageWidth;
let newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 5).toString(10)};
width: ${innerShadowSize}px;
height: ${rect.height}px;
background: linear-gradient(to right,
rgba(0, 0, 0, ${(this.shadow.opacity * progress) / 100}) 5%,
rgba(0, 0, 0, 0) 100%);
left: ${rect.left + rect.width / 2}px;
transform-origin: 0 0;
`;
newStyle +=
(this.getDirection() === FlipDirection.FORWARD && this.shadow.progress > 100) ||
(this.getDirection() === FlipDirection.BACK && this.shadow.progress <= 100)
? `transform: translate3d(0, 0, 0);`
: `transform: translate3d(0, 0, 0) rotateY(180deg);`;
this.hardInnerShadow.style.cssText = newStyle;
}
/**
* Draw outer shadow to the hard page
*/
private drawHardOuterShadow(): void {
const rect = this.getRect();
const progress =
this.shadow.progress > 100 ? 200 - this.shadow.progress : this.shadow.progress;
let shadowSize = ((100 - progress) * (2.5 * rect.pageWidth)) / 100 + 20;
if (shadowSize > rect.pageWidth) shadowSize = rect.pageWidth;
let newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 4).toString(10)};
width: ${shadowSize}px;
height: ${rect.height}px;
background: linear-gradient(to left, rgba(0, 0, 0, ${
this.shadow.opacity
}) 5%, rgba(0, 0, 0, 0) 100%);
left: ${rect.left + rect.width / 2}px;
transform-origin: 0 0;
`;
newStyle +=
(this.getDirection() === FlipDirection.FORWARD && this.shadow.progress > 100) ||
(this.getDirection() === FlipDirection.BACK && this.shadow.progress <= 100)
? `transform: translate3d(0, 0, 0) rotateY(180deg);`
: `transform: translate3d(0, 0, 0);`;
this.hardShadow.style.cssText = newStyle;
}
/**
* Draw inner shadow to the soft page
*/
private drawInnerShadow(): void {
const rect = this.getRect();
const innerShadowSize = (this.shadow.width * 3) / 4;
const shadowTranslate = this.getDirection() === FlipDirection.FORWARD ? innerShadowSize : 0;
const shadowDirection =
this.getDirection() === FlipDirection.FORWARD ? 'to left' : 'to right';
const shadowPos = this.convertToGlobal(this.shadow.pos);
const angle = this.shadow.angle + (3 * Math.PI) / 2;
const clip = [
this.pageRect.topLeft,
this.pageRect.topRight,
this.pageRect.bottomRight,
this.pageRect.bottomLeft,
];
let polygon = 'polygon( ';
for (const p of clip) {
let g =
this.getDirection() === FlipDirection.BACK
? {
x: -p.x + this.shadow.pos.x,
y: p.y - this.shadow.pos.y,
}
: {
x: p.x - this.shadow.pos.x,
y: p.y - this.shadow.pos.y,
};
g = Helper.GetRotatedPoint(g, { x: shadowTranslate, y: 100 }, angle);
polygon += g.x + 'px ' + g.y + 'px, ';
}
polygon = polygon.slice(0, -2);
polygon += ')';
const newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 10).toString(10)};
width: ${innerShadowSize}px;
height: ${rect.height * 2}px;
background: linear-gradient(${shadowDirection},
rgba(0, 0, 0, ${this.shadow.opacity}) 5%,
rgba(0, 0, 0, 0.05) 15%,
rgba(0, 0, 0, ${this.shadow.opacity}) 35%,
rgba(0, 0, 0, 0) 100%);
transform-origin: ${shadowTranslate}px 100px;
transform: translate3d(${shadowPos.x - shadowTranslate}px, ${
shadowPos.y - 100
}px, 0) rotate(${angle}rad);
clip-path: ${polygon};
-webkit-clip-path: ${polygon};
`;
this.innerShadow.style.cssText = newStyle;
}
/**
* Draw outer shadow to the soft page
*/
private drawOuterShadow(): void {
const rect = this.getRect();
const shadowPos = this.convertToGlobal({ x: this.shadow.pos.x, y: this.shadow.pos.y });
const angle = this.shadow.angle + (3 * Math.PI) / 2;
const shadowTranslate = this.getDirection() === FlipDirection.BACK ? this.shadow.width : 0;
const shadowDirection =
this.getDirection() === FlipDirection.FORWARD ? 'to right' : 'to left';
const clip = [
{ x: 0, y: 0 },
{ x: rect.pageWidth, y: 0 },
{ x: rect.pageWidth, y: rect.height },
{ x: 0, y: rect.height },
];
let polygon = 'polygon( ';
for (const p of clip) {
if (p !== null) {
let g =
this.getDirection() === FlipDirection.BACK
? {
x: -p.x + this.shadow.pos.x,
y: p.y - this.shadow.pos.y,
}
: {
x: p.x - this.shadow.pos.x,
y: p.y - this.shadow.pos.y,
};
g = Helper.GetRotatedPoint(g, { x: shadowTranslate, y: 100 }, angle);
polygon += g.x + 'px ' + g.y + 'px, ';
}
}
polygon = polygon.slice(0, -2);
polygon += ')';
const newStyle = `
display: block;
z-index: ${(this.getSettings().startZIndex + 10).toString(10)};
width: ${this.shadow.width}px;
height: ${rect.height * 2}px;
background: linear-gradient(${shadowDirection}, rgba(0, 0, 0, ${
this.shadow.opacity
}), rgba(0, 0, 0, 0));
transform-origin: ${shadowTranslate}px 100px;
transform: translate3d(${shadowPos.x - shadowTranslate}px, ${
shadowPos.y - 100
}px, 0) rotate(${angle}rad);
clip-path: ${polygon};
-webkit-clip-path: ${polygon};
`;
this.outerShadow.style.cssText = newStyle;
}
/**
* Draw left static page
*/
private drawLeftPage(): void {
if (this.orientation === Orientation.PORTRAIT || this.leftPage === null) return;
if (
this.direction === FlipDirection.BACK &&
this.flippingPage !== null &&
this.flippingPage.getDrawingDensity() === PageDensity.HARD
) {
(this.leftPage as HTMLPage).getElement().style.zIndex = (
this.getSettings().startZIndex + 5
).toString(10);
this.leftPage.setHardDrawingAngle(180 + this.flippingPage.getHardAngle());
this.leftPage.draw(this.flippingPage.getDrawingDensity());
} else {
this.leftPage.simpleDraw(PageOrientation.LEFT);
}
}
/**
* Draw right static page
*/
private drawRightPage(): void {
if (this.rightPage === null) return;
if (
this.direction === FlipDirection.FORWARD &&
this.flippingPage !== null &&
this.flippingPage.getDrawingDensity() === PageDensity.HARD
) {
(this.rightPage as HTMLPage).getElement().style.zIndex = (
this.getSettings().startZIndex + 5
).toString(10);
this.rightPage.setHardDrawingAngle(180 + this.flippingPage.getHardAngle());
this.rightPage.draw(this.flippingPage.getDrawingDensity());
} else {
this.rightPage.simpleDraw(PageOrientation.RIGHT);
}
}
/**
* Draw the next page at the time of flipping
*/
private drawBottomPage(): void {
if (this.bottomPage === null) return;
const tempDensity =
this.flippingPage != null ? this.flippingPage.getDrawingDensity() : null;
if (!(this.orientation === Orientation.PORTRAIT && this.direction === FlipDirection.BACK)) {
(this.bottomPage as HTMLPage).getElement().style.zIndex = (
this.getSettings().startZIndex + 3
).toString(10);
this.bottomPage.draw(tempDensity);
}
}
protected drawFrame(): void {
this.clear();
this.drawLeftPage();
this.drawRightPage();
this.drawBottomPage();
if (this.flippingPage != null) {
(this.flippingPage as HTMLPage).getElement().style.zIndex = (
this.getSettings().startZIndex + 5
).toString(10);
this.flippingPage.draw();
}
if (this.shadow != null && this.flippingPage !== null) {
if (this.flippingPage.getDrawingDensity() === PageDensity.SOFT) {
this.drawOuterShadow();
this.drawInnerShadow();
} else {
this.drawHardOuterShadow();
this.drawHardInnerShadow();
}
}
}
private clear(): void {
for (const page of this.app.getPageCollection().getPages()) {
if (
page !== this.leftPage &&
page !== this.rightPage &&
page !== this.flippingPage &&
page !== this.bottomPage
) {
(page as HTMLPage).getElement().style.cssText = 'display: none';
}
if (page.getTemporaryCopy() !== this.flippingPage) {
page.hideTemporaryCopy();
}
}
}
public update(): void {
super.update();
if (this.rightPage !== null) {
this.rightPage.setOrientation(PageOrientation.RIGHT);
}
if (this.leftPage !== null) {
this.leftPage.setOrientation(PageOrientation.LEFT);
}
}
}