js实现恐龙跳跃类避障小游戏代码

代码语言:html

所属分类:游戏

代码描述:js实现恐龙跳跃类避障小游戏代码

代码标签: 恐龙 避障 游戏

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">

</head>

<body>

    <h3 style="text-align: center">请点击这里,按键盘方向键 ↑ 开始</h3>
    <div id="runner-container" class="runner-container offline" style="height: 200px; width: 600px; background: #fff; margin-left: auto; margin-right: auto; margin-top: 100px; border: 1px solid hsla(0,0%,21%,1.00); box-shadow: -1px 11px 46px 5px rgba(0,0,0,0.34); -webkit-box-shadow: -1px 11px 46px 5px rgba(0,0,0,0.34); -moz-box-shadow: -1px 11px 46px 5px rgba(0,0,0,0.34);">
        &nbsp;
    </div>


    <img id="sprite" style="display: none;" src="//repo.bfw.wiki/bfwrepo/images/game/kongllongspirte.png" alt="test" />


<script>
    var canvas = document.createElement('canvas'),
    a = document.getElementById('runner-container'),
    ctx = canvas.getContext('2d');
canvas.id = 'c';
canvas.width = 600;
canvas.height = 150;
a.appendChild(canvas);

//坐标
var spriteDefinition = {
        CACTUS_LARGE: {
            x: 332,
            y: 2
        }, //大仙人掌
        CACTUS_SMALL: {
            x: 228,
            y: 2
        }, //小仙人掌
        CLOUD: {
            x: 86,
            y: 2
        }, //云
        HORIZON: {
            x: 2,
            y: 54
        }, //地面
        MOON: {
            x: 484,
            y: 2
        }, //月亮
        PTERODACTYL: {
            x: 134,
            y: 2
        }, //翼龙
        RESTART: {
            x: 2,
            y: 2
        }, //重新开始按钮
        TEXT_SPRITE: {
            x: 655,
            y: 2
        }, //分数
        TREX: {
            x: 848,
            y: 2
        }, //霸王龙
        STAR: {
            x: 645,
            y: 2
        } //星星
    },
    FPS = 60,
    DEFAULT_WIDTH = 600,

    imgSprite = document.getElementById('sprite');

Runner.config = {
    ACCELERATION: 0.001,
    BG_CLOUD_SPEED: 0.2,
    BOTTOM_PAD: 10,
    CLEAR_TIME: 3000,
    CLOUD_FREQUENCY: 0.5,
    GAMEOVER_CLEAR_TIME: 750,
    GAP_COEFFICIENT: 0.6,
    GRAVITY: 0.6,
    INITIAL_JUMP_VELOCITY: 12,
    INVERT_FADE_DURATION: 10000, //夜晚持续时间
    INVERT_DISTANCE: 700, //每700距离进行昼夜交替
    MAX_CLOUDS: 6, //云最大数量
    MAX_OBSTACLE_LENGTH: 3,
    MAX_OBSTACLE_DUPLICATION: 2,
    MAX_SPEED: 13,
    MIN_JUMP_HEIGHT: 35,
    MOBILE_SPEED_COEFFICIENT: 1.2,
    RESOURCE_TEMPLATE_ID: 'audio-resources',
    SPEED: 6,
    SPEED_DROP_COEFFICIENT: 3
};
Runner.defaultDimensions = {
    HEIGHT: 150,
    WIDTH: 600
};

Runner.classes = {
    CANVAS: 'runner-canvas',
    CONTAINER: 'runner-container',
    CRASHED: 'crashed',
    ICON: 'icon-offline',
    INVERTED: 'inverted',
    SNACKBAR: 'snackbar',
    SNACKBAR_SHOW: 'snackbar-show',
    TOUCH_CONTROLLER: 'controller'
};

Runner.sounds = {
    BUTTON_PRESS: 'offline-sound-press',
    HIT: 'offline-sound-hit',
    SCORE: 'offline-sound-reached'
};

Runner.keycodes = {
    JUMP: {
        '38': 1,
        '32': 1
    }, // Up, spacebar
    DUCK: {
        '40': 1
    }, // Down
    RESTART: {
        '13': 1
    } // Enter
};

