今天我们来分享一款很酷的HTML5重力感应动画教程,这款动画可以让你甩动页面中的小球,小球的大小都不同,并且鼠标点击空白区域时又可以生成一定数量的小球。当我们甩动小球时,各个小球之间就会发生互相碰撞的效果,并且在运动过程中模拟了重力感应的物理效果。

1.jpg

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

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

HTML代码:

1
<div id="canvas"></div>

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

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

 css |  copy code |? 
1
<script src="box2d.js"></script>

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

Javascript代码:

 javascript |  copy code |? 
001
var canvas;
002
 
003
var delta = [ 0, 0 ];
004
var stage = [ window.screenX, window.screenY, window.innerWidth, window.innerHeight ];
005
getBrowserDimensions();
006
 
007
var themes = [ [ "#10222B", "#95AB63", "#BDD684", "#E2F0D6", "#F6FFE0" ],
008
        [ "#362C2A", "#732420", "#BF734C", "#FAD9A0", "#736859" ],
009
        [ "#0D1114", "#102C2E", "#695F4C", "#EBBC5E", "#FFFBB8" ],
010
        [ "#2E2F38", "#FFD63E", "#FFB54B", "#E88638", "#8A221C" ],
011
        [ "#121212", "#E6F2DA", "#C9F24B", "#4D7B85", "#23383D" ],
012
        [ "#343F40", "#736751", "#F2D7B6", "#BFAC95", "#8C3F3F" ],
013
        [ "#000000", "#2D2B2A", "#561812", "#B81111", "#FFFFFF" ],
014
        [ "#333B3A", "#B4BD51", "#543B38", "#61594D", "#B8925A" ] ];
015
var theme;
016
 
017
var worldAABB, world, iterations = 1, timeStep = 1 / 15;
018
 
019
var walls = [];
020
var wall_thickness = 200;
021
var wallsSetted = false;
022
 
023
var bodies, elements, text;
024
 
025
var createMode = false;
026
var destroyMode = false;
027
 
028
var isMouseDown = false;
029
var mouseJoint;
030
var mouse = { x: 0, y: 0 };
031
var gravity = { x: 0, y: 1 };
032
 
033
var PI2 = Math.PI * 2;
034
 
035
var timeOfLastTouch = 0;
036
 
037
init();
038
play();
039
 
040
function init() {
041
 
042
    canvas = document.getElementById( 'canvas' );
043
 
044
    document.onmousedown = onDocumentMouseDown;
045
    document.onmouseup = onDocumentMouseUp;
046
    document.onmousemove = onDocumentMouseMove;
047
    document.ondblclick = onDocumentDoubleClick;
048
 
049
    document.addEventListener( 'touchstart', onDocumentTouchStart, false );
050
    document.addEventListener( 'touchmove', onDocumentTouchMove, false );
051
    document.addEventListener( 'touchend', onDocumentTouchEnd, false );
052
 
053
    window.addEventListener( 'deviceorientation', onWindowDeviceOrientation, false );
054
 
055
    // init box2d
056
 
057
    worldAABB = new b2AABB();
058
    worldAABB.minVertex.Set( -200, -200 );
059
    worldAABB.maxVertex.Set( window.innerWidth + 200, window.innerHeight + 200 );
060
 
061
    world = new b2World( worldAABB, new b2Vec2( 0, 0 ), true );
062
 
063
    setWalls();
064
    reset();
065
}
066
 
067
 
068
function play() {
069
 
070
    setInterval( loop, 1000 / 40 );
071
}
072
 
073
function reset() {
074
 
075
    var i;
076
 
077
    if ( bodies ) {
078
 
079
        for ( i = 0; i < bodies.length; i++ ) {
080
 
081
            var body = bodies[ i ]
082
            canvas.removeChild( body.GetUserData().element );
083
            world.DestroyBody( body );
084
            body = null;
085
        }
086
    }
087
 
088
    // color theme
089
    theme = themes[ Math.random() * themes.length >> 0 ];
090
    document.body.style[ 'backgroundColor' ] = theme[ 0 ];
091
 
092
    bodies = [];
093
    elements = [];
094
 
095
    createInstructions();
096
 
097
    for( i = 0; i < 10; i++ ) {
098
 
099
        createBall();
100
 
101
    }
102
 
103
}
104
 
