js

 

研究了一番impress.js:css

 

我的感受impress.js的代碼量(算上註釋一共不到1000行)和難度(沒有jQuery的各類black magic= =)都很是適合新手學習,因此寫一個總結,幫助你們理解源碼。html

 

考慮到不少朋友並不喜歡深刻細節,下文分爲四部分:css3

 

函數目錄:彙總全部函數及其做用,方便查看canvas

事件分析:瞭解impress.js的運行基礎api

流程分析:瞭解impress.js的運行流程瀏覽器

消化代碼:具體到行的代碼講解app

前三部分是必看的,最後一部分能夠根據我的興趣選擇。因爲我看代碼一貫喜歡摳細節,在我看來細節纔是最能提升能力而且最有趣的地方,因此我會把每行代碼甚至每一個變量每一個表達式都講清楚,讓你真正的看懂impress.js。函數

 

因爲最後一節會寫詳細解釋,因此前幾節中出現的代碼我不會詳細解釋,只會說明大概的功能,方便你們理解。對細節感興趣的朋友能夠看最後一節。學習

 

函數目錄scala

你能夠暫時先跳過這一節或者簡單瀏覽一下,後面看代碼的時候能夠再來查函數做用。

 

函數名 函數做用

pfx 給css3屬性加上當前瀏覽器可用的前綴

arrayify 將Array-Like對象轉換成Array對象

css 將指定屬性應用到指定元素上

toNumber 將參數轉換成數字,若是沒法轉換返回默認值

byId 經過id獲取元素

$ 返回知足選擇器的第一個元素

$$ 返回知足選擇器的全部元素

triggerEvent 在指定元素上觸發指定事件

translate 將translate對象轉換成css使用的字符串

rotate 將rotate對象轉換成css使用的字符串

scale 將scale對象轉換成css使用的字符串

perspective 將perspective對象轉換成css使用的字符串

getElementFromHash 根據hash來獲取元素,hash就是URL中形如#step1的東西

computeWindowScale 根據當前窗口尺寸計算scale因子,用於放大和縮小

empty 什麼用都沒有的函數,當瀏覽器不支持impress的時候會用到,一點用都沒有

impress 主函數,構造impress對象,這是一個全局對象

onStepEnter 用於觸發impress:stepenter事件

onStepLeave 用於觸發impress:stepleave事件

initStep 初始化給定step

init 主初始化函數

getStep 獲取指定step

goto 切換到指定step

prev 切換到上一個step

next 切換到下一個step

throttle 能夠延後運行某個函數

事件分析

先明白一個基本概念——step。 step就是impress.js畫布中的基本單位,一個step就是一幕,你按一次鍵盤上的←鍵或者→鍵就會切換一次step。

 

事件是impress.js運行的基礎,共有三個,分別是impress:init, impress:stepenter和impress:stepleave(下文將省略impress前綴)。

 

init是初始化事件,stepenter是進入下一步事件,stepleave是離開上一步事件。

 

init事件只在初始化時候觸發,且只被觸發一次,由於impress.js內部有一個initialized變量,初始化以後這個變量會置True,從而保證只初始化一次。 下一節中咱們會詳細講解init事件,這裏暫時跳過。

 

那麼stepenter和stepleave有什麼用呢? 假設咱們如今處在第1步,咱們按一下鍵盤上的→鍵就會切換到第2步,這背後impress.js實際上連續觸發了兩個事件:stepleave和stepenter,二者一先一後連貫起來就構成了咱們看到的切換效果。

 

流程分析

impress對象暴露了四個API,分別是goto(), init(), next(), prev()。因爲next()和prev()都是基於goto()寫的,因此咱們下面重點分析goto()和init()。

 

impress.js的運行流程能夠分爲兩大部分——初始化過程以及step切換過程,正好對應init()和goto()。就像上面說到的。初始化過程只會被運行一次,而切換過程可能被觸發不少次。

 

咱們先來分析重中之重——初始化過程

 

初始化過程分爲兩個階段,第一個階段是運行init()函數,第二個階段是運行綁定到impress:init上的函數。這兩個階段之間的鏈接很是簡單,就是在init()函數的結尾觸發impress:init事件,這樣綁定上去的函數就會所有觸發了。

 

來看看init()函數都幹了什麼:

 

 

 1 var init = function () {

 2 if (initialized) { return; }

 3 

 4 // 首先設定viewport

 5 var meta = $("meta[name='viewport']") || document.createElement("meta");

 6 meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";

 7 if (meta.parentNode !== document.head) {

 8 meta.name = 'viewport';

 9 document.head.appendChild(meta);

10 }

11 

12 // 初始化config對象

13 var rootData = root.dataset;

14 config = {

15 width: toNumber( rootData.width, defaults.width ),

16 height: toNumber( rootData.height, defaults.height ),

17 maxScale: toNumber( rootData.maxScale, defaults.maxScale ),

18 minScale: toNumber( rootData.minScale, defaults.minScale ),                

19 perspective: toNumber( rootData.perspective, defaults.perspective ),

20 transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration )

21 };

22 

23 // 計算當前scale

24 windowScale = computeWindowScale( config );

25 

26 // 將全部step放到canvas中,再將canvas放到root中。

27 // 注意這裏的canvas和css3中的canvas不要緊,這裏的canvas只是一個div