Runner.events = {
    ANIM_END: 'webkitAnimationEnd',
    CLICK: 'click',
    KEYDOWN: 'keydown',
    KEYUP: 'keyup',
    MOUSEDOWN: 'mousedown',
    MOUSEUP: 'mouseup',
    RESIZE: 'resize',
    TOUCHEND: 'touchend',
    TOUCHSTART: 'touchstart',
    VISIBILITY: 'visibilitychange',
    BLUR: 'blur',
    FOCUS: 'focus',
    LOAD: 'load'
};

function Runner(outerContainerId, opt_config) {
    if (Runner.instance_) {
        return Runner.instance_;
    }
    Runner.instance_ = this;
    //this.outerContainerEl = document.querySelector(outerContainerId);
    this.containerEl = null;
    this.snackbarEl = null;
    //this.detailsButton = this.outerContainerEl.querySelector('#details-button');
    this.config = opt_config || Runner.config;
    this.dimensions = Runner.defaultDimensions;
    this.canvas = null;
    this.ctx = null;

    this.tRex = null;
    this.distanceMeter = null;
    this.distanceRan = 0;
    this.highestScore = 0;

    this.time = 0;
    this.runningTime = 0;
    this.msPerFrame = 1000 / FPS;
    this.currentSpeed = this.config.SPEED;

    this.obstacles = []; //障碍物

    this.started = false;
    this.activated = false;
    this.crashed = false;
    this.paused = false;
    this.inverted = false;
    this.invertTimer = 0;
    this.resizeTimerId_ = null;

    this.playCount = 0;

    // Sound FX.
    this.audioBuffer = null;
    this.soundFx = {};

    // Global web audio context for playing sounds.
    this.audioContext = null;

    // Images.
    this.images = {};
    this.imagesLoaded = 0;

    this.loadImages();
}

