3

I have to create some falling snowflakes in javascript, but they have change current path if they get collision with other flake. Something like on this image: enter image description here

Here is my current code: http://codepen.io/wojtek1150/pen/QyaYdY

var flakePositions = [[]];  
var temp = 0;
// snowflake proto
function Snowflake() {
    this.pos = new Physics();
    // snowflake guid
    this.id = '';
    // inits
    this.MAX_X_START_POS = 100;
    this.X_START_OFFSET = 0;
    this.MAX_Y_START_POS = 50;
    this.Y_START_OFFSET = -50;
    this.MAX_X_SPEED = 4;
    this.MAX_Y_SPEED = 1.2;

    // use to get sin && cos
    this.animationStepsCounter = 0
    this.fallFactor = 100;
    // snowflake html
    this.getId = function () {
        if (this.id == '') {
            this.id = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,
                function (c) {
                    var r = crypto.getRandomValues(new Uint8Array(1))[0] % 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
                    return v.toString(16);
                });
        }
        return this.id;
    }
    this.initalize = function () {   
        temp++;
        //var size = 5 + Math.random() * 20;
        var size = 20;
        this.flakeDOM.style.width = size + "px";
        this.flakeDOM.style.height = size + "px";
        this.flakeDOM.style.opacity = Math.random();
        this.pos.x = (Math.random() * this.MAX_X_START_POS);
        this.pos.y = this.Y_START_OFFSET+(Math.random() * this.MAX_Y_START_POS);
        this.pos.xSpeed = Math.random() * this.MAX_X_SPEED* Math.sign(-0.5 + Math.random());
        this.pos.ySpeed = Math.random() * this.MAX_Y_SPEED;
      
        //create array
        flakePositions[temp] = [];      
        flakePositions[temp]['id'] = this.id;
        flakePositions[temp]['x'] = this.flakeDOM.style.top;
        flakePositions[temp]['y'] = this.flakeDOM.style.left;
        flakePositions[temp]['width'] = this.flakeDOM.style.width;
        flakePositions[temp]['height'] = this.flakeDOM.style.height;
        flakePositions[temp]['xspeed'] = this.pos.xSpeed;
        flakePositions[temp]['yspeed'] = this.pos.ySpeed
    }
    this.move = function () {
      this.flakeDOM.style.top = (this.pos.y+=this.pos.ySpeed) + "px";
        this.flakeDOM.style.left = (this.pos.x += Math.sin(this.animationStepsCounter/this.fallFactor) * this.pos.xSpeed) + "px";
        this.animationStepsCounter += this.pos.ySpeed;  
      
        //update array
        flakePositions[temp]['x'] = this.flakeDOM.style.top;
        flakePositions[temp]['y'] = this.flakeDOM.style.left;
      
            
        //check position with rest
        for (var i = 1, len = flakePositions.length; i < len-1; i++) {
                        
            var rect1 = flakePositions[i];
            var rect1d = rect1['id'];
            var rect1sx = rect1['xspeed'];
            var rect1sy = rect1['yspeed'];
            var rect1x = parseInt(rect1['x']);
            var rect1y = parseInt(rect1['y']);
            
            for (var j = 2, len = flakePositions.length; j < len; j++) {
                var rect2 = flakePositions[j];
                
                var rect2d = rect2['id'];
                var rect2x = parseInt(rect2['x']);
                var rect2y = parseInt(rect2['y']);
                
                //if(rect1x == rect2x && rect1y == rect2y)
                if(rect1x < rect2x + 10 && rect1x + 10 > rect2x &&
                rect1y < rect2y  + 10 && 10 + rect1y  > rect2y )
                {
                    console.log('collision detected');
                    var t = document.getElementById(rect1d);
                    t.style.top = t.style.top+rect1sx+10;
                    t.style.left = t.style.left+rect1sy-10;
                }
            }
            
            
        }
    }
}


    

function Physics() {
    // pos
    this.x = 0;
    this.y = 0;
    this.z = 0;
    // speed
    this.xSpeed = 0;
    this.ySpeed = 0;
    this.zSpeed = 0;
    // acceleration
    this.xAccel = 1;
    this.yAccel = 1;
    this.zAccel = 1;
}

snowflakes = new Array();
var interval = 0;

function makeThisBoom() {
    // snowflakes container
    snowfield = document.getElementById('snow');
    // snowflakes count
    snoflakesCount = 20;
    for (var i = 0; i < snoflakesCount; i++) {
        snowflakes[i] = new Snowflake();
        var flake = document.createElement('div');
        snowflakes[i].flakeDOM = flake;
        flake.id = snowflakes[i].getId();
        flake.classList.add('sf');
        snow.appendChild(flake);
        snowflakes[i].initalize();
        snowflakes[i].move();
    }    
    interval = setInterval(anime,50);
}

function anime() {
    for (var flake of snowflakes) {
        flake.move();
    }
}

