Source: tool/FPSConsole.js

/**
 * 렌더러 측정 정보를 보여주는 도구
 * - FPSConsole을 사용시 애니메이션 속도에 영향을 줄 수 있습니다.
 * @param {Object} htOption
 * @param {String} [htOption.color="gray"] 콘솔 폰트 색상
 * @param {Number} [htOption.left=0] 콘솔 left 위치
 * @param {Number} [htOption.top=0] 콘솔 top 위치
 * @param {Number} [htOption.interval=60] 갱신 주기 (단위는 s)
 * @param {Boolean} [htOption.visible=true] 콘솔을 화면에 표시 여부
 * @param {Number} [htOption.start=0] 콘솔 자료 수집 시작 시점(frame)
 * @param {Number} [htOption.limit=0] 렌더링할 프레임 수(0이면 임의로 멈추지 않는다)
 * @param {Boolean} [htOption.useConsole=false] 콘솔을 화면에 표시 안할 때 window.console에 표시할지 여부
 * @param {Boolean} [htOption.useDrawingCount=false] 레이어에서 몇 번이나 draw가 일어났는지 확인
 * @example
 * var oConsole = new collie.FPSConsole().load();
 * oConsole.add("test", "testValue", "testTitle"); // Add a custom data
 * collie.Renderer.start();
 * @requires collie.tool.js
 * @extends collie.Component
 * @class
 */