Runner.prototype = {
    loadImages: function() {
        this.spriteDef = spriteDefinition;
        this.init();
    },
    loadSounds: function() {
        this.audioContext = new AudioContext();
    },
    setSpeed: function(opt_speed) {
        if (opt_speed) this.currentSpeed = opt_speed;
    },
    init: function() {
        this.setSpeed();
        this.canvas = c;
        this.ctx = ctx;
        this.ctx.fillStyle = '#f7f7f7';
        this.ctx.fill();

        this.horizon = new Horizon(this.canvas, this.spriteDef, this.dimensions,
            this.config.GAP_COEFFICIENT);

        this.distanceMeter = new DistanceMeter(this.canvas, this.spriteDef.TEXT_SPRITE, this.dimensions.WIDTH);

        this.tRex = new Trex(this.canvas, this.spriteDef.TREX);

        this.startListening();
        this.update();
    },
    //开场动画
    playIntro: function() {
        if (!this.started && !this.crashed) {
            this.playingIntro = true;
            this.tRex.playingIntro = true;

            var keyframes = '@-webkit-keyframes intro { ' +
                'from { width:' + Trex.config.WIDTH + 'px }' +
                'to { width: ' + this.dimensions.WIDTH + 'px }' +
                '}';
            document.styleSheets[0].insertRule(keyframes, 0);
            this.containerEl = document.getElementById('runner-container');
            this.containerEl.addEventListener('webkitAnimationEnd',
                this.startGame.bind(this));

            this.containerEl.style.webkitAnimation = 'intro .4s ease-out 1 both';
            this.containerEl.style.width = this.dimensions.WIDTH + 'px';

            this.activated = true;
            this.started = true;
        } else if (this.crashed) {
            this.restart();
        }
    },
    startGame: function() {
        this.runningTime = 0;
        this.playingIntro = false;
        this.tRex.playingIntro = false;
        this.containerEl.style.webkitAnimation = '';
        this.playCount++;

        document.addEventListener('visibilitychange', this.onVisibilityChange.bind(this));
        window.addEventListener('blur', this.onVisibilityChange.bind(this));
        window.addEventListener('focus', this.onVisibilityChange.bind(this));
    },
    clearCanvas: function() {
        this.ctx.clearRect(0, 0, this.dimensions.WIDTH, this.dimensions.HEIGHT);
    },
    //todo
    update: function() {
        this.drawPending = false;

        var now = getTimeStamp();
        var deltaTime = now - (this.time || now);
        this.time = now;

        if (this.activated) {

            this.clearCanvas();

            if (this.tRex.jumping) {
                this.tRex.updateJump(deltaTime);
            }

            this.runningTime += deltaTime;
            var hasObstacles = this.runningTime > this.config.CLEAR_TIME;
            //如果是第一次跳跃并且没有播放开场动画,则播放开场动画
            if (this.tRex.jumpCount == 1 && !this.playingIntro) {
                this.playIntro();
            }

            if (this.playingIntro) {
                this.horizon.update(0, this.currentSpeed, hasObstacles);
            } else {
                deltaTime = !this.started ? 0 : deltaTime;
                this.horizon.update(deltaTime, this.currentSpeed, hasObstacles,
                    this.inverted);
            }
            var collision = hasObstacles &&
                checkForCollision(this.horizon.obstacles[0], this.tRex);

            if (!collision) {
                this.distanceRan += this.currentSpeed * deltaTime / this.msPerFrame;

                if (this.currentSpeed < this.config.MAX_SPEED) {
                    this.currentSpeed += this.config.ACCELERATION;
                }
            } else {
                this.gameOver();
            }

            var playAchievementSound = this.distanceMeter.update(deltaTime,
                Math.ceil(this.distanceRan));

            if (playAchievementSound) {
                this.playSound(this.soundFx.SCORE);
            }
            //若夜晚持续时间大于设定值则变为白天
            if (this.invertTimer > this.config.INVERT_FADE_DURATION) {
                this.invertTimer = 0;
                this.invertTrigger = false;
                this.invert();
            } else if (this.invertTimer) {
                this.invertTimer += deltaTime;
            } else {
                var actualDistance =
                    this.distanceMeter.getActualDistance(Math.ceil(this.distanceRan));

                if (actualDistance > 0) {
                    this.invertTrigger = !(actualDistance %
                        this.config.INVERT_DISTANCE);

                    if (this.invertTrigger && this.invertTimer === 0) {
                        this.invertTimer += deltaTime;
                        this.invert();
                    }
                }
            }
        }
        if (!this.crashed) {
            this.tRex.update(deltaTime);
            this.raq();
        }
    },
    handleEvent: function(e) {
        return (function(evtType, events) {
            switch (evtType) {
                case events.KEYDOWN:
                case events.TOUCHSTART:
                case events.MOUSEDOWN:
                    this.onKeyDown(e);
                    break;
                case events.KEYUP:
                case events.TOUCHEND:
                case events.MOUSEUP:
                    this.onKeyUp(e);
                    break;
            }
        }.bind(this))(e.type, Runner.events);
    },
    startListening: function() {
        document.addEventListener(Runner.events.KEYDOWN, this);
        document.addEventListener(Runner.events.KEYUP, this);
        document.addEventListener(Runner.events.MOUSEDOWN, this);
        document.addEventListener(Runner.events.MOUSEUP, this);
    },
    stopListening: function() {
        document.removeEventListener(Runner.events.KEYDOWN, this);
        document.removeEventListener(Runner.events.KEYUP, this);
        document.removeEventListener(Runner.events.MOUSEDOWN, this);
        document.removeEventListener(Runner.events.MOUSEUP, this);
    },
    onKeyDown: function(e) {

        if (e.target != this.detailsButton) {
            if (!this.crashed && Runner.keycodes.JUMP[e.keyCode]) {
                e.preventDefault();
                if (!this.activated) {
                    this.loadSounds();
                    this.activated = true;
                }
                if (!this.tRex.jumping && !this.tRex.ducking) {
                    this.tRex.startJump(this.currentSpeed);
                }
            }
        }
        if (this.activated && !this.crashed && Runner.keycodes.DUCK[e.keyCode]) {

            e.preventDefault();
            if (this.tRex.jumping) {
                // Speed drop, activated only when jump key is not pressed.
                this.tRex.setSpeedDrop();
            } else if (!this.tRex.jumping && !this.tRex.ducking) {
                // Duck.
                this.tRex.setDuck(true);
            }
        }
    },
    onKeyUp: function(e) {
        var keyCode = String(e.keyCode);
        var isjumpKey = Runner.keycodes.JUMP[keyCode] ||
            e.type == Runner.events.TOUCHEND ||
            e.type == Runner.events.MOUSEDOWN;

        if (this.isRunning() && isjumpKey) {
            e.preventDefault();
            this.tRex.endJump();
        } else if (Runner.keycodes.DUCK[keyCode]) {
            e.preventDefault();
            this.tRex.speedDrop = false;
            this.tRex.setDuck(false);
        } else if (this.crashed) {
            e.preventDefault();
            // Check that enough time has elapsed before allowing jump key to restart.
            var deltaTime = getTimeStamp() - this.time;

            if (Runner.keycodes.RESTART[keyCode] || this.isLeftClickOnCanvas(e) ||
                (deltaTime >= this.config.GAMEOVER_CLEAR_TIME &&
                    Runner.keycodes.JUMP[keyCode])) {
                e.preventDefault();
                this.restart();
            }
        } else if (this.paused && isjumpKey) {
            // Reset the jump state
            e.preventDefault();
            this.tRex.reset();
            this.play();
        }
    },
    isLeftClickOnCanvas: function(e) {
        return e.button != null && e.button < 2 &&
            e.type == Runner.events.MOUSEUP && e.target == this.canvas;
    },
    raq: function() {
        if (!this.drawPending) {
            this.drawPending = true;
            this.raqId = requestAnimationFrame(this.update.bind(this));
        }
    },
    isRunning: function() {
        return !!this.raqId;
    },
    gameOver: function() {
        //this.playSound(this.soundFx.HIT);
        //vibrate(200);

        this.stop();
        this.crashed = true;
        this.distanceMeter.acheivement = false;

        this.tRex.update(100, Trex.status.CRASHED);

        // Game over panel.
        if (!this.gameOverPanel) {
            this.gameOverPanel = new GameOverPanel(this.canvas,
                this.spriteDef.TEXT_SPRITE, this.spriteDef.RESTART,
                this.dimensions);
        } else {
            this.gameOverPanel.draw();
        }

        // Update the high score.
        if (this.distanceRan > this.highestScore) {
            this.highestScore = Math.ceil(this.distanceRan);
            this.distanceMeter.setHighScore(this.highestScore);
        }

        // Reset the time clock.
        this.time = getTimeStamp();
    },
    stop: function() {
        this.activated = false;
        this.paused = true;
        cancelAnimationFrame(this.raqId);
        this.raqId = 0;
    },
    play: function() {
        if (!this.crashed) {
            this.activated = true;
            this.paused = false;
            this.tRex.update(0, Trex.status.RUNNING);
            this.time = getTimeStamp();
            this.update();
        }
    },
    restart: function() {
        if (!this.raqId) {
            this.playCount++;
            this.runningTime = 0;
            this.activated = true;
            this.crashed = false;
            this.distanceRan = 0;
            this.setSpeed(this.config.SPEED);
            this.time = getTimeStamp();
            this.containerEl.classList.remove(Runner.classes.CRASHED);
            this.clearCanvas();
            this.distanceMeter.reset(this.highestScore);
            this.horizon.reset();
            this.tRex.reset();
            //this.playSound(this.soundFx.BUTTON_PRESS);
            this.invert(true);
            this.update();
        }
    },
    onVisibilityChange: function(e) {
        if (document.hidden || document.webkitHidden || e.type == 'blur' ||
            document.visibilityState != 'visible') {
            this.stop();
        } else if (!this.crashed) {
            this.tRex.reset();
            this.play();
        }
    },
    playSound: function(soundBuffer) {
        if (soundBuffer) {
            var sourceNode = this.audioContext.createBufferSource();
            sourceNode.buffer = soundBuffer;
            sourceNode.connect(this.audioContext.destination);
            sourceNode.start(0);
        }
    },
    invert: function(reset) {
        if (reset) {
            a.classList.toggle(Runner.classes.INVERTED, this.invertTrigger);
            //document.body.classList.toggle(Runner.classes.INVERTED,false);
            this.invertTimer = 0;
            this.inverted = false;
        } else {
            this.inverted = a.classList.toggle(Runner.classes.INVERTED, this.invertTrigger);

            //this.inverted = document.body.classList.toggle(Runner.classes.INVERTED,this.invertTrigger);
        }
    }
};

