Starling实现的硬皮翻书效果
作者:郭少瑞
———————————————————————————————————————–
在今年的最后一天,这个效果终于更新为软纸翻页的版本(封面和封底仍然是硬纸)
硬纸到软纸的最大困难,在于拖拽中的几何算法和纹理拼接(因为Starling还未提供遮罩,所以不能按照遮罩的思路做,只能根据顶点拼接纹理)。几何算法是从天地会的这篇帖子中获取的,感谢这位作者。拼接则相对简单,显示中如果遇到五边形,则用一个三角形和一个四边形拼接。通过合理控制每个多边形的顶点坐标和纹理UV坐标,可以模拟实现原来遮罩才能做的效果。
———————————————————————————————————————–
Flash的翻书效果想必大家都看到过很多,不过基于Stage3D的版本似乎还很难找到(也可能是我孤陋寡闻了,如果您知道的话欢迎补充)。现在很多项目已经开始使用Stage3D(或基于Stage3D的衍生框架比如Starling)来制作了,有时候也需要将原有传统Flash的效果移植到Stage3D层面来实现,这样融合更方便,性能上也可能更强大一些。
下面是一个基于Starling的翻书效果实现,当然还很简陋,只实现了硬皮翻页,软纸翻页还没有实现。不过我觉得在这个例子上进行一下扩展,用图片纹理坐标和顶点控制的方式,是可以实现出原来遮罩才能实现的效果的(传统Flash翻书效果大都使用遮罩),所以这个例子权作抛砖引玉,期待能有更为完善的例子出来。
效果演示
您可以点击下面的地址,查看这个Demo的实现效果:
http://www.todoair.com/demo/book/StarlingBook.html
Sorry,因为没写Loading,文件尺寸较大,需要您耐心等待一会儿。
实现过程
为了尽可能利用Starling的批处理优势来提升性能,所以素材采用了TextureAtlas的方式,将所有页的图片集中到一张大图上(当然这个地方也有缺点,如果一张图放不下的话,就要分几张图存放,并修改获取素材的方式,以便从不同的源中获得纹理)。在渲染处理上,使用QuadBatch来代替普通的显示对象叠加,来尽量提升性能。在笔者的电脑上,这个例子可以稳定在60FPS运行。
核心是PageFlipContainer这个类,这个类使用一个QuadBatch实例来更新显示,并侦听Touch事件来启动翻页过程。具体代码如下:
package test.pf
{
import flash.display.Bitmap;
import flash.geom.Point;
import starling.display.Image;
import starling.display.QuadBatch;
import starling.display.Sprite;
import starling.events.Event;
import starling.events.Touch;
import starling.events.TouchEvent;
import starling.events.TouchPhase;
import starling.textures.Texture;
import starling.textures.TextureAtlas;
import test.TrangleImage;
/**
* 基于Starling的翻页组件
* @author shaorui
*/
public class PageFlipContainer extends Sprite
{
/**包含内页的图集*/
private var altas:TextureAtlas;
/**书的宽度*/
private var bookWidth: Number ;
/**书的高度*/
private var bookHeight: Number ;
/**书的总页数*/
private var bookCount: Number ;
/**批处理显示*/
private var quadBatch:QuadBatch;
/**左侧显示页面页码*/
private var leftPageNum: int = - 1 ;
/**右侧显示页面页码*/
private var rightPageNum: int = 0 ;
/**翻动中的页面编码(正面,反面为+1)*/
private var flipingPageNum: int = - 1 ;
/**正在翻页的位置(-1到1),由程序控制,外部无须调用*/
public var flipingPageLocation: Number = - 1 ;
/**是否需要更新*/
private var needUpdate: Boolean = true ;
/**@private*/
public function PageFlipContainer(altas:TextureAtlas,bookWidth: Number ,bookHeight: Number ,bookCount: Number )
{
super ();
this .altas = altas;
this .bookWidth = bookWidth;
this .bookHeight = bookHeight;
this .bookCount = bookCount;
initPage();
}
/**初始化页*/
private function initPage(): void
{
quadBatch = new QuadBatch();
addChild(quadBatch);
textures = altas.getTextures();
cacheImage = new Image(textures[ 0 ]);
flipImage = new ImagePage(textures[ 0 ]);
addEventListener(Event.ENTER_FRAME,enterFrameHandler);
addEventListener(Event.ADDED_TO_STAGE,firstFrameInit);
addEventListener(TouchEvent.TOUCH,onTouchHandler);
}
/**显示的时候初始化第一个画面*/
private function firstFrameInit(): void
{
removeEventListener(Event.ADDED_TO_STAGE,firstFrameInit);
enterFrameHandler();
needUpdate = false ;
}
/**用于缓存纹理的图片*/
private var cacheImage:Image;
/**翻动的图片*/
private var flipImage:ImagePage;
/**缓存的纹理数组*/
private var textures:Vector.;
/**每帧调用*/
private function enterFrameHandler(event:Event= null ): void
{
if (stage == null || !needUpdate)
return ;
quadBatch.reset();
if (flipingPageNum >= 0 )
{
leftPageNum = flipingPageNum - 1 ;
rightPageNum = flipingPageNum + 2 ;
}
//选择左侧的页面
if (validatePageNumber(leftPageNum))
{
cacheImage.x = 0 ;
cacheImage.texture = textures[leftPageNum];
quadBatch.addImage(cacheImage);
}
//渲染右侧的页面
if (validatePageNumber(rightPageNum))
{
cacheImage.x = bookWidth/ 2 ;
cacheImage.texture = textures[rightPageNum];
quadBatch.addImage(cacheImage);
}
//渲染正在翻转的页面
if (validatePageNumber(flipingPageNum))
{
if (flipingPageLocation>= 0 )
flipImage = new ImagePage(textures[flipingPageNum]);
else
flipImage = new ImagePage(textures[flipingPageNum+ 1 ]);
flipImage.setLocation(flipingPageLocation);
quadBatch.addImage(flipImage);
flipImage.dispose();
}
}
/**是否处于拖动状态*/
private var isDraging: Boolean = false ;
/**触碰处理*/
private function onTouchHandler(event:TouchEvent): void
{
var touch:Touch = event.getTouch( this );
if (touch != null && (touch.phase == TouchPhase.BEGAN || touch.phase == TouchPhase.MOVED || touch.phase == TouchPhase.ENDED))
{
var point:Point = touch.getLocation( this );
var imgWidth: Number = bookWidth/ 2 ;
if (touch.phase == TouchPhase.BEGAN)
{
isDraging = true ;
if (point.x >= imgWidth)
{
if (validatePageNumber(rightPageNum))
{
flipingPageNum = rightPageNum;
}
}
else
{
if (validatePageNumber(leftPageNum))
{
flipingPageNum = leftPageNum- 1 ;
}
}
}
else if (touch.phase == TouchPhase.MOVED)
{
if (isDraging)
{
flipingPageLocation = (point.x-imgWidth)/imgWidth;
if (flipingPageLocation > 1 )
flipingPageLocation = 1 ;
if (flipingPageLocation < - 1 ) flipingPageLocation = - 1 ; validateNow(); } } else { isDraging = false ; finishTouchByMotion(point.x); } } else { needUpdate = false ; } } /**触控结束后,完成翻页过程*/ private function finishTouchByMotion(endX: Number ): void { var imgWidth: Number = bookWidth/ 2 ; needUpdate = true ; touchable = false ; addEventListener(Event.ENTER_FRAME,executeMotion); function executeMotion(event:Event): void { if (endX >= imgWidth)
{
flipingPageLocation += 0.04 ;
if (flipingPageLocation >= 1 )
{
flipingPageLocation = 1 ;
removeEventListener(Event.ENTER_FRAME,executeMotion);
tweenCompleteHandler();
}
}
else
{
flipingPageLocation -= 0.04 ;
if (flipingPageLocation = 0 && pageNum < bookCount) return true ; else return false ; } /**当前页码*/ public function get pageNumber(): int { if (leftPageNum >= 0 )
return leftPageNum;
else
return rightPageNum;
}
/**强制更新一次显示*/
public function validateNow(): void
{
needUpdate = true ;
enterFrameHandler();
needUpdate = false ;
}
/**跳页*/
public function gotoPage(pn: int ): void
{
if (pn < 0 ) pn = 0 ; if (pn >= bookCount)
pn = bookCount- 1 ;
if (pn == 0 )
{
leftPageNum = - 1 ;
rightPageNum = 0 ;
}
else if (pn == bookCount- 1 )
{
leftPageNum = pn;
rightPageNum = - 1 ;
}
else
{
if (pn% 2 == 0 )
pn = pn - 1 ;
leftPageNum = pn;
rightPageNum = pn+ 1 ;
}
flipingPageNum = - 1 ;
validateNow();
}
}
}
使用方式:
/**初始化*/
private function initGame(event:Event): void
{
/*----------------------翻页组件-----------------------*/
//把图片合集到一起,减少DRW值
var bookImgs:Bitmap = new bookImgClass();
var xml:XML = XML( new booXml());
//这个工具可以给图片加上阴影,提升显示效果
ShadowUtil.addShadow(bookImgs,xml);
var texture:Texture = Texture.fromBitmap(bookImgs);
var atlas:TextureAtlas = new TextureAtlas(texture,xml);
//创建一个翻页容器,设置纹理,书的尺寸和总页数
pageFlipContainer = new PageFlipContainer(atlas, 800 , 480 , 8 );
pageFlipContainer.x = 100 ;
pageFlipContainer.y = 100 ;
addChild(pageFlipContainer);
//创建一个按钮控制翻页
var btn:Button = new Button(Texture.fromBitmap( new btnImgClass() as Bitmap), "下一页" );
btn.x = 100 ;
btn.y = 600 ;
btn.addEventListener(TouchEvent.TOUCH,btnTouchHandler);
addChild(btn);
}
/**翻页*/
private function btnTouchHandler(event:TouchEvent): void
{
var touch:Touch = event.getTouch(event.target as DisplayObject);
if (touch != null && touch.phase == TouchPhase.ENDED)
{
var pn: int = pageFlipContainer.pageNumber+ 1 ;
if ( pn% 2 == 0 )
pn+= 1 ;
if ( pn >= 8 )
pn = 0 ;
pageFlipContainer.gotoPage(pn);
}
}
标签:function,flipingPageLocation,硬皮,private,Starling,翻书,var,import,pn From: https://blog.51cto.com/kenkao/6000192