function setInterface() {
    document.getElementById('startstop').onclick = function () {
        if (interval != 0) {
            clearInterval(interval);
            interval = 0;
        } else interval = setInterval(anime, 50);
    }
}
document.addEventListener("DOMContentLoaded", makeThisBoom);
document.addEventListener("DOMContentLoaded", setInterface);
.sf{
  position:absolute;
  z-index:9999999;
  /*background: -moz-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%, rgba(255,255,255,0) 100%);
  background: -webkit-radial-gradient(center, ellipse cover, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
  background: radial-gradient(ellipse at center, rgba(255,255,255,1) 0%,rgba(255,255,255,0) 100%);
  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#00ffffff',GradientType=1 );
  */
  border-radius:50%;
  display:block;
  width:20px; height:20px;
  /* FOR DEV ONLY */
  background:#FFF;
  opacity:1!important;
}
body{
  background:#222;
  overflow:hidden;
}
#snow {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
}
#startstop{
  width:100px;
  height:30px;
  border:0;
  background:rgb(61, 95, 123);
  color:#FFF;
  outline:none;
}
<button id="startstop">Start/stop</button>
 <div id="snow"> </div>

I already know how to get positions and I got if statement to check if there is any collision. But I don't know how to change path in proper way, even just bounce it :(

Any suggestions?

Wojciech Parys
  • 339
  • 2
  • 18
  • 1
    Did you look at this question? It might be helpful. http://stackoverflow.com/questions/345838/ball-to-ball-collision-detection-and-handling?rq=1 – jpw Jan 21 '16 at 21:09
  • 1
    Why reinvent the wheels when you can just use a [well established physics library](http://box2d-js.sourceforge.net/)? – Derek 朕會功夫 Jan 21 '16 at 21:12
  • I can't use ready-made solutions. This is exercise from school. I just want to know how to create it to get better ;) – Wojciech Parys Jan 21 '16 at 22:08

1 Answers1

1

One of the key things for you to think about when doing this, are the cost/reward of implementing this kind of feature. I don't think these collisions will help you create the illusion of snowfalling. When the snowflakes in your current iteration miss each other it gives the illusion of 3d. If they were to hit each other and bounce it might give the incorrect illusion of balls falling in a 2d plane.

That being said, to implement what you are asking without using a library would be a huge time sync. I would recommend taking a look at PhysicsJS or matter-js.

Below you can see the function I personally use in a library I was working on. You can adapt most of it for your use. The truth is this a complicated ask.

define( 'detect/detectCircleCircleCollision' , [ 'lib/underscore' ] , function ( _ ) {

    return function detectCircleCircleCollision (   circlePositionA,
                                                    circleRadiusA,
                                                    circleDisplacementA,
                                                    circlePositionB,
                                                    circleRadiusB,
                                                    circleDisplacementB,
                                                    boolean,
                                                    normalBody ) {

        boolean = _.isUndefined( boolean ) || boolean ? true : false;

        normalBody = _.isUndefined( normalBody ) || normalBody ? true : false;

        var
            relativePosition            = circlePositionA.subtract.new( circlePositionB ),
            combineRadius               = circleRadiusA + circleRadiusB,
            relativePositionDotProduct  = relativePosition.lengthSqr(),
            relativeDisplacement        = circleDisplacementA.subtract.new( circleDisplacementB ),
            a,b,c,r,t,newCircleOnePosition,newCircleTwoPosition,newCirclePositionDifference,collisionPoint;

        if ( relativePositionDotProduct < combineRadius * combineRadius ) {

            if ( boolean ) return true;

            return collision(   0,//Time
                                circlePositionB.add.new( vector( relativePosition ).magnitude.set( circleRadiusA ) ),//point
                                relativePosition.normalize(),//Normal
                                normalBody,//normalbody
                                vector( relativePosition ).magnitude.set( circleRadiusA + circleRadiusB - relativePosition.magnitude() ) );//intersection

        }

        a = relativeDisplacement.dotProduct( relativeDisplacement );
        b = relativePosition.dotProduct( relativePosition );
        c = relativePositionDotProduct - combineRadius * combineRadius;
        r = b * b - a * c;

        if ( r < 0 ) return false;

        t = -b - r * r / a;

        if ( t > 1 || t < 0 ) return false;
        else if ( boolean ) return true;

        newCircleOnePosition        = circleDisplacementA.scale.new( t ).add( circlePositionA );
        newCircleTwoPosition        = circleDisplacementB.scale.new( t ).add( circlePositionB );
        newCirclePositionDifference = newCircleTwoPosition.subtract.new( newCircleOnePosition ).normalize();
        collisionPoint              = newCirclePositionDifference.scale.new( circleRadiusA );

        return collision(   t,
                            collisionPoint.add( newCircleOnePosition ),
                            newCirclePositionDifference,
                            normalBody,
                            false );



    };

} );
hyphnKnight
  • 303
  • 2
  • 6
  • I don't need to create illusion of snowfalling. I just need to learn how to create something like You send (eg. PhysicsJS). It's project for school. Falling snow is the idea from my lecturer. So I can't do it with plugins :( – Wojciech Parys Jan 21 '16 at 22:04
  • ok, So sorry it took a while to respond. I have a function to do this for you, I currently don't have a ton of time to explain it but here you go. – hyphnKnight Jan 26 '16 at 01:02