window['Runner'] = Runner;

function decodeBase64ToArrayBuffer(base64String) {
    var len = (base64String.length / 4) * 3;
    var str = atob(base64String);
    var arrayBuffer = new ArrayBuffer(len);
    var bytes = new Uint8Array(arrayBuffer);

    for (var i = 0; i < len; i++) {
        bytes[i] = str.charCodeAt(i);
    }
    return bytes.buffer;
}

GameOverPanel.dimensions = {
    TEXT_X: 0,
    TEXT_Y: 13,
    TEXT_WIDTH: 191,
    TEXT_HEIGHT: 11,
    RESTART_WIDTH: 36,
    RESTART_HEIGHT: 32
};

function GameOverPanel(canvas, textImgPos, restartImgPos, dimensions) {
    this.canvas = canvas;
    this.ctx = canvas.getContext('2d');
    this.canvasDimensions = dimensions;
    this.textImgPos = textImgPos;
    this.restartImgPos = restartImgPos;
    this.draw();
}


GameOverPanel.prototype = {
    /**
     * Update the panel dimensions.
     * @param {number} width New canvas width.
     * @param {number} opt_height Optional new canvas height.
     */
    updateDimensions: function(width, opt_height) {
        this.canvasDimensions.WIDTH = width;
        if (opt_height) {
            this.canvasDimensions.HEIGHT = opt_height;
        }
    },

    /**
     * Draw the panel.
     */
    draw: function() {
        var dimensions = GameOverPanel.dimensions;

        var centerX = this.canvasDimensions.WIDTH / 2;

        // Game over text.
        var textSourceX = dimensions.TEXT_X;
        var textSourceY = dimensions.TEXT_Y;
        var textSourceWidth = dimensions.TEXT_WIDTH;
        var textSourceHeight = dimensions.TEXT_HEIGHT;

        var textTargetX = Math.round(centerX - (dimensions.TEXT_WIDTH / 2));
        var textTargetY = Math.round((this.canvasDimensions.HEIGHT - 25) / 3);
        var textTargetWidth = dimensions.TEXT_WIDTH;
        var textTargetHeight = dimensions.TEXT_HEIGHT;

        var restartSourceWidth = dimensions.RESTART_WIDTH;
        var restartSourceHeight = dimensions.RESTART_HEIGHT;
        var restartTargetX = centerX - (dimensions.RESTART_WIDTH / 2);
        var restartTargetY = this.canvasDimensions.HEIGHT / 2;


        textSourceX += this.textImgPos.x;
        textSourceY += this.textImgPos.y;

        // Game over text from sprite.
        this.ctx.drawImage(imgSprite,
            textSourceX, textSourceY, textSourceWidth, textSourceHeight,
            textTargetX, textTargetY, textTargetWidth, textTargetHeight);

        // Restart button.
        this.ctx.drawImage(imgSprite,
            this.restartImgPos.x, this.restartImgPos.y,
            restartSourceWidth, restartSourceHeight,
            restartTargetX, restartTargetY, dimensions.RESTART_WIDTH,
            dimensions.RESTART_HEIGHT);
    }
};