105
//
106
 
107
function onDocumentMouseDown() {
108
 
109
    isMouseDown = true;
110
    return false;
111
}
112
 
113
function onDocumentMouseUp() {
114
 
115
    isMouseDown = false;
116
    return false;
117
}
118
 
119
function onDocumentMouseMove( event ) {
120
 
121
    mouse.x = event.clientX;
122
    mouse.y = event.clientY;
123
}
124
 
125
function onDocumentDoubleClick() {
126
 
127
    reset();
128
}
129
 
130
function onDocumentTouchStart( event ) {
131
 
132
    if( event.touches.length == 1 ) {
133
 
134
        event.preventDefault();
135
 
136
        // Faking double click for touch devices
137
 
138
        var now = new Date().getTime();
139
 
140
        if ( now - timeOfLastTouch  < 250 ) {
141
 
142
            reset();
143
            return;
144
        }
145
 
146
        timeOfLastTouch = now;
147
 
148
        mouse.x = event.touches[ 0 ].pageX;
149
        mouse.y = event.touches[ 0 ].pageY;
150
        isMouseDown = true;
151
    }
152
}
153
 
154
function onDocumentTouchMove( event ) {
155
 
156
    if ( event.touches.length == 1 ) {
157
 
158
        event.preventDefault();
159
 
160
        mouse.x = event.touches[ 0 ].pageX;
161
        mouse.y = event.touches[ 0 ].pageY;
162
 
163
    }
164
 
165
}
166
 
167
function onDocumentTouchEnd( event ) {
168
 
169
    if ( event.touches.length == 0 ) {
170
 
171
        event.preventDefault();
172
        isMouseDown = false;
173
 
174
    }
175
 
176
}
177
 
178
function onWindowDeviceOrientation( event ) {
179
 
180
    if ( event.beta ) {
181
 
182
        gravity.x = Math.sin( event.gamma * Math.PI / 180 );
183
        gravity.y = Math.sin( ( Math.PI / 4 ) + event.beta * Math.PI / 180 );
184
 
185
    }
186
 
187
}
188
 
189
//
190
 
191
function createInstructions() {
192
 
193
    var size = 250;
194
 
195
    var element = document.createElement( 'div' );
196
    element.width = size;
197
    element.height = size;    
198
    element.style.position = 'absolute';
199
    element.style.left = -200 + 'px';
200
    element.style.top = -200 + 'px';
201
    element.style.cursor = "default";
202
 
203
    canvas.appendChild(element);
204
    elements.push( element );
205
 
206
    var circle = document.createElement( 'canvas' );
207
    circle.width = size;
208
    circle.height = size;
209
 
210
    var graphics = circle.getContext( '2d' );
211
 
212
    graphics.fillStyle = theme[ 3 ];
213
    graphics.beginPath();
214
    graphics.arc( size * .5, size * .5, size * .5, 0, PI2, true );
215
    graphics.closePath();
216
    graphics.fill();
217
 
218
    element.appendChild( circle );
219
 
220
    text = document.createElement( 'div' );
221
    text.onSelectStart = null;
222
    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. Click on the background.<br />3. Shake your browser.<br />4. Double click.<br />5. Play!</span>';
223
    text.style.color = theme[1];
224
    text.style.position = 'absolute';
225
    text.style.left = '0px';
226
    text.style.top = '0px';
227
    text.style.fontFamily = 'Georgia';
228
    text.style.textAlign = 'center';
229
    element.appendChild(text);
230
 
231
    text.style.left = ((250 - text.clientWidth) / 2) +'px';
232
    text.style.top = ((250 - text.clientHeight) / 2) +'px';    
233
 
234
    var b2body = new b2BodyDef();
235
 
236
    var circle = new b2CircleDef();
237
    circle.radius = size / 2;
238
    circle.density = 1;
239
    circle.friction = 0.3;
240
    circle.restitution = 0.3;
241
    b2body.AddShape(circle);
242
    b2body.userData = {element: element};
243
 
244
    b2body.position.Set( Math.random() * stage[2], Math.random() * -200 );
245
    b2body.linearVelocity.Set( Math.random() * 400 - 200, Math.random() * 400 - 200 );
246
    bodies.push( world.CreateBody(b2body) );    
247
}
248
 