collie.FPSConsole = collie.Class(/** @lends collie.FPSConsole.prototype */{
    $init : function (htOption) {
        this.option({
            color : 'gray',
            left : 10,
            top : 10,
            useConsole : false,
            useDrawingCount : false,
            visible : true,
            limit : 0,
            start : 0,
            interval : 60
        });
        
        
        if (typeof htOption !== "undefined") {
            this.option(htOption);
        }
        
        this._elContainer = null;
        this._elParent = null;
        this._aList = [];
        this._bLoaded = false;
        this._bStart = false;
        this._nWidth = 100;
        this._nHeight = 13;
        this._nPadding = 2;
        this._nLastUpdatedFrame = 0;
        this._htData = {};
        this._htInfo = {};
        this._htFPS = {
            average : 0,
            min : null,
            max : null,
            total : 0,
            count : 0
        };

        this._htEvent = {
            process : this._onProcess.bind(this),
            stop : this._onProcess.bind(this)
        };
        
        this.optionSetter("visible", function (value) {
            if (!value) {
                this._elContainer.style.display = "none";
            } else {
                this._elContainer.style.display = "block";
            }
        }.bind(this));
        
        this._initElement();
    },
    
    /**
     * @private
     */
    _initElement : function () {
        this._elContainer = document.createElement("ul");
        this._elContainer.id = "__collie_fpsconsole";
        this._elContainer.style.position = "absolute";
        this._elContainer.style.top = this.option("top") + "px";
        this._elContainer.style.left = this.option("left") + "px";
        this._elContainer.style.margin = "0";
        this._elContainer.style.padding = "0";
        this._elContainer.style.width = this._nWidth + "px";
        this._elContainer.style.height = this._nHeight + "px";
        this._elContainer.style.font = "bold 0.75em Helvetica";
        this._elContainer.style.color = this.option("color");
        this._elContainer.style.zIndex = 9999;
        this._elContainer.style.display = (this.option("console") ? 'none' : 'block');
        
        // 기본 2개 엘리먼트
        this.add("fps", 0, "FPS");
        this.add("mode", "");
    },
    
    /**
     * FPSConsole을 로드 한다
     * 
     * @param {HTMLElement|String} elParent console을 붙일 엘리먼트, 없으면 document.body에 붙는다.
     * @return {collie.FPSConsole}
     */
    load : function (elParent) {
        if (typeof elParent === "string") {
            elParent = document.getElementById(elParent);
        }
        
        if (typeof elParent !== "undefined") {
            this._elParent = elParent;
            this._elParent.appendChild(this._elContainer);
        }
        
        collie.Renderer.attach(this._htEvent);
        this._bLoaded = true;
        return this;
    },
    
    /**
     * FPSConsole을 unload한다
     * 
     * @return {collie.FPSConsole}
     */
    unload : function () {
        if (this._bLoaded) {
            if (this._elParent !== null) {
                this._elParent.removeChild(this._elContainer);
                this._elParent = null;
            }
            
            collie.Renderer.detach(this._htEvent);
            this._bLoaded = false;
            this._bStart = false;
        }
        
        return this;
    },
    
    /**
     * Console 엘리먼트를 반환
     * @return {HTMLElement}
     */
    getElement : function () {
        return this._elContainer;
    },
    
    /**
     * Add a new data for display in the FPSConsole list.
     * 
     * @param {String} sName For handling value of a new data. It's used in the change method.
     * @param {Variable} vValue
     * @param {String} [sTitle] The Title for a new data is optional.
     * @return {collie.FPSConsole} For method chaining
     * @example
     * var oConsole = new FPSConsole({
     *  color : "#fff"
     * }).load();
     * 
     * oConsole.add("test", "testValue", "testTitle"); // It looks like "<b>testTitle</b> testValue" 
     */
    add : function (sName, vValue, sTitle) {
        if (sName in this._htOption) {
            throw new Error("Exists data name in FPSConsole");
            return false;
        }
 
        var el = document.createElement("li");
        el.style.listStyleType = "none";
        el.style.height = this._nHeight + "px";
        el.style.paddingTop = this._nPadding + "px";
        el.innerHTML = (typeof sTitle !== "undefined" ? "<b>" + sTitle + ":</b> " : "");
        
        var elContent = document.createElement("span");
        elContent.style.fontWeight = "bold";
        elContent.innerHTML = vValue;
        el.appendChild(elContent);
        this._elContainer.appendChild(el);
        var nLength = this._aList.push({
            name : sName,
            value : vValue,
            element : elContent
        });
        
        this._elContainer.style.height = (nLength * this._nHeight) + "px";
        this._htData[sName] = nLength - 1;
        return this;
    },
    
    /**
     * 추가한 값을 변경
     * 
     * @param {String} sName
     * @param {Variable} vValue
     */
    change : function (sName, vValue) {
        if (typeof this._htData[sName] !== "undefined") {
            var htContent = this._aList[this._htData[sName]];
            htContent.value = vValue;
            htContent.element.innerHTML = vValue;
        }
    },
    
    /**
     * @private
     */
    _onProcess : function (oEvent) {
        if (this._htOption.start > oEvent.frame) {
            return;
        }
        
        // 처음 시작할 때
        if (!this._bStart) {
            if (this._elParent === null) {
                this._elParent = collie.Renderer.getElement();
                this._elParent.appendChild(this._elContainer);
            }
            
            this.change("mode", this._setMode());
            this._bStart = true;
        }
        
        this._setValue(oEvent, oEvent.type === "stop");
        
        if (this._htOption.limit && oEvent.frame > this._htOption.limit) {
            collie.Renderer.stop();
        }
    },
    
    /**
     * @private
     */
    _setMode : function () {
        var sMode = collie.Renderer.getRenderingMode() + (collie.Renderer.isRetinaDisplay() ? "(retina)" : "");
        sMode = sMode.toUpperCase();
        return sMode; 
    },
    
    _setFPS : function () {
        return this._htInfo.fps;
    },
    
    /**
     * @private
     * @param {Object} htInfo
     * @param {Boolean} bForceView 조건이 충족되지 않아도 view에 표시한다
     */
    _setValue : function (htInfo, bForceView) {
        this._htInfo.frame = htInfo.frame;
        this._htInfo.skippedFrame = htInfo.skippedFrame;
        this._htInfo.fps = htInfo.fps;
        this._htInfo.renderingTime = htInfo.renderingTime;
        this._htFPS.total += htInfo.fps;
        this._htFPS.count++;
        this._htInfo.fpsAverage = Math.round((this._htFPS.total / this._htFPS.count) * 100) / 100;
        this._htInfo.fpsMin = this._htFPS.min = this._htFPS.min !== null ? Math.min(this._htFPS.min, htInfo.fps) : htInfo.fps;
        this._htInfo.fpsMax = this._htFPS.max = this._htFPS.max !== null ? Math.max(this._htFPS.max, htInfo.fps) : htInfo.fps;

        // stop된 경우
        if (this._nLastUpdatedFrame > htInfo.frame) {
            this._nLastUpdatedFrame = 0;
        }
        
        if (bForceView || this._nLastUpdatedFrame + this._htOption.interval <= htInfo.frame) {
            this._update();
        }
        
        return this;
    },
    
    /**
     * @private
     * @param {String} [sName] 없으면 객체 반환
     * @return {Object|Variable}
     */
    _getValue : function (sName) {
        return !sName ? this._htInfo : this._htInfo[sName];
    },
        
    /**
     * 화면에 표시 한다
     * 
     * @private
     */
    _update : function () {
        this.change("fps", this._setFPS());
        
        if (!this.option("visible")) {
            if (this.option("useConsole")) {
                for (var i = 0, l = this._aList.length; i < l; i++) {
                    console.log(this._aList[i].name + ": " + this._aList[i].value);
                }
            }
        } else if (this._bLoaded) {
            this._nLastUpdatedFrame = this._htInfo.frame;
            
            if (this._htOption.useDrawingCount) {
                for (var i = 0, l = collie.Renderer._aLayerList.length; i < l; i++) {
                    this._addDrawingCount(i);
                    this.change("layer" + i, collie.Renderer._aLayerList[i].drawCount);
                }
            }
        }
    },
    
    /**
     * drawingCount 추가
     * 
     * @private
     * @param {Number} i 레이어 순서
     */
    _addDrawingCount : function (i) {
        var sName = "layer" + i;
        
        if (!this._htData[sName]) {
            this.add(sName, 0, "#" + i);
        }
    }
}, collie.Component);
comments powered by Disqus