//地面
function HorizonLine(canvas, spritePos) {
    this.spritePos = spritePos;
    this.canvas = canvas;
    this.ctx = canvas.getContext("2d");
    this.sourceDimensions = {};
    this.dimensions = HorizonLine.dimensions;
    this.sourceXPos = [this.spritePos.x, this.spritePos.x + this.dimensions.WIDTH];
    this.xPos = [];
    this.yPos = 0;
    this.bumpThreshold = 0.5; //地形系数

    this.setSourceDimesions();
    this.draw();
}

HorizonLine.dimensions = {
    WIDTH: 600, //宽600
    HEIGHT: 12, //高12像素
    YPOS: 127 //在画布中的位置
};

HorizonLine.prototype = {
    setSourceDimesions: function() {
        for (var dimension in HorizonLine.dimensions) {
            this.sourceDimensions[dimension] = HorizonLine.dimensions[dimension];
            this.dimensions[dimension] = HorizonLine.dimensions[dimension];
        }
        //地面在画布上的位置
        this.xPos = [0, HorizonLine.dimensions.WIDTH]; //0,600
        this.yPos = HorizonLine.dimensions.YPOS;
    },
    //随机地形
    getRandomType: function() {
        return Math.random() > this.bumpThreshold ? this.dimensions.WIDTH : 0;
    },
    draw: function() {
        this.ctx.drawImage(imgSprite,
            this.sourceXPos[0], this.spritePos.y,
            this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT,
            this.xPos[0], this.yPos,
            this.dimensions.WIDTH, this.dimensions.HEIGHT);

        this.ctx.drawImage(imgSprite,
            this.sourceXPos[1], this.spritePos.y,
            this.sourceDimensions.WIDTH, this.sourceDimensions.HEIGHT,
            this.xPos[1], this.yPos,
            this.dimensions.WIDTH, this.dimensions.HEIGHT);

    },
    updateXPos: function(pos, increment) {
        var line1 = pos,
            line2 = pos === 0 ? 1 : 0;

        this.xPos[line1] -= increment;
        this.xPos[line2] = this.xPos[line1] + this.dimensions.WIDTH;

        if (this.xPos[line1] <= -this.dimensions.WIDTH) {
            this.xPos[line1] += this.dimensions.WIDTH * 2;
            this.xPos[line2] = this.xPos[line1] - this.dimensions.WIDTH;
            this.sourceXPos[line1] = this.getRandomType() + this.spritePos.x;
        }
    },
    update: function(deltaTime, speed) {
        var increment = Math.floor(speed * (FPS / 1000) * deltaTime);
        if (this.xPos[0] <= 0) {
            this.updateXPos(0, increment);
        } else {
            this.updateXPos(1, increment);
        }
        this.draw();
    },
    reset: function() {
        this.xPos[0] = 0;
        this.xPos[1] = HorizonLine.dimensions.WIDTH;
    }
};


