PixiJS + TweenMax 制作時(shí)間軸H5(一鏡到底)

參考效果:

一部40年的生活演變史
網(wǎng)易,逃不掉的四字魔咒(手機(jī)預(yù)覽)

參考資料:

PixiJS官方手冊(cè)
PixiJS教程通俗易懂(日語)
PixiJS教程通俗易懂(翻譯)
實(shí)戰(zhàn)教程(帶demo)
TweenMax官方手冊(cè)
(使用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>
  • 基本的實(shí)現(xiàn)步驟
//1 、 以可視區(qū)域大小創(chuàng)建渲染器
var renderer = PIXI.autoDetectRenderer(window.innerWidth,window.innerHeight);
//2 、 將舞臺(tái)添加到,頁面中
document.body.appendChild(renderer.view);
//3 、 創(chuàng)建元素容器,類似 ‘組’概念,Container可以嵌套Container
var stageBox = new PIXI.Container();
/*
4 、
使用加載器預(yù)加載圖片。& 監(jiān)聽加載成功事件 ‘progress’ 多次觸發(fā) ,target.progress 返回加載成功百分比
加載完成 觸發(fā)load 里面的 setup函數(shù) 。 
*/
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年的生活演變史】

預(yù)覽地址

  • 其中js部分有很多可以封裝的地方,自己沒有做封裝
  • 只做了部分效果(后半部分大同小異)
  • 設(shè)計(jì)稿在這里,bg1.psd
  • 對(duì)應(yīng)設(shè)計(jì)稿還原場(chǎng)景
//根據(jù)設(shè)計(jì)稿 還原場(chǎng)景
  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);
  ...
  • 設(shè)置場(chǎng)景中需要?jiǎng)赢嫷脑?/li>
//設(shè)置動(dòng)畫
  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);
  ...
  • 里面有很多地方用到了幀動(dòng)畫,使用pixijs 的 AnimatedSprite(動(dòng)畫精靈)實(shí)現(xiàn)
  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();
  • 其中用到了倆個(gè)時(shí)間軸,因?yàn)檎w動(dòng)畫中存在循環(huán)播放的動(dòng)畫(按鈕放大縮小效果)。一個(gè)時(shí)間軸控制播放到不同時(shí)間播放不同的動(dòng)畫,另一個(gè)時(shí)間軸控制循環(huán)動(dòng)畫(按鈕縮放動(dòng)畫)


    image.png
  var tm = new TimelineLite({paused:true});
  var tm2 = new TimelineLite();
  • range() 函數(shù)判斷 在不同時(shí)間 播放不同的背景聲音以及幀動(dòng)畫的跳轉(zhuǎn)暫停。
  document.querySelector('#music0').play();
  spriteBox.bd.gotoAndStop(timeline/60);
  • 場(chǎng)景滑動(dòng)緩動(dòng)事件,以及緩動(dòng)效果。
        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是外層總?cè)萜鳎╯tageBox)的動(dòng)畫時(shí)長(zhǎng)
           
            range();
            tm.seek(timeline);//跳轉(zhuǎn)到對(duì)應(yīng)時(shí)間
            renderer.render(stageBox);//渲染場(chǎng)景
            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('點(diǎn)擊');
            })
            //根據(jù)設(shè)計(jì)稿 還原場(chǎng)景
            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]);//精靈 添加進(jìn) 容器
            }

            stageBox.scale.set(window.innerWidth/draftWidth);//容器根據(jù)設(shè)計(jì)稿 計(jì)算縮放
            
            //設(shè)置動(dòng)畫
            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是外層總?cè)萜鳎╯tageBox)的動(dòng)畫時(shí)長(zhǎng)
           
            range();
            tm.seek(timeline);//跳轉(zhuǎn)到對(duì)應(yīng)時(shí)間
            renderer.render(stageBox);//渲染場(chǎng)景
            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>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容