249
function createBall( x, y ) {
250
 
251
    var x = x || Math.random() * stage[2];
252
    var y = y || Math.random() * -200;
253
 
254
    var size = (Math.random() * 100 >> 0) + 20;
255
 
256
    var element = document.createElement("canvas");
257
    element.width = size;
258
    element.height = size;
259
    element.style.position = 'absolute';
260
    element.style.left = -200 + 'px';
261
    element.style.top = -200 + 'px';
262
    element.style.WebkitTransform = 'translateZ(0)';
263
    element.style.MozTransform = 'translateZ(0)';
264
    element.style.OTransform = 'translateZ(0)';
265
    element.style.msTransform = 'translateZ(0)';
266
    element.style.transform = 'translateZ(0)';
267
 
268
    var graphics = element.getContext("2d");
269
 
270
    var num_circles = Math.random() * 10 >> 0;
271
 
272
    for (var i = size; i > 0; i-= (size/num_circles)) {
273
 
274
        graphics.fillStyle = theme[ (Math.random() * 4 >> 0) + 1];
275
        graphics.beginPath();
276
        graphics.arc(size * .5, size * .5, i * .5, 0, PI2, true); 
277
        graphics.closePath();
278
        graphics.fill();
279
    }
280
 
281
    canvas.appendChild(element);
282
 
283
    elements.push( element );
284
 
285
    var b2body = new b2BodyDef();
286
 
287
    var circle = new b2CircleDef();
288
    circle.radius = size >> 1;
289
    circle.density = 1;
290
    circle.friction = 0.3;
291
    circle.restitution = 0.3;
292
    b2body.AddShape(circle);
293
    b2body.userData = {element: element};
294
 
295
    b2body.position.Set( x, y );
296
    b2body.linearVelocity.Set( Math.random() * 400 - 200, Math.random() * 400 - 200 );
297
    bodies.push( world.CreateBody(b2body) );
298
}
299
 
300
//
301
 
302
function loop() {
303
 
304
    if (getBrowserDimensions()) {
305
 
306
        setWalls();
307
 
308
    }
309
 
310
    delta[0] += (0 - delta[0]) * .5;
311
    delta[1] += (0 - delta[1]) * .5;
312
 
313
    world.m_gravity.x = gravity.x * 350 + delta[0];
314
    world.m_gravity.y = gravity.y * 350 + delta[1];
315
 
316
    mouseDrag();
317
    world.Step(timeStep, iterations);
318
 
319
    for (i = 0; i < bodies.length; i++) {
320
 
321
        var body = bodies[i];
322
        var element = elements[i];
323
 
324
        element.style.left = (body.m_position0.x - (element.width >> 1)) + 'px';
325
        element.style.top = (body.m_position0.y - (element.height >> 1)) + 'px';
326
 
327
        if (element.tagName == 'DIV') {
328
 
329
            var style = 'rotate(' + (body.m_rotation0 * 57.2957795) + 'deg) translateZ(0)';
330
            text.style.WebkitTransform = style;
331
            text.style.MozTransform = style;
332
            text.style.OTransform = style;
333
            text.style.msTransform = style;
334
            text.style.transform = style;
335
 
336
        }
337
 
338
    }
339
 
340
}
341
 
342
 
343
// .. BOX2D UTILS
344
 
345
function createBox(world, x, y, width, height, fixed) {
346
 
347
    if (typeof(fixed) == 'undefined') {
348
 
349
        fixed = true;
350
 
351
    }
352
 
353
    var boxSd = new b2BoxDef();
354
 
355
    if (!fixed) {
356
 
357
        boxSd.density = 1.0;
358
 
359
    }
360
 
361
    boxSd.extents.Set(width, height);
362
 
363
    var boxBd = new b2BodyDef();
364
    boxBd.AddShape(boxSd);
365
    boxBd.position.Set(x,y);
366
 
367
    return world.CreateBody(boxBd);
368
 
369
}
370
 