28 arrayify( root.childNodes ).forEach(function ( el ) {

29 canvas.appendChild( el );

30 });

31 root.appendChild(canvas);

32 

33 // 設置html元素的初始高度

34 document.documentElement.style.height = "100%";

35 

36 // 設置body元素的初始屬性

37 css(body, {

38 height: "100%",

39 overflow: "hidden"

40 });

41 

42 // 設置根元素的初始屬性

43 var rootStyles = {

44 position: "absolute",

45 transformOrigin: "top left",

46 transition: "all 0s ease-in-out",

47 transformStyle: "preserve-3d"

48 };

49 

50 css(root, rootStyles);

51 css(root, {

52 top: "50%",

53 left: "50%",

54 transform: perspective( config.perspective/windowScale ) + scale( windowScale )

55 });

56 css(canvas, rootStyles);

57 

58 // 不能肯定impress-disabled類是否存在,因此先remove一下

59 body.classList.remove("impress-disabled");

60 body.classList.add("impress-enabled");

61 

62 // 獲取全部step並初始化他們

63 steps = $$(".step", root);

64 steps.forEach( initStep );

65 

66 // 設置canvas的初始狀態

67 currentState = {

68 translate: { x: 0, y: 0, z: 0 },

69 rotate: { x: 0, y: 0, z: 0 },

70 scale: 1

71 };

72 

73 initialized = true;

74 

75 // 觸發init事件

76 triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] });

77 };

 

 

 

init()函數搞清楚了,下面咱們分析第二階段:運行綁定到impress:init事件上的函數。 來看看impress:init事件上面都綁定了什麼函數:

 

按 Ctrl+C 複製代碼

 

按 Ctrl+C 複製代碼

 

 

咱們來梳理一遍,初始化過程作了什麼事:

 

init()函數中主要初始化畫布、step以及impress對象內部用到的一些狀態

綁定到impress:init事件上的函數把其餘須要綁定的事件都進行了綁定,讓impress能夠正常工做

接下來咱們分析step切換過程,來看看goto函數都幹了什麼

 

什麼?你有點累了?加把勁,必定要看完goto

 

 

 1 var goto = function ( el, duration ) {

 2 

 3 if ( !initialized || !(el = getStep(el)) ) {

 4 //若是沒初始化或者el不是一個step就返回

 5 return false;

 6 }

 7 

 8 // 爲了不載入時候瀏覽器滾動,手動滾動到0,0

 9 window.scrollTo(0, 0);

10 

11 var step = stepsData["impress-" + el.id];

12 

13 // 清理當前活躍step上面的標記

14 if ( activeStep ) {

15 activeStep.classList.remove("active");

16 body.classList.remove("impress-on-" + activeStep.id);

17 }

18 // 給el加活躍標記

19 el.classList.add("active");

20 

21 body.classList.add("impress-on-" + el.id);

22 

23 // 計算canvas相對於當前step的變換參數

24 var target = {

25 rotate: {

26 x: -step.rotate.x,

27 y: -step.rotate.y,

28 z: -step.rotate.z

29 },

30 translate: {

31 x: -step.translate.x,

32 y: -step.translate.y,

33 z: -step.translate.z

34 },

35 scale: 1 / step.scale

36 };

37 

38 // 處理縮放

39 var zoomin = target.scale >= currentState.scale;

40 

41 duration = toNumber(duration, config.transitionDuration);

42 var delay = (duration / 2);

43 

44 // 若是el就是當前活躍step,從新計算scale

45 if (el === activeStep) {

46 windowScale = computeWindowScale(config);

47 }

48 

49 var targetScale = target.scale * windowScale;

50 

51 // 觸發stepleave事件

52 if (activeStep && activeStep !== el) {

53 onStepLeave(activeStep);

54 }

55 

56 css(root, {

57 transform: perspective( config.perspective / targetScale ) + scale( targetScale ),

58 transitionDuration: duration + "ms",

59 transitionDelay: (zoomin ? delay : 0) + "ms"

60 });

61 

62 css(canvas, {

63 transform: rotate(target.rotate, true) + translate(target.translate),

64 transitionDuration: duration + "ms",

65 transitionDelay: (zoomin ? 0 : delay) + "ms"

66 });

67 

68 if ( currentState.scale === target.scale ||

69 (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y &&

70 currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x &&

71 currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) {

72 delay = 0;

73 }

74 

75 // 存儲當前狀態

76 currentState = target;

77 activeStep = el;

78 

79 // 觸發stepenter事件

80 window.clearTimeout(stepEnterTimeout);

81 stepEnterTimeout = window.setTimeout(function() {

82 onStepEnter(activeStep);

83 }, duration + delay);

84 

85 return el;

86 };

 

 

 

好了,下面簡單看看prev和next函數:

 

 

 1 var prev = function () {

 2 var prev = steps.indexOf( activeStep ) - 1;

 3 prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ];

 4 

 5 return goto(prev);

 6 };

 7 

 8 var next = function () {

 9 var next = steps.indexOf( activeStep ) + 1;

10 next = next < steps.length ? steps[ next ] : steps[ 0 ];

11 

12 return goto(next);

13 };

諸如此類的例子還有不少!

相關文章
相關標籤/搜索