IT资讯 |  编程 |  数据库 |  安全 |  系统 |  服务器 |  嵌入式 |  设计 |  基础 |  组网 |  QQ | 

HTML5重力感应小球冲撞动画实现教程

作者:html5tricks  发布日期:2014-08-19 20:36:50
  • 收藏本文    我要投稿
  • 今天我们来分享一款很酷的HTML5重力感应动画教程,这款动画可以让你甩动页面中的小球,小球的大小都不同,并且鼠标点击空白区域时又可以生成一定数量的小球。当我们甩动小球时,各个小球之间就会发生互相碰撞的效果,并且在运动过程中模拟了重力感应的物理效果。你可以在DEMO演示中来尝试一下。

    html5-ball-pool

    你也可以在这里查看在线演示

    接下来我们来分析一下这款超酷的HTML5重力动画实现的思路及源码,主要由HTML代码和Javascript代码组成。

    HTML代码:

    <div id='canvas'></div>

    还是很简单,HTML仅仅是列出了一个canvas容器,今后我们将在这里生成一些列canvas元素,这些小球就在canvas中运动。

    另外由于该动画利用了box2d的js脚本库,所以在页面上你也需要引用它:

    <script src='box2d.js'></script>

    接下来是Javascript代码,在canvas上动态创建大小和样式不一的小球,并发生碰撞效果。

    Javascript代码:

    var canvas;
    
    var delta = [ 0, 0 ];
    var stage = [ window.screenX, window.screenY, window.innerWidth, window.innerHeight ];
    getBrowserDimensions();
    
    var themes = [ [ '#10222B', '#95AB63', '#BDD684', '#E2F0D6', '#F6FFE0' ],
            [ '#362C2A', '#732420', '#BF734C', '#FAD9A0', '#736859' ],
            [ '#0D1114', '#102C2E', '#695F4C', '#EBBC5E', '#FFFBB8' ],
            [ '#2E2F38', '#FFD63E', '#FFB54B', '#E88638', '#8A221C' ],
            [ '#121212', '#E6F2DA', '#C9F24B', '#4D7B85', '#23383D' ],
            [ '#343F40', '#736751', '#F2D7B6', '#BFAC95', '#8C3F3F' ],
            [ '#000000', '#2D2B2A', '#561812', '#B81111', '#FFFFFF' ],
            [ '#333B3A', '#B4BD51', '#543B38', '#61594D', '#B8925A' ] ];
    var theme;
    
    var worldAABB, world, iterations = 1, timeStep = 1 / 15;
    
    var walls = [];
    var wall_thickness = 200;
    var wallsSetted = false;
    
    var bodies, elements, text;
    
    var createMode = false;
    var destroyMode = false;
    
    var isMouseDown = false;
    var mouseJoint;
    var mouse = { x: 0, y: 0 };
    var gravity = { x: 0, y: 1 };
    
    var PI2 = Math.PI * 2;
    
    var timeOfLastTouch = 0;
    
    init();
    play();
    
    function init() {
    
        canvas = document.getElementById( 'canvas' );
    
        document.onmousedown = onDocumentMouseDown;
        document.onmouseup = onDocumentMouseUp;
        document.onmousemove = onDocumentMouseMove;
        document.ondblclick = onDocumentDoubleClick;
    
        document.addEventListener( 'touchstart', onDocumentTouchStart, false );
        document.addEventListener( 'touchmove', onDocumentTouchMove, false );
        document.addEventListener( 'touchend', onDocumentTouchEnd, false );
    
        window.addEventListener( 'deviceorientation', onWindowDeviceOrientation, false );
    
        // init box2d
    
        worldAABB = new b2AABB();
        worldAABB.minVertex.Set( -200, -200 );
        worldAABB.maxVertex.Set( window.innerWidth + 200, window.innerHeight + 200 );
    
        world = new b2World( worldAABB, new b2Vec2( 0, 0 ), true );
    
        setWalls();
        reset();
    }
    
    
    function play() {
    
        setInterval( loop, 1000 / 40 );
    }
    
    function reset() {
    
        var i;
    
        if ( bodies ) {
    
            for ( i = 0; i < bodies.length; i++ ) {
    
                var body = bodies[ i ]
                canvas.removeChild( body.GetUserData().element );
                world.DestroyBody( body );
                body = null;
            }
        }
    
        // color theme
        theme = themes[ Math.random() * themes.length >> 0 ];
        document.body.style[ 'backgroundColor' ] = theme[ 0 ];
    
        bodies = [];
        elements = [];
    
        createInstructions();
    
        for( i = 0; i < 10; i++ ) {
    
            createBall();
    
        }
    
    }
    
    //
    
    function onDocumentMouseDown() {
    
        isMouseDown = true;
        return false;
    }
    
    function onDocumentMouseUp() {
    
        isMouseDown = false;
        return false;
    }
    
    function onDocumentMouseMove( event ) {
    
        mouse.x = event.clientX;
        mouse.y = event.clientY;
    }
    
    function onDocumentDoubleClick() {
    
        reset();
    }
    
    function onDocumentTouchStart( event ) {
    
        if( event.touches.length == 1 ) {
    
            event.preventDefault();
    
            // Faking double click for touch devices
    
            var now = new Date().getTime();
    
            if ( now - timeOfLastTouch  < 250 ) {
    
                reset();
                return;
            }
    
            timeOfLastTouch = now;
    
            mouse.x = event.touches[ 0 ].pageX;
            mouse.y = event.touches[ 0 ].pageY;
            isMouseDown = true;
        }
    }
    
    function onDocumentTouchMove( event ) {
    
        if ( event.touches.length == 1 ) {
    
            event.preventDefault();
    
            mouse.x = event.touches[ 0 ].pageX;
            mouse.y = event.touches[ 0 ].pageY;
    
        }
    
    }
    
    function onDocumentTouchEnd( event ) {
    
        if ( event.touches.length == 0 ) {
    
            event.preventDefault();
            isMouseDown = false;
    
        }
    
    }
    
    function onWindowDeviceOrientation( event ) {
    
        if ( event.beta ) {
    
            gravity.x = Math.sin( event.gamma * Math.PI / 180 );
            gravity.y = Math.sin( ( Math.PI / 4 ) + event.beta * Math.PI / 180 );
    
        }
    
    }
    
    //
    
    function createInstructions() {
    
        var size = 250;
    
        var element = document.createElement( 'div' );
        element.width = size;
        element.height = size;    
        element.style.position = 'absolute';
        element.style.left = -200 + 'px';
        element.style.top = -200 + 'px';
        element.style.cursor = 'default';
    
        canvas.appendChild(element);
        elements.push( element );
    
        var circle = document.createElement( 'canvas' );
        circle.width = size;
        circle.height = size;
    
        var graphics = circle.getContext( '2d' );
    
        graphics.fillStyle = theme[ 3 ];
        graphics.beginPath();
        graphics.arc( size * .5, size * .5, size * .5, 0, PI2, true );
        graphics.closePath();
        graphics.fill();
    
        element.appendChild( circle );
    
        text = document.createElement( 'div' );
        text.onSelectStart = null;
        text.innerHTML = '<span style='color:' + theme[0] + ';font-size:40px;'>Hello!</span><br /><br /><span style='font-size:15px;'><strong>This is how it works:</strong><br /><br />1. Drag a ball.<br />2.&nbsp;Click&nbsp;on&nbsp;the&nbsp;background.<br />3. Shake your browser.<br />4. Double click.<br />5. Play!</span>';
        text.style.color = theme[1];
        text.style.position = 'absolute';
        text.style.left = '0px';
        text.style.top = '0px';
        text.style.fontFamily = 'Georgia';
        text.style.textAlign = 'center';
        element.appendChild(text);
    
        text.style.left = ((250 - text.clientWidth) / 2) +'px';
        text.style.top = ((250 - text.clientHeight) / 2) +'px';    
    
        var b2body = new b2BodyDef();
    
        var circle = new b2CircleDef();
        circle.radius = size / 2;
        circle.density = 1;
        circle.friction = 0.3;
        circle.restitution = 0.3;
        b2body.AddShape(circle);
        b2body.userData = {element: element};
    
        b2body.position.Set( Math.random() * stage[2], Math.random() * -200 );
        b2body.linearVelocity.Set( Math.random() * 400 - 200, Math.random() * 400 - 200 );
        bodies.push( world.CreateBody(b2body) );    
    }
    
    function createBall( x, y ) {
    
        var x = x || Math.random() * stage[2];
        var y = y || Math.random() * -200;
    
        var size = (Math.random() * 100 >> 0) + 20;
    
        var element = document.createElement('canvas');
        element.width = size;
        element.height = size;
        element.style.position = 'absolute';
        element.style.left = -200 + 'px';
        element.style.top = -200 + 'px';
        element.style.WebkitTransform = 'translateZ(0)';
        element.style.MozTransform = 'translateZ(0)';
        element.style.OTransform = 'translateZ(0)';
        element.style.msTransform = 'translateZ(0)';
        element.style.transform = 'translateZ(0)';
    
        var graphics = element.getContext('2d');
    
        var num_circles = Math.random() * 10 >> 0;
    
        for (var i = size; i > 0; i-= (size/num_circles)) {
    
            graphics.fillStyle = theme[ (Math.random() * 4 >> 0) + 1];
            graphics.beginPath();
            graphics.arc(size * .5, size * .5, i * .5, 0, PI2, true); 
            graphics.closePath();
            graphics.fill();
        }
    
        canvas.appendChild(element);
    
        elements.push( element );
    
        var b2body = new b2BodyDef();
    
        var circle = new b2CircleDef();
        circle.radius = size >> 1;
        circle.density = 1;
        circle.friction = 0.3;
        circle.restitution = 0.3;
        b2body.AddShape(circle);
        b2body.userData = {element: element};
    
        b2body.position.Set( x, y );
        b2body.linearVelocity.Set( Math.random() * 400 - 200, Math.random() * 400 - 200 );
        bodies.push( world.CreateBody(b2body) );
    }
    
    //
    
    function loop() {
    
        if (getBrowserDimensions()) {
    
            setWalls();
    
        }
    
        delta[0] += (0 - delta[0]) * .5;
        delta[1] += (0 - delta[1]) * .5;
    
        world.m_gravity.x = gravity.x * 350 + delta[0];
        world.m_gravity.y = gravity.y * 350 + delta[1];
    
        mouseDrag();
        world.Step(timeStep, iterations);
    
        for (i = 0; i < bodies.length; i++) {
    
            var body = bodies[i];
            var element = elements[i];
    
            element.style.left = (body.m_position0.x - (element.width >> 1)) + 'px';
            element.style.top = (body.m_position0.y - (element.height >> 1)) + 'px';
    
            if (element.tagName == 'DIV') {
    
                var style = 'rotate(' + (body.m_rotation0 * 57.2957795) + 'deg) translateZ(0)';
                text.style.WebkitTransform = style;
                text.style.MozTransform = style;
                text.style.OTransform = style;
                text.style.msTransform = style;
                text.style.transform = style;
    
            }
    
        }
    
    }
    
    
    // .. BOX2D UTILS
    
    function createBox(world, x, y, width, height, fixed) {
    
        if (typeof(fixed) == 'undefined') {
    
            fixed = true;
    
        }
    
        var boxSd = new b2BoxDef();
    
        if (!fixed) {
    
            boxSd.density = 1.0;
    
        }
    
        boxSd.extents.Set(width, height);
    
        var boxBd = new b2BodyDef();
        boxBd.AddShape(boxSd);
        boxBd.position.Set(x,y);
    
        return world.CreateBody(boxBd);
    
    }
    
    function mouseDrag()
    {
        // mouse press
        if (createMode) {
    
            createBall( mouse.x, mouse.y );
    
        } else if (isMouseDown && !mouseJoint) {
    
            var body = getBodyAtMouse();
    
            if (body) {
    
                var md = new b2MouseJointDef();
                md.body1 = world.m_groundBody;
                md.body2 = body;
                md.target.Set(mouse.x, mouse.y);
                md.maxForce = 30000 * body.m_mass;
                // md.timeStep = timeStep;
                mouseJoint = world.CreateJoint(md);
                body.WakeUp();
    
            } else {
    
                createMode = true;
    
            }
    
        }
    
        // mouse release
        if (!isMouseDown) {
    
            createMode = false;
            destroyMode = false;
    
            if (mouseJoint) {
    
                world.DestroyJoint(mouseJoint);
                mouseJoint = null;
    
            }
    
        }
    
        // mouse move
        if (mouseJoint) {
    
            var p2 = new b2Vec2(mouse.x, mouse.y);
            mouseJoint.SetTarget(p2);
        }
    }
    
    function getBodyAtMouse() {
    
        // Make a small box.
        var mousePVec = new b2Vec2();
        mousePVec.Set(mouse.x, mouse.y);
    
        var aabb = new b2AABB();
        aabb.minVertex.Set(mouse.x - 1, mouse.y - 1);
        aabb.maxVertex.Set(mouse.x + 1, mouse.y + 1);
    
        // Query the world for overlapping shapes.
        var k_maxCount = 10;
        var shapes = new Array();
        var count = world.Query(aabb, shapes, k_maxCount);
        var body = null;
    
        for (var i = 0; i < count; ++i) {
    
            if (shapes[i].m_body.IsStatic() == false) {
    
                if ( shapes[i].TestPoint(mousePVec) ) {
    
                    body = shapes[i].m_body;
                    break;
    
                }
    
            }
    
        }
    
        return body;
    
    }
    
    function setWalls() {
    
        if (wallsSetted) {
    
            world.DestroyBody(walls[0]);
            world.DestroyBody(walls[1]);
            world.DestroyBody(walls[2]);
            world.DestroyBody(walls[3]);
    
            walls[0] = null; 
            walls[1] = null;
            walls[2] = null;
            walls[3] = null;
        }
    
        walls[0] = createBox(world, stage[2] / 2, - wall_thickness, stage[2], wall_thickness);
        walls[1] = createBox(world, stage[2] / 2, stage[3] + wall_thickness, stage[2], wall_thickness);
        walls[2] = createBox(world, - wall_thickness, stage[3] / 2, wall_thickness, stage[3]);
        walls[3] = createBox(world, stage[2] + wall_thickness, stage[3] / 2, wall_thickness, stage[3]);    
    
        wallsSetted = true;
    
    }
    
    // BROWSER DIMENSIONS
    
    function getBrowserDimensions() {
    
        var changed = false;
    
        if (stage[0] != window.screenX) {
    
            delta[0] = (window.screenX - stage[0]) * 50;
            stage[0] = window.screenX;
            changed = true;
    
        }
    
        if (stage[1] != window.screenY) {
    
            delta[1] = (window.screenY - stage[1]) * 50;
            stage[1] = window.screenY;
            changed = true;
    
        }
    
        if (stage[2] != window.innerWidth) {
    
            stage[2] = window.innerWidth;
            changed = true;
    
        }
    
        if (stage[3] != window.innerHeight) {
    
            stage[3] = window.innerHeight;
            changed = true;
    
        }
    
        return changed;
    
    }

    上面mouseDrag方法就实现了鼠标拖拽甩动小球的功能,这也是该动画最重要的方法。全部代码可以下载源码来研究。http://www.it165.net/uploadfile/files/2014/0819/html5-ball-pool.rar

Tag标签:小球  重力  动画  
  • 专题推荐