PixiJS + TweenMax 制作时间轴H5(一镜到底)

参考效果:

一部40年的生活演变史
网易,逃不掉的四字魔咒(手机预览)

参考资料:

PixiJS官方手册
PixiJS教程通俗易懂(日语)
PixiJS教程通俗易懂(翻译)
实战教程(带demo)
TweenMax官方手册
(使用TwwenJS代替TweenMax也行)

PixiJS & TweenMax 的CDN

<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.1/pixi.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.1/TweenMax.min.js"> </script>
  • 基本的实现步骤
//1 、 以可视区域大小创建渲染器
var renderer = PIXI.autoDetectRenderer(window.innerWidth,window.innerHeight);
//2 、 将舞台添加到,页面中
document.body.appendChild(renderer.view);
//3 、 创建元素容器,类似 ‘组’概念,Container可以嵌套Container
var stageBox = new PIXI.Container();
/*
4 、
使用加载器预加载图片。& 监听加载成功事件 ‘progress’ 多次触发 ,target.progress 返回加载成功百分比
加载完成 触发load 里面的 setup函数 。 
*/
PIXI.loader.add([
   './img/test1.png'
]).on("progress", function(target, resource){
   console.log(~~target.progress);
}).load(setup);
function setup(){
  var test1= new PIXI.Sprite(//普通精灵
     PIXI.loader.resources["./img/test1.png"].texture
  );
  animate();
}
//以每秒60fps渲染
function animate(){
   renderer.render(stageBox);
   requestAnimationFrame(animate);
}

模仿【一部40年的生活演变史】

预览地址

  • 其中js部分有很多可以封装的地方,自己没有做封装
  • 只做了部分效果(后半部分大同小异)
  • 设计稿在这里,bg1.psd
  • 对应设计稿还原场景
//根据设计稿 还原场景
  spriteBox.bg1.position.set(0,0);
  spriteBox.bg2.position.set(0,3000);
  spriteBox.bg3.position.set(0,3000*2);
  spriteBox.bg4.position.set(0,3000*3);
  spriteBox.bg5.position.set(0,3000*4);
  spriteBox.bd.position.set(270,967);
  ...
  • 设置场景中需要动画的元素
//设置动画
  tm.to(stageBox,10,{y:-(stageBox.height-window.innerHeight),ease:Bounce.Linear},0);
  tm.to(spriteBox.bd,0.3,{y:spriteBox.bd.y+50,ease:Bounce.Linear},0.05);
  tm.to(spriteBox.text1.scale,0.15,{x:1,y:1,ease:Bounce.Linear},0.00);
  ...
  • 里面有很多地方用到了帧动画,使用pixijs 的 AnimatedSprite(动画精灵)实现
  var cj_arr = [];
  for(let i=1;i<5;i++){
    cj_arr.push('./images/cj'+i+'.png')
  }
  spriteBox.cj = new PIXI.extras.AnimatedSprite.fromImages(cj_arr);
  spriteBox.cj.animationSpeed = -0.08;
  spriteBox.cj.play();
  • 其中用到了俩个时间轴,因为整体动画中存在循环播放的动画(按钮放大缩小效果)。一个时间轴控制播放到不同时间播放不同的动画,另一个时间轴控制循环动画(按钮缩放动画)


    image.png
  var tm = new TimelineLite({paused:true});
  var tm2 = new TimelineLite();
  • range() 函数判断 在不同时间 播放不同的背景声音以及帧动画的跳转暂停。
  document.querySelector('#music0').play();
  spriteBox.bd.gotoAndStop(timeline/60);
  • 场景滑动缓动事件,以及缓动效果。
        document.addEventListener('touchstart',function(e){
            clearInterval(setTime);
            touchstart = true;
            touchstartY = e.changedTouches[0].clientY;
        });
       document.addEventListener('touchmove',function(e){
            if(!touchstart){return;}

            var touchmoveY = e.changedTouches[0].clientY;
            steptime = -(touchmoveY-touchstartY)/1000;
            var time = timeline+=steptime;

            if(time < 0) timeline = 0;
            if(time >= 10) timeline = 10;//这里的10是外层总容器(stageBox)的动画时长
           
            range();
            tm.seek(timeline);//跳转到对应时间
            renderer.render(stageBox);//渲染场景
            touchstartY = touchmoveY;
        })
        document.addEventListener('touchend',function(){
            touchstart = false;
            setTime = setInterval(() => {

                steptime*=0.95;
                
                if(Math.abs(steptime)<1/1000){
                    clearInterval(setTime)
                }
               
                var time = timeline+=steptime;

                if(time < 0) timeline = 0;
                if(time >= 10) timeline = 10;
                
                range();
                tm.seek(timeline);
                renderer.render(stageBox);
            },10);
        })
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.1/pixi.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.0.1/TweenMax.min.js"> </script>

    <style>
        *{
            padding: 0;
            margin: 0;
        }
        html,body{width: 100%;height: 100%;overflow: hidden;}
    </style>
