以下是 HTM5 Canvas实现3D飞机飞行动画特效代码 的示例演示效果:
部分效果截图:
HTML代码(index.html):
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>HTM5 Canvas实现3D飞机飞行动画特效</title>
<!--<link href='http://fonts.useso.com/css?family=Playfair+Display:400,700,700italic' rel='stylesheet' type='text/css'>-->
<link rel="stylesheet" type="text/css" href="css/demo.css" />
<!--必要样式-->
<link rel="stylesheet" type="text/css" href="css/game.css" />
<script type="text/javascript" src="js/TweenMax.min.js"></script>
<script type="text/javascript" src="js/three.min.js"></script>
<script type="text/javascript" src="js/game.js" /></script>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div class="game-holder" id="gameHolder">
<div class="header">
<h1><span>the</span>Aviator</h1>
<h2>fly it to the end</h2>
<div class="score" id="score">
<div class="score__content" id="level">
<div class="score__label">level</div>
<div class="score__value score__value--level" id="levelValue">1</div>
<svg class="level-circle" id="levelCircle" viewbox="0 0 200 200">
<circle id="levelCircleBgr" r="80" cx="100" cy="100" fill="none" stroke="#d1b790" stroke-width="24px" />
<circle id="levelCircleStroke" r="80" cx="100" cy="100" fill="none" #f25346 stroke="#68c3c0" stroke-width="14px" stroke-dasharray="502" />
</svg>
</div>
<div class="score__content" id="dist">
<div class="score__label">distance</div>
<div class="score__value score__value--dist" id="distValue">000</div>
</div>
<div class="score__content" id="energy">
<div class="score__label">energy</div>
<div class="score__value score__value--energy" id="energyValue">
<div class="energy-bar" id="energyBar"></div>
</div>
</div>
</div>
</div>
<div class="world" id="world"></div>
<div class="message message--replay" id="replayMessage">Click to Replay</div>
<div class="message message--instructions" id="instructions">Grab the blue pills<span>avoid the red ones</span></div>
</div>
<!-- 主要代码部分 -->
<!-- Demo links -->
<nav class="meta">
<a class="demo-link" href="part1.html">Part 1</a>
<a class="demo-link" href="part2.html">Part 2</a>
</nav>
</body>
</html>
JS代码(game.js):
//COLORSvar Colors ={
red:0xf25346,white:0xd8d0d1,brown:0x59332e,brownDark:0x23190f,pink:0xF5986E,yellow:0xf4ce93,blue:0x68c3c0,}
;
///////////////// GAME VARIABLESvar game;
var deltaTime = 0;
var newTime = new Date().getTime();
var oldTime = new Date().getTime();
var ennemiesPool = [];
var particlesPool = [];
var particlesInUse = [];
function resetGame(){
game ={
speed:0,initSpeed:.00035,baseSpeed:.00035,targetBaseSpeed:.00035,incrementSpeedByTime:.0000025,incrementSpeedByLevel:.000005,distanceForSpeedUpdate:100,speedLastUpdate:0,distance:0,ratioSpeedDistance:50,energy:100,ratioSpeedEnergy:3,level:1,levelLastUpdate:0,distanceForLevelUpdate:1000,planeDefaultHeight:100,planeAmpHeight:80,planeAmpWidth:75,planeMoveSensivity:0.005,planeRotXSensivity:0.0008,planeRotZSensivity:0.0004,planeFallSpeed:.001,planeMinSpeed:1.2,planeMaxSpeed:1.6,planeSpeed:0,planeCollisionDisplacementX:0,planeCollisionSpeedX:0,planeCollisionDisplacementY:0,planeCollisionSpeedY:0,seaRadius:600,seaLength:800,//seaRotationSpeed:0.006,wavesMinAmp:5,wavesMaxAmp:20,wavesMinSpeed:0.001,wavesMaxSpeed:0.003,cameraFarPos:500,cameraNearPos:150,cameraSensivity:0.002,coinDistanceTolerance:15,coinValue:3,coinsSpeed:.5,coinLastSpawn:0,distanceForCoinsSpawn:100,ennemyDistanceTolerance:10,ennemyValue:10,ennemiesSpeed:.6,ennemyLastSpawn:0,distanceForEnnemiesSpawn:50,status:"playing",}
;
fieldLevel.innerHTML = Math.floor(game.level);
}
//THREEJS RELATED VARIABLESvar scene,camera,fieldOfView,aspectRatio,nearPlane,farPlane,renderer,container,controls;
//SCREEN & MOUSE VARIABLESvar HEIGHT,WIDTH,mousePos ={
x:0,y:0}
;
//INIT THREE JS,SCREEN AND MOUSE EVENTSfunction createScene(){
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
scene = new THREE.Scene();
aspectRatio = WIDTH / HEIGHT;
fieldOfView = 50;
nearPlane = .1;
farPlane = 10000;
camera = new THREE.PerspectiveCamera( fieldOfView,aspectRatio,nearPlane,farPlane );
scene.fog = new THREE.Fog(0xf7d9aa,100,950);
camera.position.x = 0;
camera.position.z = 200;
camera.position.y = game.planeDefaultHeight;
//camera.lookAt(new THREE.Vector3(0,400,0));
renderer = new THREE.WebGLRenderer({
alpha:true,antialias:true}
);
renderer.setSize(WIDTH,HEIGHT);
renderer.shadowMap.enabled = true;
container = document.getElementById('world');
container.appendChild(renderer.domElement);
window.addEventListener('resize',handleWindowResize,false);
/* controls = new THREE.OrbitControls(camera,renderer.domElement);
controls.minPolarAngle = -Math.PI / 2;
controls.maxPolarAngle = Math.PI;
//controls.noZoom = true;
//controls.noPan = true;
//*/
}
// MOUSE AND SCREEN EVENTSfunction handleWindowResize(){
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH,HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
function handleMouseMove(event){
var tx = -1 + (event.clientX / WIDTH)*2;
var ty = 1 - (event.clientY / HEIGHT)*2;
mousePos ={
x:tx,y:ty}
;
}
function handleTouchMove(event){
event.preventDefault();
var tx = -1 + (event.touches[0].pageX / WIDTH)*2;
var ty = 1 - (event.touches[0].pageY / HEIGHT)*2;
mousePos ={
x:tx,y:ty}
;
}
function handleMouseUp(event){
if (game.status == "waitingReplay"){
resetGame();
hideReplay();
}
}
function handleTouchEnd(event){
if (game.status == "waitingReplay"){
resetGame();
hideReplay();
}
}
// LIGHTSvar ambientLight,hemisphereLight,shadowLight;
function createLights(){
hemisphereLight = new THREE.HemisphereLight(0xaaaaaa,0x000000,.9) ambientLight = new THREE.AmbientLight(0xdc8874,.5);
shadowLight = new THREE.DirectionalLight(0xffffff,.9);
shadowLight.position.set(150,350,350);
shadowLight.castShadow = true;
shadowLight.shadow.camera.left = -400;
shadowLight.shadow.camera.right = 400;
shadowLight.shadow.camera.top = 400;
shadowLight.shadow.camera.bottom = -400;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 1000;
shadowLight.shadow.mapSize.width = 4096;
shadowLight.shadow.mapSize.height = 4096;
var ch = new THREE.CameraHelper(shadowLight.shadow.camera);
//scene.add(ch);
scene.add(hemisphereLight);
scene.add(shadowLight);
scene.add(ambientLight);
}
var Pilot = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "pilot";
this.angleHairs=0;
var bodyGeom = new THREE.BoxGeometry(15,15,15);
var bodyMat = new THREE.MeshPhongMaterial({
color:Colors.brown,shading:THREE.FlatShading}
);
var body = new THREE.Mesh(bodyGeom,bodyMat);
body.position.set(2,-12,0);
this.mesh.add(body);
var faceGeom = new THREE.BoxGeometry(10,10,10);
var faceMat = new THREE.MeshLambertMaterial({
color:Colors.pink}
);
var face = new THREE.Mesh(faceGeom,faceMat);
this.mesh.add(face);
var hairGeom = new THREE.BoxGeometry(4,4,4);
var hairMat = new THREE.MeshLambertMaterial({
color:Colors.brown}
);
var hair = new THREE.Mesh(hairGeom,hairMat);
hair.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0,2,0));
var hairs = new THREE.Object3D();
this.hairsTop = new THREE.Object3D();
for (var i=0;
i<12;
i++){
var h = hair.clone();
var col = i%3;
var row = Math.floor(i/3);
var startPosZ = -4;
var startPosX = -4;
h.position.set(startPosX + row*4,0,startPosZ + col*4);
h.geometry.applyMatrix(new THREE.Matrix4().makeScale(1,1,1));
this.hairsTop.add(h);
}
hairs.add(this.hairsTop);
var hairSideGeom = new THREE.BoxGeometry(12,4,2);
hairSideGeom.applyMatrix(new THREE.Matrix4().makeTranslation(-6,0,0));
var hairSideR = new THREE.Mesh(hairSideGeom,hairMat);
var hairSideL = hairSideR.clone();
hairSideR.position.set(8,-2,6);
hairSideL.position.set(8,-2,-6);
hairs.add(hairSideR);
hairs.add(hairSideL);
var hairBackGeom = new THREE.BoxGeometry(2,8,10);
var hairBack = new THREE.Mesh(hairBackGeom,hairMat);
hairBack.position.set(-1,-4,0) hairs.add(hairBack);
hairs.position.set(-5,5,0);
this.mesh.add(hairs);
var glassGeom = new THREE.BoxGeometry(5,5,5);
var glassMat = new THREE.MeshLambertMaterial({
color:Colors.brown}
);
var glassR = new THREE.Mesh(glassGeom,glassMat);
glassR.position.set(6,0,3);
var glassL = glassR.clone();
glassL.position.z = -glassR.position.z var glassAGeom = new THREE.BoxGeometry(11,1,11);
var glassA = new THREE.Mesh(glassAGeom,glassMat);
this.mesh.add(glassR);
this.mesh.add(glassL);
this.mesh.add(glassA);
var earGeom = new THREE.BoxGeometry(2,3,2);
var earL = new THREE.Mesh(earGeom,faceMat);
earL.position.set(0,0,-6);
var earR = earL.clone();
earR.position.set(0,0,6);
this.mesh.add(earL);
this.mesh.add(earR);
}
Pilot.prototype.updateHairs = function(){
//* var hairs = this.hairsTop.children;
var l = hairs.length;
for (var i=0;
i<l;
i++){
var h = hairs[i];
h.scale.y = .75 + Math.cos(this.angleHairs+i/3)*.25;
}
this.angleHairs += game.speed*deltaTime*40;
//*/
}
var AirPlane = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "airPlane";
// Cabin var geomCabin = new THREE.BoxGeometry(80,50,50,1,1,1);
var matCabin = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
geomCabin.vertices[4].y-=10;
geomCabin.vertices[4].z+=20;
geomCabin.vertices[5].y-=10;
geomCabin.vertices[5].z-=20;
geomCabin.vertices[6].y+=30;
geomCabin.vertices[6].z+=20;
geomCabin.vertices[7].y+=30;
geomCabin.vertices[7].z-=20;
var cabin = new THREE.Mesh(geomCabin,matCabin);
cabin.castShadow = true;
cabin.receiveShadow = true;
this.mesh.add(cabin);
// Engine var geomEngine = new THREE.BoxGeometry(20,50,50,1,1,1);
var matEngine = new THREE.MeshPhongMaterial({
color:Colors.white,shading:THREE.FlatShading}
);
var engine = new THREE.Mesh(geomEngine,matEngine);
engine.position.x = 50;
engine.castShadow = true;
engine.receiveShadow = true;
this.mesh.add(engine);
// Tail Plane var geomTailPlane = new THREE.BoxGeometry(15,20,5,1,1,1);
var matTailPlane = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var tailPlane = new THREE.Mesh(geomTailPlane,matTailPlane);
tailPlane.position.set(-40,20,0);
tailPlane.castShadow = true;
tailPlane.receiveShadow = true;
this.mesh.add(tailPlane);
// Wings var geomSideWing = new THREE.BoxGeometry(30,5,120,1,1,1);
var matSideWing = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var sideWing = new THREE.Mesh(geomSideWing,matSideWing);
sideWing.position.set(0,15,0);
sideWing.castShadow = true;
sideWing.receiveShadow = true;
this.mesh.add(sideWing);
var geomWindshield = new THREE.BoxGeometry(3,15,20,1,1,1);
var matWindshield = new THREE.MeshPhongMaterial({
color:Colors.white,transparent:true,opacity:.3,shading:THREE.FlatShading}
);
;
var windshield = new THREE.Mesh(geomWindshield,matWindshield);
windshield.position.set(5,27,0);
windshield.castShadow = true;
windshield.receiveShadow = true;
this.mesh.add(windshield);
var geomPropeller = new THREE.BoxGeometry(20,10,10,1,1,1);
geomPropeller.vertices[4].y-=5;
geomPropeller.vertices[4].z+=5;
geomPropeller.vertices[5].y-=5;
geomPropeller.vertices[5].z-=5;
geomPropeller.vertices[6].y+=5;
geomPropeller.vertices[6].z+=5;
geomPropeller.vertices[7].y+=5;
geomPropeller.vertices[7].z-=5;
var matPropeller = new THREE.MeshPhongMaterial({
color:Colors.brown,shading:THREE.FlatShading}
);
this.propeller = new THREE.Mesh(geomPropeller,matPropeller);
this.propeller.castShadow = true;
this.propeller.receiveShadow = true;
var geomBlade = new THREE.BoxGeometry(1,80,10,1,1,1);
var matBlade = new THREE.MeshPhongMaterial({
color:Colors.brownDark,shading:THREE.FlatShading}
);
var blade1 = new THREE.Mesh(geomBlade,matBlade);
blade1.position.set(8,0,0);
blade1.castShadow = true;
blade1.receiveShadow = true;
var blade2 = blade1.clone();
blade2.rotation.x = Math.PI/2;
blade2.castShadow = true;
blade2.receiveShadow = true;
this.propeller.add(blade1);
this.propeller.add(blade2);
this.propeller.position.set(60,0,0);
this.mesh.add(this.propeller);
var wheelProtecGeom = new THREE.BoxGeometry(30,15,10,1,1,1);
var wheelProtecMat = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var wheelProtecR = new THREE.Mesh(wheelProtecGeom,wheelProtecMat);
wheelProtecR.position.set(25,-20,25);
this.mesh.add(wheelProtecR);
var wheelTireGeom = new THREE.BoxGeometry(24,24,4);
var wheelTireMat = new THREE.MeshPhongMaterial({
color:Colors.brownDark,shading:THREE.FlatShading}
);
var wheelTireR = new THREE.Mesh(wheelTireGeom,wheelTireMat);
wheelTireR.position.set(25,-28,25);
var wheelAxisGeom = new THREE.BoxGeometry(10,10,6);
var wheelAxisMat = new THREE.MeshPhongMaterial({
color:Colors.brown,shading:THREE.FlatShading}
);
var wheelAxis = new THREE.Mesh(wheelAxisGeom,wheelAxisMat);
wheelTireR.add(wheelAxis);
this.mesh.add(wheelTireR);
var wheelProtecL = wheelProtecR.clone();
wheelProtecL.position.z = -wheelProtecR.position.z;
this.mesh.add(wheelProtecL);
var wheelTireL = wheelTireR.clone();
wheelTireL.position.z = -wheelTireR.position.z;
this.mesh.add(wheelTireL);
var wheelTireB = wheelTireR.clone();
wheelTireB.scale.set(.5,.5,.5);
wheelTireB.position.set(-35,-5,0);
this.mesh.add(wheelTireB);
var suspensionGeom = new THREE.BoxGeometry(4,20,4);
suspensionGeom.applyMatrix(new THREE.Matrix4().makeTranslation(0,10,0)) var suspensionMat = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var suspension = new THREE.Mesh(suspensionGeom,suspensionMat);
suspension.position.set(-35,-5,0);
suspension.rotation.z = -.3;
this.mesh.add(suspension);
this.pilot = new Pilot();
this.pilot.mesh.position.set(-10,27,0);
this.mesh.add(this.pilot.mesh);
this.mesh.castShadow = true;
this.mesh.receiveShadow = true;
}
;
Sky = function(){
this.mesh = new THREE.Object3D();
this.nClouds = 20;
this.clouds = [];
var stepAngle = Math.PI*2 / this.nClouds;
for(var i=0;
i<this.nClouds;
i++){
var c = new Cloud();
this.clouds.push(c);
var a = stepAngle*i;
var h = game.seaRadius + 150 + Math.random()*200;
c.mesh.position.y = Math.sin(a)*h;
c.mesh.position.x = Math.cos(a)*h;
c.mesh.position.z = -300-Math.random()*500;
c.mesh.rotation.z = a + Math.PI/2;
var s = 1+Math.random()*2;
c.mesh.scale.set(s,s,s);
this.mesh.add(c.mesh);
}
}
Sky.prototype.moveClouds = function(){
for(var i=0;
i<this.nClouds;
i++){
var c = this.clouds[i];
c.rotate();
}
this.mesh.rotation.z += game.speed*deltaTime;
}
Sea = function(){
var geom = new THREE.CylinderGeometry(game.seaRadius,game.seaRadius,game.seaLength,40,10);
geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI/2));
geom.mergeVertices();
var l = geom.vertices.length;
this.waves = [];
for (var i=0;
i<l;
i++){
var v = geom.vertices[i];
//v.y = Math.random()*30;
this.waves.push({
y:v.y,x:v.x,z:v.z,ang:Math.random()*Math.PI*2,amp:game.wavesMinAmp + Math.random()*(game.wavesMaxAmp-game.wavesMinAmp),speed:game.wavesMinSpeed + Math.random()*(game.wavesMaxSpeed - game.wavesMinSpeed)}
);
}
;
var mat = new THREE.MeshPhongMaterial({
color:Colors.blue,transparent:true,opacity:.8,shading:THREE.FlatShading,}
);
this.mesh = new THREE.Mesh(geom,mat);
this.mesh.name = "waves";
this.mesh.receiveShadow = true;
}
Sea.prototype.moveWaves = function (){
var verts = this.mesh.geometry.vertices;
var l = verts.length;
for (var i=0;
i<l;
i++){
var v = verts[i];
var vprops = this.waves[i];
v.x = vprops.x + Math.cos(vprops.ang)*vprops.amp;
v.y = vprops.y + Math.sin(vprops.ang)*vprops.amp;
vprops.ang += vprops.speed*deltaTime;
this.mesh.geometry.verticesNeedUpdate=true;
}
}
Cloud = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "cloud";
var geom = new THREE.CubeGeometry(20,20,20);
var mat = new THREE.MeshPhongMaterial({
color:Colors.white,}
);
//* var nBlocs = 3+Math.floor(Math.random()*3);
for (var i=0;
i<nBlocs;
i++ ){
var m = new THREE.Mesh(geom.clone(),mat);
m.position.x = i*15;
m.position.y = Math.random()*10;
m.position.z = Math.random()*10;
m.rotation.z = Math.random()*Math.PI*2;
m.rotation.y = Math.random()*Math.PI*2;
var s = .1 + Math.random()*.9;
m.scale.set(s,s,s);
this.mesh.add(m);
m.castShadow = true;
m.receiveShadow = true;
}
//*/
}
Cloud.prototype.rotate = function(){
var l = this.mesh.children.length;
for(var i=0;
i<l;
i++){
var m = this.mesh.children[i];
m.rotation.z+= Math.random()*.005*(i+1);
m.rotation.y+= Math.random()*.002*(i+1);
}
}
Ennemy = function(){
var geom = new THREE.TetrahedronGeometry(8,2);
var mat = new THREE.MeshPhongMaterial({
color:Colors.red,shininess:0,specular:0xffffff,shading:THREE.FlatShading}
);
this.mesh = new THREE.Mesh(geom,mat);
this.mesh.castShadow = true;
this.angle = 0;
this.dist = 0;
}
EnnemiesHolder = function (){
this.mesh = new THREE.Object3D();
this.ennemiesInUse = [];
}
EnnemiesHolder.prototype.spawnEnnemies = function(){
var nEnnemies = game.level;
for (var i=0;
i<nEnnemies;
i++){
var ennemy;
if (ennemiesPool.length){
ennemy = ennemiesPool.pop();
}
else{
ennemy = new Ennemy();
}
ennemy.angle = - (i*0.1);
ennemy.distance = game.seaRadius + game.planeDefaultHeight + (-1 + Math.random() * 2) * (game.planeAmpHeight-20);
ennemy.mesh.position.y = -game.seaRadius + Math.sin(ennemy.angle)*ennemy.distance;
ennemy.mesh.position.x = Math.cos(ennemy.angle)*ennemy.distance;
this.mesh.add(ennemy.mesh);
this.ennemiesInUse.push(ennemy);
}
}
EnnemiesHolder.prototype.rotateEnnemies = function(){
for (var i=0;
i<this.ennemiesInUse.length;
i++){
var ennemy = this.ennemiesInUse[i];
ennemy.angle += game.speed*deltaTime*game.ennemiesSpeed;
if (ennemy.angle > Math.PI*2) ennemy.angle -= Math.PI*2;
ennemy.mesh.position.y = -game.seaRadius + Math.sin(ennemy.angle)*ennemy.distance;
ennemy.mesh.position.x = Math.cos(ennemy.angle)*ennemy.distance;
ennemy.mesh.rotation.z += Math.random()*.1;
ennemy.mesh.rotation.y += Math.random()*.1;
//var globalEnnemyPosition = ennemy.mesh.localToWorld(new THREE.Vector3());
var diffPos = airplane.mesh.position.clone().sub(ennemy.mesh.position.clone());
var d = diffPos.length();
if (d<game.ennemyDistanceTolerance){
particlesHolder.spawnParticles(ennemy.mesh.position.clone(),15,Colors.red,3);
ennemiesPool.unshift(this.ennemiesInUse.splice(i,1)[0]);
this.mesh.remove(ennemy.mesh);
game.planeCollisionSpeedX = 100 * diffPos.x / d;
game.planeCollisionSpeedY = 100 * diffPos.y / d;
ambientLight.intensity = 2;
removeEnergy();
i--;
}
else if (ennemy.angle > Math.PI){
ennemiesPool.unshift(this.ennemiesInUse.splice(i,1)[0]);
this.mesh.remove(ennemy.mesh);
i--;
}
}
}
Particle = function(){
var geom = new THREE.TetrahedronGeometry(3,0);
var mat = new THREE.MeshPhongMaterial({
color:0x009999,shininess:0,specular:0xffffff,shading:THREE.FlatShading}
);
this.mesh = new THREE.Mesh(geom,mat);
}
Particle.prototype.explode = function(pos,color,scale){
var _this = this;
var _p = this.mesh.parent;
this.mesh.material.color = new THREE.Color( color);
this.mesh.material.needsUpdate = true;
this.mesh.scale.set(scale,scale,scale);
var targetX = pos.x + (-1 + Math.random()*2)*50;
var targetY = pos.y + (-1 + Math.random()*2)*50;
var speed = .6+Math.random()*.2;
TweenMax.to(this.mesh.rotation,speed,{
x:Math.random()*12,y:Math.random()*12}
);
TweenMax.to(this.mesh.scale,speed,{
x:.1,y:.1,z:.1}
);
TweenMax.to(this.mesh.position,speed,{
x:targetX,y:targetY,delay:Math.random() *.1,ease:Power2.easeOut,onComplete:function(){
if(_p) _p.remove(_this.mesh);
_this.mesh.scale.set(1,1,1);
particlesPool.unshift(_this);
}
}
);
}
ParticlesHolder = function (){
this.mesh = new THREE.Object3D();
this.particlesInUse = [];
}
ParticlesHolder.prototype.spawnParticles = function(pos,density,color,scale){
var nPArticles = density;
for (var i=0;
i<nPArticles;
i++){
var particle;
if (particlesPool.length){
particle = particlesPool.pop();
}
else{
particle = new Particle();
}
this.mesh.add(particle.mesh);
particle.mesh.visible = true;
var _this = this;
particle.mesh.position.y = pos.y;
particle.mesh.position.x = pos.x;
particle.explode(pos,color,scale);
}
}
Coin = function(){
var geom = new THREE.TetrahedronGeometry(5,0);
var mat = new THREE.MeshPhongMaterial({
color:0x009999,shininess:0,specular:0xffffff,shading:THREE.FlatShading}
);
this.mesh = new THREE.Mesh(geom,mat);
this.mesh.castShadow = true;
this.angle = 0;
this.dist = 0;
}
CoinsHolder = function (nCoins){
this.mesh = new THREE.Object3D();
this.coinsInUse = [];
this.coinsPool = [];
for (var i=0;
i<nCoins;
i++){
var coin = new Coin();
this.coinsPool.push(coin);
}
}
CoinsHolder.prototype.spawnCoins = function(){
var nCoins = 1 + Math.floor(Math.random()*10);
var d = game.seaRadius + game.planeDefaultHeight + (-1 + Math.random() * 2) * (game.planeAmpHeight-20);
var amplitude = 10 + Math.round(Math.random()*10);
for (var i=0;
i<nCoins;
i++){
var coin;
if (this.coinsPool.length){
coin = this.coinsPool.pop();
}
else{
coin = new Coin();
}
this.mesh.add(coin.mesh);
this.coinsInUse.push(coin);
coin.angle = - (i*0.02);
coin.distance = d + Math.cos(i*.5)*amplitude;
coin.mesh.position.y = -game.seaRadius + Math.sin(coin.angle)*coin.distance;
coin.mesh.position.x = Math.cos(coin.angle)*coin.distance;
}
}
CoinsHolder.prototype.rotateCoins = function(){
for (var i=0;
i<this.coinsInUse.length;
i++){
var coin = this.coinsInUse[i];
if (coin.exploding) continue;
coin.angle += game.speed*deltaTime*game.coinsSpeed;
if (coin.angle>Math.PI*2) coin.angle -= Math.PI*2;
coin.mesh.position.y = -game.seaRadius + Math.sin(coin.angle)*coin.distance;
coin.mesh.position.x = Math.cos(coin.angle)*coin.distance;
coin.mesh.rotation.z += Math.random()*.1;
coin.mesh.rotation.y += Math.random()*.1;
//var globalCoinPosition = coin.mesh.localToWorld(new THREE.Vector3());
var diffPos = airplane.mesh.position.clone().sub(coin.mesh.position.clone());
var d = diffPos.length();
if (d<game.coinDistanceTolerance){
this.coinsPool.unshift(this.coinsInUse.splice(i,1)[0]);
this.mesh.remove(coin.mesh);
particlesHolder.spawnParticles(coin.mesh.position.clone(),5,0x009999,.8);
addEnergy();
i--;
}
else if (coin.angle > Math.PI){
this.coinsPool.unshift(this.coinsInUse.splice(i,1)[0]);
this.mesh.remove(coin.mesh);
i--;
}
}
}
// 3D Modelsvar sea;
var airplane;
function createPlane(){
airplane = new AirPlane();
airplane.mesh.scale.set(.25,.25,.25);
airplane.mesh.position.y = game.planeDefaultHeight;
scene.add(airplane.mesh);
}
function createSea(){
sea = new Sea();
sea.mesh.position.y = -game.seaRadius;
scene.add(sea.mesh);
}
function createSky(){
sky = new Sky();
sky.mesh.position.y = -game.seaRadius;
scene.add(sky.mesh);
}
function createCoins(){
coinsHolder = new CoinsHolder(20);
scene.add(coinsHolder.mesh)}
function createEnnemies(){
for (var i=0;
i<10;
i++){
var ennemy = new Ennemy();
ennemiesPool.push(ennemy);
}
ennemiesHolder = new EnnemiesHolder();
//ennemiesHolder.mesh.position.y = -game.seaRadius;
scene.add(ennemiesHolder.mesh)}
function createParticles(){
for (var i=0;
i<10;
i++){
var particle = new Particle();
particlesPool.push(particle);
}
particlesHolder = new ParticlesHolder();
//ennemiesHolder.mesh.position.y = -game.seaRadius;
scene.add(particlesHolder.mesh)}
function loop(){
newTime = new Date().getTime();
deltaTime = newTime-oldTime;
oldTime = newTime;
if (game.status=="playing"){
// Add energy coins every 100m;
if (Math.floor(game.distance)%game.distanceForCoinsSpawn == 0 && Math.floor(game.distance) > game.coinLastSpawn){
game.coinLastSpawn = Math.floor(game.distance);
coinsHolder.spawnCoins();
}
if (Math.floor(game.distance)%game.distanceForSpeedUpdate == 0 && Math.floor(game.distance) > game.speedLastUpdate){
game.speedLastUpdate = Math.floor(game.distance);
game.targetBaseSpeed += game.incrementSpeedByTime*deltaTime;
}
if (Math.floor(game.distance)%game.distanceForEnnemiesSpawn == 0 && Math.floor(game.distance) > game.ennemyLastSpawn){
game.ennemyLastSpawn = Math.floor(game.distance);
ennemiesHolder.spawnEnnemies();
}
if (Math.floor(game.distance)%game.distanceForLevelUpdate == 0 && Math.floor(game.distance) > game.levelLastUpdate){
game.levelLastUpdate = Math.floor(game.distance);
game.level++;
fieldLevel.innerHTML = Math.floor(game.level);
game.targetBaseSpeed = game.initSpeed + game.incrementSpeedByLevel*game.level}
updatePlane();
updateDistance();
updateEnergy();
game.baseSpeed += (game.targetBaseSpeed - game.baseSpeed) * deltaTime * 0.02;
game.speed = game.baseSpeed * game.planeSpeed;
}
else if(game.status=="gameover"){
game.speed *= .99;
airplane.mesh.rotation.z += (-Math.PI/2 - airplane.mesh.rotation.z)*.0002*deltaTime;
airplane.mesh.rotation.x += 0.0003*deltaTime;
game.planeFallSpeed *= 1.05;
airplane.mesh.position.y -= game.planeFallSpeed*deltaTime;
if (airplane.mesh.position.y <-200){
showReplay();
game.status = "waitingReplay";
}
}
else if (game.status=="waitingReplay"){
}
airplane.propeller.rotation.x +=.2 + game.planeSpeed * deltaTime*.005;
sea.mesh.rotation.z += game.speed*deltaTime;
//*game.seaRotationSpeed;
if ( sea.mesh.rotation.z > 2*Math.PI) sea.mesh.rotation.z -= 2*Math.PI;
ambientLight.intensity += (.5 - ambientLight.intensity)*deltaTime*0.005;
coinsHolder.rotateCoins();
ennemiesHolder.rotateEnnemies();
sky.moveClouds();
sea.moveWaves();
renderer.render(scene,camera);
requestAnimationFrame(loop);
}
function updateDistance(){
game.distance += game.speed*deltaTime*game.ratioSpeedDistance;
fieldDistance.innerHTML = Math.floor(game.distance);
var d = 502*(1-(game.distance%game.distanceForLevelUpdate)/game.distanceForLevelUpdate);
levelCircle.setAttribute("stroke-dashoffset",d);
}
var blinkEnergy=false;
function updateEnergy(){
game.energy -= game.speed*deltaTime*game.ratioSpeedEnergy;
game.energy = Math.max(0,game.energy);
energyBar.style.right = (100-game.energy)+"%";
energyBar.style.backgroundColor = (game.energy<50)? "#f25346":"#68c3c0";
if (game.energy<30){
energyBar.style.animationName = "blinking";
}
else{
energyBar.style.animationName = "none";
}
if (game.energy <1){
game.status = "gameover";
}
}
function addEnergy(){
game.energy += game.coinValue;
game.energy = Math.min(game.energy,100);
}
function removeEnergy(){
game.energy -= game.ennemyValue;
game.energy = Math.max(0,game.energy);
}
function updatePlane(){
game.planeSpeed = normalize(mousePos.x,-.5,.5,game.planeMinSpeed,game.planeMaxSpeed);
var targetY = normalize(mousePos.y,-.75,.75,game.planeDefaultHeight-game.planeAmpHeight,game.planeDefaultHeight+game.planeAmpHeight);
var targetX = normalize(mousePos.x,-1,1,-game.planeAmpWidth*.7,-game.planeAmpWidth);
game.planeCollisionDisplacementX += game.planeCollisionSpeedX;
targetX += game.planeCollisionDisplacementX;
game.planeCollisionDisplacementY += game.planeCollisionSpeedY;
targetY += game.planeCollisionDisplacementY;
airplane.mesh.position.y += (targetY-airplane.mesh.position.y)*deltaTime*game.planeMoveSensivity;
airplane.mesh.position.x += (targetX-airplane.mesh.position.x)*deltaTime*game.planeMoveSensivity;
airplane.mesh.rotation.z = (targetY-airplane.mesh.position.y)*deltaTime*game.planeRotXSensivity;
airplane.mesh.rotation.x = (airplane.mesh.position.y-targetY)*deltaTime*game.planeRotZSensivity;
var targetCameraZ = normalize(game.planeSpeed,game.planeMinSpeed,game.planeMaxSpeed,game.cameraNearPos,game.cameraFarPos);
camera.fov = normalize(mousePos.x,-1,1,40,80);
camera.updateProjectionMatrix () camera.position.y += (airplane.mesh.position.y - camera.position.y)*deltaTime*game.cameraSensivity;
game.planeCollisionSpeedX += (0-game.planeCollisionSpeedX)*deltaTime * 0.03;
game.planeCollisionDisplacementX += (0-game.planeCollisionDisplacementX)*deltaTime *0.01;
game.planeCollisionSpeedY += (0-game.planeCollisionSpeedY)*deltaTime * 0.03;
game.planeCollisionDisplacementY += (0-game.planeCollisionDisplacementY)*deltaTime *0.01;
airplane.pilot.updateHairs();
}
function showReplay(){
replayMessage.style.display="block";
}
function hideReplay(){
replayMessage.style.display="none";
}
function normalize(v,vmin,vmax,tmin,tmax){
var nv = Math.max(Math.min(v,vmax),vmin);
var dv = vmax-vmin;
var pc = (nv-vmin)/dv;
var dt = tmax-tmin;
var tv = tmin + (pc*dt);
return tv;
}
var fieldDistance,energyBar,replayMessage,fieldLevel,levelCircle;
function init(event){
// UI fieldDistance = document.getElementById("distValue");
energyBar = document.getElementById("energyBar");
replayMessage = document.getElementById("replayMessage");
fieldLevel = document.getElementById("levelValue");
levelCircle = document.getElementById("levelCircleStroke");
resetGame();
createScene();
createLights();
createPlane();
createSea();
createSky();
createCoins();
createEnnemies();
createParticles();
document.addEventListener('mousemove',handleMouseMove,false);
document.addEventListener('touchmove',handleTouchMove,false);
document.addEventListener('mouseup',handleMouseUp,false);
document.addEventListener('touchend',handleTouchEnd,false);
loop();
}
window.addEventListener('load',init,false);
JS代码(main_step1.js):
//COLORSvar Colors ={
red:0xf25346,white:0xd8d0d1,brown:0x59332e,pink:0xF5986E,brownDark:0x23190f,blue:0x68c3c0,}
;
// THREEJS RELATED VARIABLESvar scene,camera,fieldOfView,aspectRatio,nearPlane,farPlane,renderer,container;
//SCREEN & MOUSE VARIABLESvar HEIGHT,WIDTH,mousePos ={
x:0,y:0}
;
//INIT THREE JS,SCREEN AND MOUSE EVENTSfunction createScene(){
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
scene = new THREE.Scene();
aspectRatio = WIDTH / HEIGHT;
fieldOfView = 60;
nearPlane = 1;
farPlane = 10000;
camera = new THREE.PerspectiveCamera( fieldOfView,aspectRatio,nearPlane,farPlane );
scene.fog = new THREE.Fog(0xf7d9aa,100,950);
camera.position.x = 0;
camera.position.z = 200;
camera.position.y = 100;
renderer = new THREE.WebGLRenderer({
alpha:true,antialias:true}
);
renderer.setSize(WIDTH,HEIGHT);
renderer.shadowMap.enabled = true;
container = document.getElementById('world');
container.appendChild(renderer.domElement);
window.addEventListener('resize',handleWindowResize,false);
}
// HANDLE SCREEN EVENTSfunction handleWindowResize(){
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
renderer.setSize(WIDTH,HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
}
// LIGHTSvar ambientLight,hemisphereLight,shadowLight;
function createLights(){
hemisphereLight = new THREE.HemisphereLight(0xaaaaaa,0x000000,.9) shadowLight = new THREE.DirectionalLight(0xffffff,.9);
shadowLight.position.set(150,350,350);
shadowLight.castShadow = true;
shadowLight.shadow.camera.left = -400;
shadowLight.shadow.camera.right = 400;
shadowLight.shadow.camera.top = 400;
shadowLight.shadow.camera.bottom = -400;
shadowLight.shadow.camera.near = 1;
shadowLight.shadow.camera.far = 1000;
shadowLight.shadow.mapSize.width = 2048;
shadowLight.shadow.mapSize.height = 2048;
scene.add(hemisphereLight);
scene.add(shadowLight);
}
var AirPlane = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "airPlane";
// Create the cabinvar geomCockpit = new THREE.BoxGeometry(60,50,50,1,1,1);
var matCockpit = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var cockpit = new THREE.Mesh(geomCockpit,matCockpit);
cockpit.castShadow = true;
cockpit.receiveShadow = true;
this.mesh.add(cockpit);
// Create Engine var geomEngine = new THREE.BoxGeometry(20,50,50,1,1,1);
var matEngine = new THREE.MeshPhongMaterial({
color:Colors.white,shading:THREE.FlatShading}
);
var engine = new THREE.Mesh(geomEngine,matEngine);
engine.position.x = 40;
engine.castShadow = true;
engine.receiveShadow = true;
this.mesh.add(engine);
// Create Tailplane var geomTailPlane = new THREE.BoxGeometry(15,20,5,1,1,1);
var matTailPlane = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var tailPlane = new THREE.Mesh(geomTailPlane,matTailPlane);
tailPlane.position.set(-35,25,0);
tailPlane.castShadow = true;
tailPlane.receiveShadow = true;
this.mesh.add(tailPlane);
// Create Wing var geomSideWing = new THREE.BoxGeometry(40,8,150,1,1,1);
var matSideWing = new THREE.MeshPhongMaterial({
color:Colors.red,shading:THREE.FlatShading}
);
var sideWing = new THREE.Mesh(geomSideWing,matSideWing);
sideWing.position.set(0,0,0);
sideWing.castShadow = true;
sideWing.receiveShadow = true;
this.mesh.add(sideWing);
// Propeller var geomPropeller = new THREE.BoxGeometry(20,10,10,1,1,1);
var matPropeller = new THREE.MeshPhongMaterial({
color:Colors.brown,shading:THREE.FlatShading}
);
this.propeller = new THREE.Mesh(geomPropeller,matPropeller);
this.propeller.castShadow = true;
this.propeller.receiveShadow = true;
// Blades var geomBlade = new THREE.BoxGeometry(1,100,20,1,1,1);
var matBlade = new THREE.MeshPhongMaterial({
color:Colors.brownDark,shading:THREE.FlatShading}
);
var blade = new THREE.Mesh(geomBlade,matBlade);
blade.position.set(8,0,0);
blade.castShadow = true;
blade.receiveShadow = true;
this.propeller.add(blade);
this.propeller.position.set(50,0,0);
this.mesh.add(this.propeller);
}
;
Sky = function(){
this.mesh = new THREE.Object3D();
this.nClouds = 20;
this.clouds = [];
var stepAngle = Math.PI*2 / this.nClouds;
for(var i=0;
i<this.nClouds;
i++){
var c = new Cloud();
this.clouds.push(c);
var a = stepAngle*i;
var h = 750 + Math.random()*200;
c.mesh.position.y = Math.sin(a)*h;
c.mesh.position.x = Math.cos(a)*h;
c.mesh.position.z = -400-Math.random()*400;
c.mesh.rotation.z = a + Math.PI/2;
var s = 1+Math.random()*2;
c.mesh.scale.set(s,s,s);
this.mesh.add(c.mesh);
}
}
Sea = function(){
var geom = new THREE.CylinderGeometry(600,600,800,40,10);
geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI/2));
var mat = new THREE.MeshPhongMaterial({
color:Colors.blue,transparent:true,opacity:.6,shading:THREE.FlatShading,}
);
this.mesh = new THREE.Mesh(geom,mat);
this.mesh.receiveShadow = true;
}
Cloud = function(){
this.mesh = new THREE.Object3D();
this.mesh.name = "cloud";
var geom = new THREE.CubeGeometry(20,20,20);
var mat = new THREE.MeshPhongMaterial({
color:Colors.white,}
);
var nBlocs = 3+Math.floor(Math.random()*3);
for (var i=0;
i<nBlocs;
i++ ){
var m = new THREE.Mesh(geom.clone(),mat);
m.position.x = i*15;
m.position.y = Math.random()*10;
m.position.z = Math.random()*10;
m.rotation.z = Math.random()*Math.PI*2;
m.rotation.y = Math.random()*Math.PI*2;
var s = .1 + Math.random()*.9;
m.scale.set(s,s,s);
m.castShadow = true;
m.receiveShadow = true;
this.mesh.add(m);
}
}
// 3D Modelsvar sea;
var airplane;
function createPlane(){
airplane = new AirPlane();
airplane.mesh.scale.set(.25,.25,.25);
airplane.mesh.position.y = 100;
scene.add(airplane.mesh);
}
function createSea(){
sea = new Sea();
sea.mesh.position.y = -600;
scene.add(sea.mesh);
}
function createSky(){
sky = new Sky();
sky.mesh.position.y = -600;
scene.add(sky.mesh);
}
function loop(){
updatePlane();
sea.mesh.rotation.z += .005;
sky.mesh.rotation.z += .01;
renderer.render(scene,camera);
requestAnimationFrame(loop);
}
function updatePlane(){
var targetY = normalize(mousePos.y,-.75,.75,25,175);
var targetX = normalize(mousePos.x,-.75,.75,-100,100);
airplane.mesh.position.y = targetY;
airplane.mesh.position.x = targetX;
airplane.propeller.rotation.x += 0.3;
}
function normalize(v,vmin,vmax,tmin,tmax){
var nv = Math.max(Math.min(v,vmax),vmin);
var dv = vmax-vmin;
var pc = (nv-vmin)/dv;
var dt = tmax-tmin;
var tv = tmin + (pc*dt);
return tv;
}
function init(event){
document.addEventListener('mousemove',handleMouseMove,false);
createScene();
createLights();
createPlane();
createSea();
createSky();
loop();
}
// HANDLE MOUSE EVENTSvar mousePos ={
x:0,y:0}
;
function handleMouseMove(event){
var tx = -1 + (event.clientX / WIDTH)*2;
var ty = 1 - (event.clientY / HEIGHT)*2;
mousePos ={
x:tx,y:ty}
;
}
window.addEventListener('load',init,false);
CSS代码(demo.css):
@font-face{font-family:'codropsicons';font-weight:normal;font-style:normal;src:url('../fonts/codropsicons/codropsicons.eot');src:url('../fonts/codropsicons/codropsicons.eot?#iefix') format('embedded-opentype'),url('../fonts/codropsicons/codropsicons.woff') format('woff'),url('../fonts/codropsicons/codropsicons.ttf') format('truetype'),url('../fonts/codropsicons/codropsicons.svg#codropsicons') format('svg');}
*,*::after,*::before{-webkit-box-sizing:border-box;box-sizing:border-box;}
body{font-family:'Avenir Next',Avenir,'Helvetica Neue',Helvetica,Arial,sans-serif;margin:0;color:#444;background:#f6f6f6;}
a{text-decoration:none;color:#d1b790;outline:none;}
a:hover,a:focus{color:#bba077;outline:none;}
/* Top Navigation Style */
.meta{font-size:0.75em;line-height:1;position:absolute;top:1em;left:1em;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;white-space:nowrap;border-top:1px solid #d1b790;}
.meta::after{content:'';position:absolute;top:0;right:0;width:1px;height:100%;background:#d1b790;}
.meta a{display:block;padding:1em 1.35em;border:1px solid #d1b790;border-top:0;}
.meta a:not(:last-child){border-right:0;}
.codrops-icon{width:4em;}
.codrops-icon span{display:none;}
.codrops-icon::before{font-family:'codropsicons';font-size:1.15em;font-weight:normal;font-style:normal;font-variant:normal;line-height:1;text-transform:none;speak:none;-webkit-font-smoothing:antialiased;}
.codrops-icon--drop::before{content:'\e001';color:#09c;}
.codrops-icon--prev::before{content:'\e004';}
/* Demo links */
.demo-link{font-family:'Playfair Display';font-weight:bold;text-align:center;letter-spacing:0.2em;text-transform:uppercase;}
.demo-link--current{color:#bba077;}
@media screen and (max-width:40em){.meta{right:1em;}
.demo-link{-webkit-flex:1;flex:1;}
}
/* Styles for sponsor */
body #cdawrap{top:auto;right:auto;bottom:12px;left:12px;border:1px solid #d1b790;background:none;}
.partisan{position:absolute;bottom:0;left:0;width:38em;padding:4.5em 7.5em 1.5em 5.5em;}
.partisan__bg{position:absolute;bottom:0;left:0;width:100%;height:100%;pointer-events:none;opacity:0.5;}
.partisan__link{position:relative;display:-webkit-flex;display:flex;-webkit-justify-content:center;justify-content:center;-webkit-align-items:center;align-items:center;}
.partisan__img,.partisan__title{opacity:0.85;-webkit-transition:opacity 0.1s;transition:opacity 0.1s;}
.partisan__link:hover .partisan__img,.partisan__link:hover .partisan__title{opacity:1;}
.partisan__img{max-width:42%;}
.partisan__title{font-family:'Playfair Display';font-weight:bold;position:relative;margin:0.5em 0 0 0.85em;color:#555d27;}
.partisan__title::before{content:'Sponsored by:';font-size:0.5em;font-weight:bold;position:absolute;bottom:100%;left:0;padding:0 0 1em 0;letter-spacing:0.25em;text-transform:uppercase;color:#95a534;}
@media screen and (max-width:80em){.partisan{font-size:76%;}
.partisan__title::before{font-size:0.25em;}
}
@media screen and (max-width:60em){.game-holder .message--instructions{bottom:5.5em;}
.partisan{width:100%;height:auto;padding:1.75em 0.5em 0.5em;text-align:center;background:rgba(190,215,48,0.5);}
.partisan__bg{display:none;}
.partisan__img{display:none;}
.partisan__title{margin:0;}
.partisan__title::before{width:100%;padding:0 0 0.25em;}
}
CSS代码(game.css):
.game-holder{position:absolute;width:100%;height:100%;background:-webkit-linear-gradient(#e4e0ba,#f7d9aa);background:linear-gradient(#e4e0ba,#f7d9aa);}
.world{position:absolute;overflow:hidden;width:100%;height:100%;}
.header{position:absolute;top:8vh;left:0;width:100%;text-align:center;pointer-events:none;}
.header h1{font-family:'Playfair Display';font-size:4.5em;line-height:1;margin:0;letter-spacing:-0.025em;color:#d1b790;}
.header h1 span{font-size:0.2em;font-style:italic;display:block;margin:0 0 -1.5em -7em;letter-spacing:0px;}
.header h2{font-size:0.585em;margin:0.25em 0;white-space:nowrap;text-indent:1em;letter-spacing:1em;text-transform:uppercase;color:#d6483b;}
.score{width:100%;margin:2em 0 0;text-align:center;white-space:nowrap;}
.score__content{position:relative;display:inline-block;padding:0 1em;vertical-align:top;}
.score__content:nth-child(2){border-right:1px solid #d1b790;border-left:1px solid #d1b790;}
.score__label{font-size:9px;position:relative;margin:0 0 0.5em 0;text-align:center;letter-spacing:3px;text-transform:uppercase;color:#d1b790;}
.score__value{font-family:'Playfair Display';font-weight:bold;color:#d1b790;}
.score__value--level{font-size:26px;}
.score__value--dist{font-size:30px;}
.level-circle{position:absolute;left:50%;width:46px;margin:-37px 0 0 -23px;-webkit-transform:rotate(-90deg);transform:rotate(-90deg);}
.score__value--energy{position:relative;width:60px;height:8px;margin-top:20px;border-radius:3px;background-color:#d1b790;}
.energy-bar{position:absolute;top:0;right:0;bottom:0;left:0;margin:2px;background-color:#f25346;-webkit-animation-name:none;animation-name:none;-webkit-animation-duration:150ms;animation-duration:150ms;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;}
.message{font-weight:bold;position:absolute;left:0;width:100%;text-align:center;text-transform:uppercase;pointer-events:none;}
.message--replay{font-size:1.25vw;bottom:40vh;display:none;text-indent:0.5em;letter-spacing:0.5em;color:#d1b790;}
.message--instructions{font-family:'Playfair Display';font-size:0.85em;bottom:8vh;letter-spacing:0.2em;color:#68c3c0;}
.message--instructions span{display:block;color:#d6483b;white-space:nowrap;}
@-webkit-keyframes blinking{0%{opacity:1;}
50%{opacity:0;}
100%{opacity:1;}
}
@keyframes blinking{0%{opacity:1;}
50%{opacity:0;}
100%{opacity:1;}
}
@media screen and (max-width:40em){.header{font-size:0.75em;top:6.5em;}
.header h2{letter-spacing:0.65em;}
}
CSS代码(styles.css):
.world{position:absolute;width:100%;height:100%;overflow:hidden;background:-webkit-linear-gradient(#e4e0ba,#f7d9aa);background:linear-gradient(#e4e0ba,#f7d9aa);}