<!doctype html> <html> <head> <meta charset="utf-8"> <title>HTML5Canvas模拟飞机航班线路动画</title> <style> *{margin:0;padding:0;} canvas { background:#111; background-size:cover; display:block; } body{overflow: hidden;} </style> </head> <body> <div></div> <script> // queue to land // number of docked or approaching planes // change color of plane for modes // make planes avoid planes // adjustable settings // add random wind factors window.requestAnimFrame=function(){return window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)}}(); $ = {}; $.util = { rand: function( min, max ) { return Math.random() * ( max - min ) + min; }, randInt: function( min, max ) { return Math.floor( Math.random() * ( max - min + 1 ) ) + min; }, norm: function( val, min, max ) { return ( val - min ) / ( max - min ); }, lerp: function( norm, min, max ) { return ( max - min ) * norm + min; }, map: function( val, sMin, sMax, dMin, dMax ) { return $.util.lerp( $.util.norm( val, sMin, sMax), dMin, dMax ); }, clamp: function( val, min, max ) { return Math.min( Math.max( val, Math.min( min, max ) ), Math.max( min, max ) ); }, distance: function( p1, p2 ) { var dx = p1.x - p2.x, dy = p1.y - p2.y; return Math.sqrt( dx * dx + dy * dy ); }, angle: function( p1, p2 ) { return Math.atan2( p1.y - p2.y, p1.x - p2.x ); }, inRange: function( val, min, max ) { return val >= Math.min( min, max ) && val <= Math.max( min, max ); }, pointInRect: function( x, y, rect ) { return $.util.inRange( x, rect.x, rect.x + rect.width ) && $.util.inRange( y, rect.y, rect.y + rect.height ); }, pointInArc: function( p, a ) { return distance( p, a ) <= a.radius; }, setProps: function( obj, props ) { for( var k in props ) { obj[ k ] = props[ k ]; } }, multicurve: function( points, ctx ) { var p0, p1, midx, midy; ctx.moveTo(points[0].x, points[0].y); for(var i = 1; i < points.length - 2; i += 1) { p0 = points[i]; p1 = points[i + 1]; midx = (p0.x + p1.x) / 2; midy = (p0.y + p1.y) / 2; ctx.quadraticCurveTo(p0.x, p0.y, midx, midy); } p0 = points[points.length - 2]; p1 = points[points.length - 1]; ctx.quadraticCurveTo(p0.x, p0.y, p1.x, p1.y); } }; $.init = function() { // setup $.c = document.createElement( 'canvas' ); $.ctx = $.c.getContext( '2d' ); document.body.appendChild( $.c ); // collections $.ports = []; $.planes = []; // events window.addEventListener( 'resize', $.reset, false ); window.addEventListener( 'click', $.reset, false ); $.reset(); $.step(); }; $.reset = function() { // dimensions $.cw = $.c.width = window.innerWidth; $.ch = $.c.height = window.innerHeight; $.dimAvg = ( $.cw + $.ch ) / 2; // type / font $.ctx.textAlign = 'center'; $.ctx.textBaseline = 'middle'; $.ctx.font = '16px monospace'; // options / settings $.opt = {}; $.opt.portCount = 6; $.opt.planeCount = 80; $.opt.portSpacingDist = $.dimAvg / $.opt.portCount; $.opt.holdingDist = 5; $.opt.approachDist = 80; $.opt.planeDist = 20; $.opt.pathSpacing = 15; $.opt.pathCount = 40; $.opt.avoidRadius = 30; $.opt.avoidMult = 0.025; // collections $.ports.length = 0; $.planes.length = 0; // delta $.lt = Date.now(); $.dt = 1; $.et = 0; $.tick = 0; // setup ports for( var i = 0; i < $.opt.portCount; i++ ) { $.ports.push( new $.Port() ); } // setup planes for( var i = 0; i < $.opt.planeCount; i++ ) { $.planes.push( new $.Plane() ); } }; $.Port = function() { this.x = $.util.rand( $.cw * 0.1, $.cw * 0.9 ); this.y = $.util.rand( $.ch * 0.1, $.ch * 0.9 ); while( !this.validSpacing() ) { this.x = $.util.rand( $.cw * 0.1, $.cw * 0.9 ); this.y = $.util.rand( $.ch * 0.1, $.ch * 0.9 ); } }; $.Port.prototype.validSpacing = function() { var spaced = true, i = $.ports.length; while( i-- ) { var otherPort = $.ports[ i ]; if( $.util.distance( otherPort, this ) < $.opt.portSpacingDist ) { spaced = false; break; } } return spaced; }; $.Port.prototype.update = function( i ) { var j = $.planes.length; this.approachingCount = 0; while( j-- ) { var plane = $.planes[ j ]; if( plane.destIndex == i && plane.approaching ) { this.approachingCount++; } } }; $.Port.prototype.render = function( i ) { $.ctx.beginPath(); $.ctx.arc( this.x, this.y, 3 + ( this.approachingCount + 5 ), 0, Math.PI * 2 ); $.ctx.fillStyle = 'hsla(120, 90%, 80%, ' + ( 0.35 + Math.sin( $.et / 20 ) * 0.2 ) + ')'; $.ctx.fill(); $.ctx.fillStyle = '#fff'; $.ctx.fillText( this.approachingCount, this.x, this.y - 30 ); }; $.Plane = function( opt ) { this.originIndex = $.util.randInt( 0, $.ports.length - 1 ); this.origin = $.ports[ this.originIndex ]; this.path = []; this.x = this.origin.x; this.y = this.origin.y; this.vx = $.util.rand( -0.35, 0.35 ); this.vy = $.util.rand( -0.35, 0.35 ); this.vmax = 1; this.accel = 0.01; this.decel = 0.96; this.angle = 0; this.approaching = false; this.holding = false; this.setDest(); }; $.Plane.prototype.setDest = function() { if( this.destIndex != undefined ) { this.originIndex = this.destIndex; this.origin = $.ports[ this.originIndex ]; } this.destIndex = $.util.randInt( 0, $.ports.length - 1 ); while( this.destIndex == this.originIndex ) { this.destIndex = $.util.randInt( 0, $.ports.length - 1 ); } this.dest = $.ports[ this.destIndex ]; this.approaching = false; this.holding = false; } $.Plane.prototype.update = function( i ) { this.ox = this.x; this.oy = this.y; if( $.tick % $.opt.pathSpacing == 0 ) { this.path.push( { x: this.x, y: this.y } ); } if( this.path.length > $.opt.pathCount ) { this.path.shift(); } this.angle = $.util.angle( this.dest, this ); this.speed = ( Math.abs( this.vx ) + Math.abs( this.vy ) ) / 2; if( !$.util.pointInRect( this.x, this.y, { x: 0, y: 0, width: $.cw, height: $.ch } ) ) { this.vx *= this.decel; this.vy *= this.decel; } if( this.speed > 0.1 ) { if( $.util.distance( this.dest, this ) < $.opt.approachDist ) { this.vx *= this.decel; this.vy *= this.decel; this.approaching = true; } } if( $.util.distance( this.dest, this ) < $.opt.holdingDist ) { this.holding = true; this.setDest(); } // plane checks /*var j = i; while( j-- ) { var otherPlane = $.planes[ j ]; if( $.util.distance( otherPlane, this ) < $.opt.avoidRadius ) { var angle = $.util.angle( otherPlane, this ); var changer = ( ( Math.abs( this.vx ) + Math.abs( this.vy ) + Math.abs( otherPlane.vx ) + Math.abs( otherPlane.vy ) ) / 4 ) * $.opt.avoidMult; this.vx -= Math.cos( angle ) * changer; this.vy -= Math.sin( angle ) * changer; otherPlane.vx += Math.cos( angle ) * changer; otherPlane.vy += Math.sin( angle ) * changer; } }*/ this.vx += Math.cos( this.angle ) * this.accel; this.vy += Math.sin( this.angle ) * this.accel; if( this.speed > this.vmax ) { this.vx *= this.decel; this.vy *= this.decel; } this.x += this.vx * $.dt; this.y += this.vy * $.dt; }; $.Plane.prototype.render = function( i ) { if( this.approaching ) { $.ctx.strokeStyle = 'hsla(0, 80%, 50%, 1)'; } else { $.ctx.strokeStyle = 'hsla(180, 80%, 50%, 1)'; } $.ctx.beginPath(); $.ctx.moveTo( this.x, this.y ); var angle = $.util.angle( { x: this.ox, y: this.oy }, this ); $.ctx.lineWidth = 2; $.ctx.lineTo( this.x - Math.cos( angle ) * ( 3 + this.speed * 2 ), this.y - Math.sin( angle ) * ( 3 + this.speed * 2 ) ); $.ctx.stroke(); var pathLength = this.path.length; if( pathLength > 1) { $.ctx.strokeStyle = 'hsla(0, 0%, 100%, 0.15)'; $.ctx.lineWidth = 1; $.ctx.beginPath(); if( pathLength >= $.opt.pathCount ) { var angle = $.util.angle( this.path[ 1 ], this.path[ 0 ] ), dx = this.path[ 0 ].x - this.path[ 1 ].x, dy = this.path[ 0 ].y - this.path[ 1 ].y, dist = Math.sqrt( dx * dx + dy * dy ), x = this.path[ 0 ].x + Math.cos( angle ) * ( dist * ( ( $.tick % $.opt.pathSpacing ) / $.opt.pathSpacing ) ), y = this.path[ 0 ].y + Math.sin( angle ) * ( dist * ( ( $.tick % $.opt.pathSpacing ) / $.opt.pathSpacing ) ); } else { var x = this.path[ 0 ].x, y = this.path[ 0 ].y } $.ctx.moveTo( x, y ); for( var i = 1; i < pathLength; i++ ) { var point = this.path[ i ]; $.ctx.lineTo( point.x, point.y ); } $.ctx.lineTo( this.x, this.y ); $.ctx.stroke(); } }; $.step = function() { requestAnimFrame( $.step ); // clear $.ctx.globalCompositeOperation = 'destination-out'; $.ctx.fillStyle = 'hsla(0, 0%, 0%, 1)'; $.ctx.fillRect( 0, 0, $.cw, $.ch ); $.ctx.globalCompositeOperation = 'lighter'; // collections var i; i = $.ports.length; while( i-- ) { $.ports[ i ].update( i ) } i = $.planes.length; while( i-- ) { $.planes[ i ].update( i ) } i = $.ports.length; while( i-- ) { $.ports[ i ].render( i ) } i = $.planes.length; while( i-- ) { $.planes[ i ].render( i ) } // delta var now = Date.now(); $.dt = $.util.clamp( ( now - $.lt ) / ( 1000 / 60 ), 0.001, 10 ); $.lt = now; $.et += $.dt; $.tick++; }; $.init(); </script> </body> </html>
HTML5Canvas模拟飞机航班线路动画
标签:
评论者:[[ schemeInfo.user.username ]]
评论内容:[[ schemeInfo.pbody ]]
评论时间:[[ schemeInfo.ptime ]]
发表你的评论:
提交评论
上一篇:
CSS3鼠标滑过购物车图片切换
下一篇:
纯CSS3开关样式的自定义单选框