</head>
<body>
    <audio hidden="" id="music0" src="media/0.mp3" loop></audio>
    <audio hidden="" id="music1" src="media/wd.mp3" loop></audio>
    <audio hidden="" id="music2" src="media/1.mp3" loop></audio>
    <script>
        var type = "WebGL";
        if(!PIXI.utils.isWebGLSupported()){
            type = "canvas"
        }
        PIXI.utils.sayHello(type);

        var renderer = PIXI.autoDetectRenderer(window.innerWidth,window.innerHeight);
            document.body.appendChild(renderer.view);
        var draftWidth = 640,draftHeight = 14949;
        var stageBox = new PIXI.Container();
        var tm = new TimelineLite({paused:true});
        var tm2 = new TimelineLite();
        var spriteBox = {};
        PIXI.loader.add([
            './images/bg1.jpg',
            './images/bg2.jpg',
            './images/bg3.jpg',
            './images/bg4.jpg',
            './images/bg5.jpg',
            './images/bd1.png',
            './images/bd2.png',
            './images/cj1.png',
            './images/cj2.png',
            './images/cj3.png',
            './images/cj4.png',
            './images/lb1.png',
            './images/lb2.png',
            './images/lb3.png',
            './images/lb4.png',
            './images/text1.png',
            './images/text4.png',
            './images/tz.png',
            './images/rgm1.png',
            './images/rgm2.png',
            './images/rgm3.png',
            './images/rgm4.png',
            './images/rgm5.png',
            './images/pz.png',
            './images/btn.png',
            './images/tw1.png',
            './images/tw2.png',
            './images/tw3.png',
            './images/tw4.png',
            './images/tw5.png',
            ]).on("progress", function(target, resource){
                console.log(~~target.progress);
            }).load(setup);
        function setup(){ 
            spriteBox.bg1 = new PIXI.Sprite(PIXI.loader.resources['./images/bg1.jpg'].texture);
            spriteBox.bg2 = new PIXI.Sprite(PIXI.loader.resources['./images/bg2.jpg'].texture);
            spriteBox.bg3 = new PIXI.Sprite(PIXI.loader.resources['./images/bg3.jpg'].texture);
            spriteBox.bg4 = new PIXI.Sprite(PIXI.loader.resources['./images/bg4.jpg'].texture);
            spriteBox.bg5 = new PIXI.Sprite(PIXI.loader.resources['./images/bg5.jpg'].texture);
            var bd_arr = [];
            for(let i=1;i<3;i++){
                bd_arr.push('./images/bd'+i+'.png')
            }
            spriteBox.bd = new PIXI.extras.AnimatedSprite.fromImages(bd_arr);
            spriteBox.bd.animationSpeed = -0.05;//序列帧放映速度
            spriteBox.text1 = new PIXI.Sprite(PIXI.loader.resources['./images/text1.png'].texture);
            var cj_arr = [];
            for(let i=1;i<5;i++){
                cj_arr.push('./images/cj'+i+'.png')
            }
            spriteBox.cj = new PIXI.extras.AnimatedSprite.fromImages(cj_arr);
            spriteBox.cj.animationSpeed = -0.08;
            spriteBox.cj.play();
            var lb_arr = [];
            for(let i=1;i<5;i++){
                lb_arr.push('./images/lb'+i+'.png')
            }
            spriteBox.lb = new PIXI.extras.AnimatedSprite.fromImages(lb_arr);
            spriteBox.lb.animationSpeed = -0.08;
            spriteBox.lb.play();
            spriteBox.text4 = new PIXI.Sprite(PIXI.loader.resources['./images/text4.png'].texture);
            var rgm_arr = [];
            for(let i=1;i<=5;i++){
                rgm_arr.push('./images/rgm'+i+'.png')
            }
            spriteBox.rgm = new PIXI.extras.AnimatedSprite.fromImages(rgm_arr);
            spriteBox.rgm.animationSpeed = -0.065;
            spriteBox.rgm.play();
            spriteBox.tz = new PIXI.Sprite(PIXI.loader.resources['./images/tz.png'].texture);
            spriteBox.pz = new PIXI.Sprite(PIXI.loader.resources['./images/pz.png'].texture);
            var tw_arr = [];
            for(let i=1;i<=5;i++){
                tw_arr.push('./images/tw'+i+'.png')
            }
            spriteBox.tw = new PIXI.extras.AnimatedSprite.fromImages(tw_arr);
            spriteBox.tw.animationSpeed = -0.05;
            spriteBox.tw.play();
            spriteBox.btn1 = new PIXI.Sprite(PIXI.loader.resources['./images/btn.png'].texture);
            spriteBox.btn1.on('tap',function(){//绑定事件
                console.log('点击');
            })
            //根据设计稿 还原场景
            spriteBox.bg1.position.set(0,0);
            spriteBox.bg2.position.set(0,3000);
            spriteBox.bg3.position.set(0,3000*2);
            spriteBox.bg4.position.set(0,3000*3);
            spriteBox.bg5.position.set(0,3000*4);
            spriteBox.bd.position.set(270,967);
            spriteBox.text1.position.set(332,993);
            spriteBox.text1.anchor.x = 0;
            spriteBox.text1.anchor.y = 1;
            spriteBox.text1.scale.set(0);
            spriteBox.lb.position.set(462,1522);
            spriteBox.cj.position.set(522,1530);
            spriteBox.text4.position.set(489,1524);
            spriteBox.text4.anchor.x = 1;
            spriteBox.text4.anchor.y = 1;
            spriteBox.text4.scale.set(0);
            spriteBox.rgm.position.set(438,2242);
            spriteBox.tz.position.set(419,2253);
            spriteBox.pz.position.set(0,2634);
            spriteBox.tw.position.set(430,2808);
            spriteBox.btn1.position.set(580,2766);
            spriteBox.btn1.scale.set(1);
            spriteBox.btn1.anchor.set(0.5,0.5);
            spriteBox.btn1.buttonMode = true;//按钮模式 
            spriteBox.btn1.interactive = true;//启用事件
            for(let key in spriteBox){
                stageBox.addChild(spriteBox[key]);//精灵 添加进 容器
            }

            stageBox.scale.set(window.innerWidth/draftWidth);//容器根据设计稿 计算缩放
            
            //设置动画
            tm.to(stageBox,10,{y:-(stageBox.height-window.innerHeight),ease:Bounce.Linear},0);
            tm.to(spriteBox.bd,0.3,{y:spriteBox.bd.y+50,ease:Bounce.Linear},0.05);
            tm.to(spriteBox.text1.scale,0.15,{x:1,y:1,ease:Bounce.Linear},0.00);
            tm.to(spriteBox.text4.scale,0.15,{x:1,y:1,ease:Bounce.Linear},0.1);
            tm2.to(spriteBox.btn1.scale,1,{x:1.3,y:1.3,ease:Bounce.Linear,yoyo:true,repeat:-1},0);
            tm2.play();
            animate();
        }
        var touchstart = false,
            steptime=0,
            touchstartY = 0;
            timeline = 0
            setTime=null;
        document.addEventListener('touchstart',function(e){
            clearInterval(setTime);
            touchstart = true;
            touchstartY = e.changedTouches[0].clientY;
        });
        document.addEventListener('touchmove',function(e){
            if(!touchstart){return;}

            var touchmoveY = e.changedTouches[0].clientY;
            steptime = -(touchmoveY-touchstartY)/1000;
            var time = timeline+=steptime;

            if(time < 0) timeline = 0;
            if(time >= 10) timeline = 10;//这里的10是外层总容器(stageBox)的动画时长
           
            range();
            tm.seek(timeline);//跳转到对应时间
            renderer.render(stageBox);//渲染场景
            touchstartY = touchmoveY;
        })
        document.addEventListener('touchend',function(){
            touchstart = false;
            setTime = setInterval(() => {

                steptime*=0.95;
                
                if(Math.abs(steptime)<1/1000){
                    clearInterval(setTime)
                }
               
                var time = timeline+=steptime;

                if(time < 0) timeline = 0;
                if(time >= 10) timeline = 10;
                
                range();
                tm.seek(timeline);
                renderer.render(stageBox);
            },10);
        })

       function range(){
            if(timeline >0.001 && timeline <= 0.313){
                document.querySelector('#music0').play();
                spriteBox.bd.gotoAndStop(timeline*14);
            }else{
                document.querySelector('#music0').pause();
                document.querySelector('#music0').currentTime=0;
            }
            if(timeline>0.413 && timeline<=0.670){
                document.querySelector('#music1').play();
            }else{
                document.querySelector('#music1').pause();
                document.querySelector('#music1').currentTime=0;
            }
            if(timeline>0.670 && timeline<=1.301){
                
                document.querySelector('#music2').play();
            }else{
                document.querySelector('#music2').pause();
                document.querySelector('#music2').currentTime=0;
            }
        }
        function animate(){
            renderer.render(stageBox);
            requestAnimationFrame(animate);
        }
    </script>
</body>
</html>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容