371
function mouseDrag()
372
{
373
    // mouse press
374
    if (createMode) {
375
 
376
        createBall( mouse.x, mouse.y );
377
 
378
    } else if (isMouseDown && !mouseJoint) {
379
 
380
        var body = getBodyAtMouse();
381
 
382
        if (body) {
383
 
384
            var md = new b2MouseJointDef();
385
            md.body1 = world.m_groundBody;
386
            md.body2 = body;
387
            md.target.Set(mouse.x, mouse.y);
388
            md.maxForce = 30000 * body.m_mass;
389
            // md.timeStep = timeStep;
390
            mouseJoint = world.CreateJoint(md);
391
            body.WakeUp();
392
 
393
        } else {
394
 
395
            createMode = true;
396
 
397
        }
398
 
399
    }
400
 
401
    // mouse release
402
    if (!isMouseDown) {
403
 
404
        createMode = false;
405
        destroyMode = false;
406
 
407
        if (mouseJoint) {
408
 
409
            world.DestroyJoint(mouseJoint);
410
            mouseJoint = null;
411
 
412
        }
413
 
414
    }
415
 
416
    // mouse move
417
    if (mouseJoint) {
418
 
419
        var p2 = new b2Vec2(mouse.x, mouse.y);
420
        mouseJoint.SetTarget(p2);
421
    }
422
}
423
 
424
function getBodyAtMouse() {
425
 
426
    // Make a small box.
427
    var mousePVec = new b2Vec2();
428
    mousePVec.Set(mouse.x, mouse.y);
429
 
430
    var aabb = new b2AABB();
431
    aabb.minVertex.Set(mouse.x - 1, mouse.y - 1);
432
    aabb.maxVertex.Set(mouse.x + 1, mouse.y + 1);
433
 
434
    // Query the world for overlapping shapes.
435
    var k_maxCount = 10;
436
    var shapes = new Array();
437
    var count = world.Query(aabb, shapes, k_maxCount);
438
    var body = null;
439
 
440
    for (var i = 0; i < count; ++i) {
441
 
442
        if (shapes[i].m_body.IsStatic() == false) {
443
 
444
            if ( shapes[i].TestPoint(mousePVec) ) {
445
 
446
                body = shapes[i].m_body;
447
                break;
448
 
449
            }
450
 
451
        }
452
 
453
    }
454
 
455
    return body;
456
 
457
}
458
 
459
function setWalls() {
460
 
461
    if (wallsSetted) {
462
 
463
        world.DestroyBody(walls[0]);
464
        world.DestroyBody(walls[1]);
465
        world.DestroyBody(walls[2]);
466
        world.DestroyBody(walls[3]);
467
 
468
        walls[0] = null; 
469
        walls[1] = null;
470
        walls[2] = null;
471
        walls[3] = null;
472
    }
473
 
474
    walls[0] = createBox(world, stage[2] / 2, - wall_thickness, stage[2], wall_thickness);
475
    walls[1] = createBox(world, stage[2] / 2, stage[3] + wall_thickness, stage[2], wall_thickness);
476
    walls[2] = createBox(world, - wall_thickness, stage[3] / 2, wall_thickness, stage[3]);
477
    walls[3] = createBox(world, stage[2] + wall_thickness, stage[3] / 2, wall_thickness, stage[3]);    
478
 
479
    wallsSetted = true;
480
 
481
}
482
 
483
// BROWSER DIMENSIONS
484
 
485
function getBrowserDimensions() {
486
 
487
    var changed = false;
488
 
489
    if (stage[0] != window.screenX) {
490
 
491
        delta[0] = (window.screenX - stage[0]) * 50;
492
        stage[0] = window.screenX;
493
        changed = true;
494
 
495
    }
496
 
497
    if (stage[1] != window.screenY) {
498
 
499
        delta[1] = (window.screenY - stage[1]) * 50;
500
        stage[1] = window.screenY;
501
        changed = true;
502
 
503
    }
504
 
505
    if (stage[2] != window.innerWidth) {
506
 
507
        stage[2] = window.innerWidth;
508
        changed = true;
509
 
510
    }
511
 
512
    if (stage[3] != window.innerHeight) {
513
 
514
        stage[3] = window.innerHeight;
515
        changed = true;
516
 
517
    }
518
 
519
    return changed;
520
 
521
}

上面mouseDrag方法就实现了鼠标拖拽甩动小球的功能,这也是该动画最重要的方法。全部代码可以下载源码来研究。源码下载>>

关于作者


发表评论