//云
Cloud.config = {
    HEIGHT: 14,
    MAX_CLOUD_GAP: 400,
    MAX_SKY_LEVEL: 30,
    MIN_CLOUD_GAP: 100,
    MIN_SKY_LEVEL: 71,
    WIDTH: 46
};

function getRandomNum(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}

function getTimeStamp() {
    return performance.now();
}

function Cloud(canvas, spritePos, containerWidth) {
    this.canvas = canvas;
    this.ctx = canvas.getContext("2d");
    this.spritePos = spritePos;
    this.containerWidth = containerWidth;
    this.xPos = containerWidth;
    this.yPos = 0;
    this.remove = false;
    //云间隙
    this.cloudGap = getRandomNum(Cloud.config.MIN_CLOUD_GAP, Cloud.config.MAX_CLOUD_GAP);
    this.init();
}

Cloud.prototype = {
    init: function() {
        //云的高度随机
        this.yPos = getRandomNum(Cloud.config.MAX_SKY_LEVEL, Cloud.config.MIN_SKY_LEVEL);
        this.draw();
    },
    draw: function() {
        this.ctx.save();
        var sourceWidth = Cloud.config.WIDTH,
            sourceHeight = Cloud.config.HEIGHT;

        this.ctx.drawImage(imgSprite,
            this.spritePos.x, this.spritePos.y,
            sourceWidth, sourceHeight,
            this.xPos, this.yPos,
            sourceWidth, sourceHeight);

        this.ctx.restore();
    },
    update: function(speed) {
        if (!this.remove) {
            //向左移动
            this.xPos -= Math.ceil(speed);
            this.draw();

            if (!this.isVisible()) {
                this.remove = true;
            }
        }
    },
    //判断是否移出屏幕外
    isVisible: function() {
        return this.xPos + Cloud.config.WIDTH > 0;
    }
};

//夜晚
//todo
NightMode.config = {
    FADE_SPEED: 0.035, //淡入淡出速度
    HEIGHT: 40, //月亮高度
    MOON_SPEED: 0.25, //月亮移动速度
    NUM_STARS: 2, //星星数量
    STAR_SIZE: 9, //星星宽度
    STAR_SPEED: 0.3, //星星速度
    STAR_MAX_Y: 70, //星星在画布上出现的位置
    WIDTH: 20 //半个月度宽度
};
NightMode.phases = [140, 120, 100, 60, 40, 20, 0];

function NightMode(canvas, spritePos, containerWidth) {
    this.spritePos = spritePos;
    this.canvas = canvas;
    this.ctx = canvas.getContext("2d");
    this.containerWidth = containerWidth;
    this.xPos = containerWidth - 50; //月亮的x坐标
    this.yPos = 30; //月亮的y坐标
    this.currentPhase = 0;
    this.opacity = 0;
    this.stars = []; //用于存储星星
    this.drawStars = false;
    this.placeStars(); //放置星星
}

NightMode.prototype = {
    update: function(activated) {
        if (activated && this.opacity == 0) {
            this.currentPhase++;
            if (this.currentPhase >= NightMode.phases.length) {
                this.currentPhase = 0;
            }
        }

        //淡入淡出
        if (activated && (this.opacity < 1 || this.opacity == 0)) {
            this.opacity += NightMode.config.FADE_SPEED;
        } else if (this.opacity > 0) {
            this.opacity -= NightMode.config.FADE_SPEED;
        }

        //移动月亮
        if (this.opacity > 0) {
            this.xPos = this.updateXPos(this.xPos, NightMode.config.MOON_SPEED);

            //移动星星
            if (this.drawStars) {
                for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
                    this.stars[i].x = this.updateXPos(this.stars[i].x, NightMode.config.STAR_SPEED);
                }
            }
            this.draw();
        } else {
            this.opacity = 0;
            this.placeStars();
        }
        this.drawStars = true;
    },
    updateXPos: function(currentPos, speed) {
        if (currentPos < -NightMode.config.WIDTH) {
            currentPos = this.containerWidth;
        } else {
            currentPos -= speed;
        }
        return currentPos;
    },
    draw: function() {
        var moonSourceWidth = this.currentPhase == 3 ? NightMode.config.WIDTH * 2 :
            NightMode.config.WIDTH;
        var moonSourceHeight = NightMode.config.HEIGHT;
        var moonSourceX = this.spritePos.x + NightMode.phases[this.currentPhase];
        var moonOutputWidth = moonSourceWidth;
        var starSize = NightMode.config.STAR_SIZE;
        var starSourceX = spriteDefinition.STAR.x;

        this.ctx.save();
        this.ctx.globalAlpha = this.opacity;

        if (this.drawStars) {
            for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
                this.ctx.drawImage(imgSprite,
                    starSourceX, this.stars[i].sourceY,
                    starSize, starSize,
                    Math.round(this.stars[i].x), this.stars[i].y,
                    NightMode.config.STAR_SIZE, NightMode.config.STAR_SIZE);
            }
        }

        this.ctx.drawImage(imgSprite,
            moonSourceX, this.spritePos.y,
            moonSourceWidth, moonSourceHeight,
            Math.round(this.xPos), this.yPos,
            moonOutputWidth, NightMode.config.HEIGHT);

        this.ctx.globalAlpha = 1;
        this.ctx.restore();
    },
    placeStars: function() {
        var segmentSize = Math.round(this.containerWidth / NightMode.config.NUM_STARS);
        for (var i = 0; i < NightMode.config.NUM_STARS; i++) {
            this.stars[i] = {};
            this.stars[i].x = getRandomNum(segmentSize * i, segmentSize * (i + 1));
            this.stars[i].y = getRandomNum(0, NightMode.config.STAR_MAX_Y);
            this.stars[i].sourceY = spriteDefinition.STAR.y + NightMode.config.STAR_SIZE * i;
        }
    },
    r.........完整代码请登录后点击上方下载按钮下载查看

网友评论0