Skip to content

Instantly share code, notes, and snippets.

@mitsuru793
Last active December 6, 2015 03:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mitsuru793/5b113f0f2f724df7267e to your computer and use it in GitHub Desktop.
Save mitsuru793/5b113f0f2f724df7267e to your computer and use it in GitHub Desktop.
impress.jsのinit部分をコードリーディングする
// 全体図が分かるように、initに関係するものだけ抜き取りました。
(function ( document, window ) {
'use strict';
// data of all presentation steps
var stepsData = {};
// rootには<div id="impress">のDOMツリーが入っています。
var roots = {};
var defaults = {
width: 1024,
height: 768,
maxScale: 1,
minScale: 0,
perspective: 1000,
transitionDuration: 1000
};
var impress = window.impress = function ( rootId ) {
var currentState = null;
var initialized = false;
var init = function () {
if (initialized) { return; }
var meta = $("meta[name='viewport']") || document.createElement("meta");
meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";
// 省略
};
};
})(document, window);
var init = function () {
// init()を実行済みなら何もしない
if (initialized) { return; }
// First we set up the viewport for mobile devices.
// For some reason iPad goes nuts when it is not done properly.
var meta = $("meta[name='viewport']") || document.createElement("meta");
// metaタグのcontent属性をセットします。
// Elementオブジェクトは、作成したタグで使える属性は、プロパティとして用意されています。(idなど)
meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";
// document.headで現在のheadタグを取得します。
// metaタグがheadタグの直下にあるかを確認しています。
// metaタグが取得できずに、新規作成した場合は親ノードはないのでfalseです。
if (meta.parentNode !== document.head) {
meta.name = 'viewport';
document.head.appendChild(meta);
}
// rootには<div id="impress">のDOMツリーが入っています。
// datasetはHTML5のdata属性にアクセスするための属性です。詳細:http://uhyohyo.net/javascript/12_4.html
var rootData = root.dataset; // DOMStringMap {}
config = {
width: toNumber( rootData.width, defaults.width ),
height: toNumber( rootData.height, defaults.height ),
maxScale: toNumber( rootData.maxScale, defaults.maxScale ),
minScale: toNumber( rootData.minScale, defaults.minScale ),
perspective: toNumber( rootData.perspective, defaults.perspective ),
transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration )
};
// 縦・横のスケールを求めて、小さい方を返します。
windowScale = computeWindowScale( config );
// div#impressの子要素をすべて、canvasにコピーします。
arrayify( root.childNodes ).forEach(function ( el ) {
canvas.appendChild( el );
});
root.appendChild(canvas);
// set initial styles
document.documentElement.style.height = "100%";
css(body, {
height: "100%",
overflow: "hidden"
});
var rootStyles = {
position: "absolute",
transformOrigin: "top left",
transition: "all 0s ease-in-out",
transformStyle: "preserve-3d"
};
css(root, rootStyles);
css(root, {
top: "50%",
left: "50%",
transform: perspective( config.perspective/windowScale ) + scale( windowScale )
});
css(canvas, rootStyles);
// classListは、HTML5で追加されました。classを追加、削除がピンポイントで可能です。
body.classList.remove("impress-disabled");
body.classList.add("impress-enabled");
// root要素からstepクラスがついた要素を取得します。
steps = $$(".step", root);
// initStepには、各DOM要素と、そのインデックス番号が渡ります。
steps.forEach( initStep );
// set a default initial state of the canvas
// 外のコンテキストで定義された変数なので、
// クロージャによって値はinit()実行後も保持されます。
currentState = {
translate: { x: 0, y: 0, z: 0 },
rotate: { x: 0, y: 0, z: 0 },
scale: 1
};
initialized = true;
// root要素に"impress:init"というイベントを発行します。
// 第3引数には、イベントハンドラに渡したいデータを設定します。
triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] });
};
// init()で使われる内部関数です。登場する順番に記述してあります。
// そのため、実際に定義されている順番とは違います。
// numeric: Number型に変換する
// fallback: numbercが非数の場合の戻り値。デフォルトでは、0が返ります。
var toNumber = function (numeric, fallback) {
// isNaNは、非数(数値ではない)の場合にtrueを返します。
// 数値だけでなく、文字列の数字でもfalseを返します。
return isNaN(numeric) ? (fallback || 0) : Number(numeric);
};
// 縦・横のスケールを求めて、小さい方を返します。
var computeWindowScale = function ( config ) {
var hScale = window.innerHeight / config.height,
wScale = window.innerWidth / config.width,
// 縦・横、小さい方のスケールを取得します。
scale = hScale > wScale ? wScale : hScale;
// config.maxScaleが存在してるかを、まず確認してから比較しています。
if (config.maxScale && scale > config.maxScale) {
scale = config.maxScale;
}
if (config.minScale && scale < config.minScale) {
scale = config.minScale;
}
return scale;
};
// 配列である引数をコピーして返します。
var arrayify = function ( a ) {
// sliceは引数がないと全要素を返します。
// 配列をコピーするのに使っています。concatで代用も可能です。
return [].slice.call( a );
};
// el(DOM)に、props(オブジェクト)にあるスタイルを設定して返します。
var css = function ( el, props ) {
var key, pkey;
for ( key in props ) {
// 親クラスのプロパティまで参照しないようにします。
if ( props.hasOwnProperty(key) ) {
pkey = pfx(key);
if ( pkey !== null ) {
el.style[pkey] = props[key];
}
}
}
return el;
};
// 引数propから、現在のブラウザで使えるCSSプロパティ名を探して取得します。
var pfx = (function () {
// styleのプロパティ名が使えるかをチェックするために使います。
var style = document.createElement('dummy').style,
// タイプ数を減らす、配列の作り方です。
prefixes = 'Webkit Moz O ms Khtml'.split(' '),
// クロージャで使われるので、この変数の値はずっと残ります。
memory = {};
// memoryにキャッシュしてある、CSSプロパティ名を取り出します。
return function ( prop ) {
// memoryにキャッシュしていない場合は、キャッシュします。
if ( typeof memory[ prop ] === "undefined" ) {
// ucはUpperCaseの略だと思います。
// プロパティ名の先頭を大文字にします。
var ucProp = prop.charAt(0).toUpperCase() + prop.substr(1),
// そのまま、ベンダープレフィックスをつけたものと、先頭大文字のスタイル名を含んだ配列を作ります。
props = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
// 使えるスタイル名がなかった時のために、nullを入れます。
memory[ prop ] = null;
// 使えるスタイル名があったら、スタイル名を入れてブレイクします。
for ( var i in props ) {
if ( style[ props[i] ] !== undefined ) {
memory[ prop ] = props[i];
break;
}
}
}
return memory[ prop ];
};
})();
// context以下のdomツリーからセレクタに当てはまる要素を取得します。
// contextのデフォルト値はdocumentです。
var $$ = function ( selector, context ) {
context = context || document;
// 取得した配列をコピーして返します。
return arrayify( context.querySelectorAll(selector) );
};
// `initStep` initializes given step element by reading data from its
// data attributes and setting correct styles.
var initStep = function ( el, idx ) {
// HTML5で追加されたdata属性のためのインターフェースです。
var data = el.dataset,
step = {
translate: {
x: toNumber(data.x),
y: toNumber(data.y),
z: toNumber(data.z)
},
rotate: {
x: toNumber(data.rotateX),
y: toNumber(data.rotateY),
z: toNumber(data.rotateZ || data.rotate)
},
scale: toNumber(data.scale, 1),
el: el
};
if ( !el.id ) {
el.id = "step-" + (idx + 1);
}
stepsData["impress-" + el.id] = step;
css(el, {
position: "absolute",
transform: "translate(-50%,-50%)" +
translate(step.translate) +
rotate(step.rotate) +
scale(step.scale),
transformStyle: "preserve-3d"
});
};
// cssの値(文字列)を組み立てます。
var translate = function ( t ) {
return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
};
// cssの値(文字列)を組み立てます。
// revertはx,y,zの値を逆から書けるオプションです。
var rotate = function ( r, revert ) {
var rX = " rotateX(" + r.x + "deg) ",
rY = " rotateY(" + r.y + "deg) ",
rZ = " rotateZ(" + r.z + "deg) ";
return revert ? rZ+rY+rX : rX+rY+rZ;
};
// cssの値(文字列)を組み立てます。
var scale = function ( s ) {
return " scale(" + s + ") ";
};
// cssの値(文字列)を組み立てます。
var perspective = function ( p ) {
return " perspective(" + p + "px) ";
};
// `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data
// and triggers it on element given as `el`.
var triggerEvent = function (el, eventName, detail) {
var event = document.createEvent("CustomEvent");
// initCustomEventは非推奨です。https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/initCustomEvent
// 引数:(イベントの種類, イベントがバブルするか, イベントがキャセル可能か, イベントデータ)
// 上記の引数を用いてCustomEventを初期化します。http://webkledgeb.blogspot.jp/2014/08/dom-customevent-3-customeventinitcustom.html
event.initCustomEvent(eventName, true, true, detail);
// 要素elに、eventイベントを発行します。イベントをエミュレートできます。
el.dispatchEvent(event);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment