SOURCE

console 命令行工具 X clear

                    
>
console
var BRAINYMO = BRAINYMO || {};

BRAINYMO.Game = (function() {
  
    var activeCards = [];
    var numOfCards;
    var cardHitCounter = 0;
    var card;
    var timer;
    var storage;

    /**
     * Method that will be invoked on card click
     */
    function handleCardClick() {

        var connection = $(this).data('connection');
        var hit;

        // Set card in active state
        // 'this' needs to be attached to context of card which is clicked
        if ( !$(this).hasClass('active') ) {
            $(this).addClass('active');
            activeCards.push($(this));

            // If user click on two cards then check
            if (activeCards.length == 2) {
                hit = checkActiveCards(activeCards);
            }

            if (hit === true) {
                cardHitCounter++;
                activeCards[0].add(activeCards[1]).unbind().addClass('wobble cursor-default');
                activeCards = [];

                // Game End
                if(cardHitCounter === (numOfCards / 2)) {
                    // Reset active cards
                    activeCards = [];
                    // Reset counter
                    cardHitCounter = 0;
                    // End game
                    endGame();
                }
            }
            // In case when user open more then 2 cards then automatically close first two
            else if(activeCards.length === 3) {
                for(var i = 0; i < activeCards.length - 1; i++) {
                    activeCards[i].removeClass('active');
                }
                activeCards.splice(0, 2);
            }
        }
    }

    function endGame() {
        timer.stopTimer();

        // Retrieve current time
        var time = timer.retrieveTime();

        // Retrieve time from storage
        var timeFromStorage = storage.retrieveBestTime();

        // if there's already time saved in storage check if it's better than current one
        if (timeFromStorage != undefined && timeFromStorage != '') {
            // if current game time is better than one saved in store then save new one
            if (time.minutes < timeFromStorage.minutes || (time.minutes == timeFromStorage.minutes && time.seconds < timeFromStorage.seconds) ) {
                storage.setBestTime(time);
            }
        }
        // else if time is not saved in storage save it
        else {
            storage.setBestTime(time);
        }

        // Update best time
        timer.updateBestTime();
    }

    function checkActiveCards(connections) {
        return connections[0].data('connection') === connections[1].data('connection');
    }

    return function(config) {

        /**
         * Main method for game initialization
         */
        this.startGame = function() {
            card = new BRAINYMO.Card();
            timer = new BRAINYMO.Timer();
            storage = new BRAINYMO.Storage();
            numOfCards = config.cards.length;
            card.attachCardEvent(handleCardClick, config);
        };

        /**
         * After game initialization call this method in order to generate cards
         */
        this.generateCardSet = function() {
            // Generate new card set
            card.generateCards(config.cards);
            // Reset active cards array
            activeCards = [];

            // Reset timer
            timer.stopTimer();
            // Set timer
            timer.startTimer();
        };

        this.startGame();
    }

})();



BRAINYMO.Card = (function () {

    // Private variables
    var $cardsContainer = $('.cards-container');
    var $cardTemplate = $('#card-template');

    /**
     * Private method
     * Take card template from DOM and update it with card data
     * @param {Object} card - card object
     * @return {Object} template - jquery object
     */
    function prepareCardTemplate (card) {
        var template = $cardTemplate
                            .clone()
                            .removeAttr('id')
                            .removeClass('hide')
                            .attr('data-connection', card.connectionID);

        // If card has background image
        if (card.backImg != '' && card.backImg != undefined) {
            template.find('.back').css({
                'background': 'url(' + card.backImg + ') no-repeat center center',
                'background-size': 'cover'
            });
        }
        // Else if card has no background image but has text
        else if (card.backTxt != '' && card.backTxt != undefined) {
            template.find('.back > label').html(card.backTxt);
        }

        return template;
    }

    /**
     * Private method
     * Method for random shuffling array
     * @param {Object} cardsArray - array of card objects
     * @return {Object} returns random shuffled array
     */
    function shuffleCards(cardsArray) {
        var currentIndex = cardsArray.length, temporaryValue, randomIndex;

        while (0 !== currentIndex) {
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex -= 1;
            temporaryValue = cardsArray[currentIndex];
            cardsArray[currentIndex] = cardsArray[randomIndex];
            cardsArray[randomIndex] = temporaryValue;
        }

        return cardsArray;
    }
    
    return function() {

        /**
         * Public method
         * Prepare all cards and insert them into DOM
         * Before inserting new set of cards method will erase all previous cards
         * @param {Object} cards - array of card objects
         */
        this.generateCards = function(cards) {
            var templates = [];
            var preparedTemplate;

            // Prepare every card and push it to array
            cards.forEach(function (card) {
                preparedTemplate = prepareCardTemplate(card);
                templates.push(preparedTemplate);
            });

            // Shuffle card array
            templates = shuffleCards(templates);

            // Hide and empty card container
            $cardsContainer.hide().empty();

            // Append all cards to cards container
            templates.forEach(function(card) {
                $cardsContainer.append(card);
            });

            // Show card container
            $cardsContainer.fadeIn('slow');
        };

        /**
         * Public method
         * Attach click event on every card
         * Before inserting new set of cards method will erase all previous cards
         * @param {Function} func - function that will be invoked on card click
         */
        this.attachCardEvent = function(func) {
            $cardsContainer.unbind().on('click', '.flip-container', function() {
                func.call(this);
            });
        }
    }
    
})();

BRAINYMO.Timer = (function() {

    var $timer = $('.timer');
    var $seconds = $timer.find('#seconds');
    var $minutes = $timer.find('#minutes');
    var $bestTimeContainer = $timer.find('.time');


    var minutes, seconds;
    
    function decorateNumber(value) {
        return value > 9 ? value : '0' + value;
    }

    return function() {
        var interval;
        var storage = new BRAINYMO.Storage();
        
        this.startTimer = function() {
            var sec = 0;
            var bestTime;

            // Set timer interval
            interval = setInterval( function() {
                seconds = ++sec % 60;
                minutes = parseInt(sec / 60, 10);
                $seconds.html(decorateNumber(seconds));
                $minutes.html(decorateNumber(minutes));
            }, 1000);

            // Show timer
            $timer.delay(1000).fadeIn();

            this.updateBestTime();
        };

        this.updateBestTime = function() {
            // Check if user have saved best game time
            bestTime = storage.retrieveBestTime();
            if(bestTime != undefined && bestTime != '') {
                $bestTimeContainer
                    .find('#bestTime')
                    .text(bestTime.minutes + ':' + bestTime.seconds)
                    .end()
                    .fadeIn();
            }
        };
        
        this.stopTimer = function() {
            clearInterval(interval);  
        };

        this.retrieveTime = function() {
            return {
                minutes: decorateNumber(minutes),
                seconds: decorateNumber(seconds)
            }
        };
    }
})();


BRAINYMO.Storage = (function() {

    return function() {

        /**
         * Save best time to localStorage
         * key = 'bestTime'
         * @param {Object} time - object with keys: 'minutes', 'seconds'
         */
        this.setBestTime = function(time) {
            localStorage.setItem('bestTime', JSON.stringify(time));
        };

        /**
         * Retrieve best time from localStorage
         */
        this.retrieveBestTime = function() {
            return JSON.parse(localStorage.getItem('bestTime'));
        };

    }
})();



// Game init
$(function() {
  
        var brainymo = new BRAINYMO.Game({
            cards: [
                {
                    backImg: 'https://s23.postimg.org/gmp35oru3/grunt.jpg',
                    connectionID: 1
                },
                {
                    backTxt: 'GRUNT',
                    connectionID: 1
                },
                {
                    backImg: 'https://s23.postimg.org/4q21yyfaj/react.jpg',
                    connectionID: 2
                },
                {
                    backTxt: 'REACT',
                    connectionID: 2
                },
                {
                    backImg: 'https://s23.postimg.org/raxfi9r6z/gsap.jpg',
                    connectionID: 3
                },
                {
                    backTxt: 'GSAP',
                    connectionID: 3
                },
                {
                    backImg: 'https://s23.postimg.org/bmrmxqm7f/ember.jpg',
                    connectionID: 4
                },
                {
                    backTxt: 'EMBER',
                    connectionID: 4
                },
                {
                    backImg: 'https://s23.postimg.org/o5cts28kr/karma.jpg',
                    connectionID: 5
                },
                {
                    backTxt: 'KARMA', 
                    connectionID: 5
                },
                {
                    backImg: 'https://s23.postimg.org/ck2nkcn3f/webpack.jpg',
                    connectionID: 6
                },
                {
                    backTxt: 'WEBPACK',
                    connectionID: 6
                },
                {
                    backImg: 'https://s23.postimg.org/prxfzjv8r/angular.jpg',
                    connectionID: 7
                },
                {
                    backTxt: 'ANGULAR',
                    connectionID: 7
                },
                {
                    backImg: 'https://s23.postimg.org/oje5rnsob/mongo.jpg',
                    connectionID: 8
                },
                {
                    backTxt: 'MONGO DB',
                    connectionID: 8
                },
            ]
        });

        $('#btn-start').click(function() {
            brainymo.generateCardSet();
            $(this).text('Restart');
        });

    });
<div class="align-center">

    <h1 class="heading">Brainymo</h1>
    <p class="desc">Frontend Arsenal Memory Game</p>

    <button class="btn" id="btn-start">
        Start
    </button>

    <div class="cards-container">
        <div class="flip-container hide" id="card-template">
            <div class="flipper">
                <div class="front">
                    <label>frontend technologies</label>
                </div>
                <div class="back">
                    <label></label>
                </div>
            </div>
        </div>
    </div>

    <div class="timer">
        <label id="minutes"></label>:
        <label id="seconds"></label>
        <div class="time">
            MY BEST TIME: <span id="bestTime"></span>
        </div>
    </div>
</div>
/* Colors */
$white: #FFFFEA;
$red: #FF5E5B;
$blue: #00CECB;
$yellow: #FFED66;

/* Fonts */
$abel: 'Abel', sans-serif;
$lobster: 'Lobster', cursive;

/* Background images */
$bgPattern: "https://www.transparenttextures.com/patterns/inspiration-geometry.png";


@mixin transform($transforms) {
	   -moz-transform: $transforms;
	     -o-transform: $transforms;
	    -ms-transform: $transforms;
	-webkit-transform: $transforms;
          transform: $transforms;
}

/* General */

body {
    background-color: $white;
    background-image: url($bgPattern);
    font-family: $abel;
    color: $red;
}



/* Main page */

.heading {
    font-size: 52px;
    font-family: $lobster;
    color: $red;
    margin-bottom: 0;
}

p.desc {
  letter-spacing: 0.5px;
  margin-top: 0;
  margin-bottom: 60px;
}



/* Cards */ 

.cards-container {
    display: block;
    margin: 40px;
}

.flip-container {
    position: relative;
    display: inline-block;
    margin: 15px;
    perspective: 1000px;
    cursor: pointer;
  
    .flipper {
        position: relative;
        -webkit-transform-style: preserve-3d;
        -webkit-transition: 0.5s;
        -moz-transform-style: preserve-3d;
        -moz-transition: 0.5s;
        -ms-transform-style: preserve-3d;
        -ms-transition: 0.5s;
        -o-transform-style: preserve-3d;
        -o-transition: 0.5s;
    }
  
    &.active .flipper {
        @include transform(rotateY(180deg)); 
    }
}
   

.flip-container, 
.front, 
.back {
    border-radius: 5px;
    color: $white;
    width: 180px;
    height: 220px;
}

.front, 
.back {
    -webkit-backface-visibility: hidden;
    -moz-backface-visibility:    hidden;
    -ms-backface-visibility:     hidden;
    position: absolute;
    top: 0;
    left: 0;
}

.front {
    background-color: $red;
    z-index: 2;
    @include transform(rotateY(0));
  
    label {
      cursor: pointer;
      display: inline-block;
      font-size: 22px;
      padding-top: 15px;
    }
}

.back {
    background-color: $blue;
    text-align: center;
    vertical-align: middle;
    display: table-cell;
    @include transform(rotateY(180deg));
  
    label {
        display: block;
        width: 100%;
        font-size: 24px;
        margin-top: 10px;
    }
}



/* Timer */

.timer {
    display: none;
    position: fixed;
    pointer-events: none;
    left: 30px;
    top: 30px;
  
    label#minutes,
    label#seconds {
        display: inline-block;
        font-size: 20px;
    }
  
    .time {
        display: none;
        font-size: 13px;
    }
}


/* Buttons */

.btn {
    display: inline-block;
    background-color: $yellow;
    padding: 15px 40px;
    border: none;
    border-radius: 30px;

    font-family: $abel;
    font-size: 20px;
    text-decoration: none;
    text-transform: uppercase;
    color: $red;

    box-shadow: 0 3px 0 $red;
    cursor: pointer;
    transition: all 100ms linear;
    
    &:hover {
        @include transform(translateY(-4px));
        box-shadow: 0 7px 0 $red;
    }

    &:focus { outline: 0; }
}


/* Github ribbon */ 
#github {
    position: absolute;
    top: 0;
    right: 0;
    border: 0;
}

/* Helpers */

.align-center {
    text-align: center;
}

.hide {
    display: none !important;
}

.cursor-default {
    cursor: default !important;
}



/* Reponsive Rules */

@media screen and (max-width: 1200px) {
    .flip-container, .front, .back {
        width: 140px;
        height: 180px;
    }

    .timer {
        padding: 10px;
        border-radius: 5px;
        background-color: $white;
    }
}

@media screen and (max-width: 992px) {
    .flip-container, .front, .back {
        width: 100px;
        height: 140px;
    }

    .front label {
        display: inline-block;
        font-size: 16px;
        padding-top: 10px;
    }

    .cards-container {
        margin: 40px 10px;
    }

    .timer {
        top: 10px;
        left: 10px;
    }
}

@media screen and (max-width: 768px) {
    .flip-container, .front, .back {
        width: 80px;
        height: 120px;
    }
}



/* Animations */

@keyframes wobble {
    from { transform: none; }
    15% { transform: translate3d(-10%, 0, 0) rotate3d(0, 0, 1, -5deg); }
    30% { transform: translate3d(10%, 0, 0) rotate3d(0, 0, 1, 3deg); }
    45% { transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -3deg); }
    60% { transform: translate3d(5%, 0, 0) rotate3d(0, 0, 1, 2deg); }
    75% { transform: translate3d(-10%, 0, 0) rotate3d(0, 0, 1, -1deg); }
    to { transform: none; }
}

.wobble {
    animation: wobble 600ms ease-in-out;
}

本项目引用的自定义外部资源