Skip to main content
5 of 9
deleted 296 characters in body
Jamal
  • 34.9k
  • 13
  • 133
  • 237

Bouncing stacking boxes in javascript

     var envelopeHeight = 100;
        var fallRange = 8.5;
        var fallMidpoint = 75;
        var maxOnScreen = 6;
        var frequency = 4500; //time between falls
        var initialVelocity = 0.2;
        
        var gravity = 0.6;
        var friction = 0.97;
        var dampening = 2;
        
        var lastEnvelope = 0;
        var envelopes = [];
        
        function onFrame() {
            var onScreen = 0;
            for (var i = 0; i < envelopes.length; i++) {
                if (!envelopes[i].dead) {
                    onScreen++;
                }
                envelopes[i].updateSelf();
            }
            if (Date.now() > lastEnvelope + frequency && onScreen <= maxOnScreen) {
                var dropX = view.bounds.width * (((Math.random() * fallRange) + fallMidpoint) / 100);
                var envelope = new Path.Rectangle(new Point(dropX, -envelopeHeight), 100, envelopeHeight);
                envelope.position = new Point(dropX, -envelopeHeight);
                envelope.fillColor = {
                    hue: Math.random() * 360,
                    saturation: 1,
                    brightness: 1
                }
                envelope.velocity = initialVelocity;
                envelope.removed = false;
                envelope.updateSelf = function() {
                    if (this.bounds.bottom > view.bounds.height + 1000) {
                        this.remove();
                        envelopes.splice(i,1);
                        return;
                    };
                    function bounce() {
                        if (this.velocity > dampening) {
                            this.velocity -= dampening;
                        } else {
                            this.velocity = 0;
                        }
                        this.velocity = -this.velocity;
                    };          
        
                    if (this.bounds.bottom >= view.bounds.height && !this.dead) {
                        bounce.apply(this);
                    }
        
                    for (var i = 0;i<envelopes.length;i++) {
                        var bottomLeft = new Point(this.bounds.left, this.bounds.bottom);
                        var bottomRight = new Point(this.bounds.left + this.bounds.width, this.bounds.bottom);
                        if (envelopes[i].hitTest(bottomLeft) && envelopes[i].id!=this.id) {
                            bounce.apply(this);
                        };
                        if (envelopes[i].hitTest(bottomRight) && envelopes[i].id!=this.id) {
                            bounce.apply(this);
                        };
                    }
        
                    this.position.y += this.velocity;
                    this.velocity += gravity;
                    this.velocity *= friction;          
                }
                envelopes.push(envelope);
                lastEnvelope = Date.now();
            };
        };
        
        function onMouseDown() {
            for (var i = 0;i<envelopes.length;i++) {
                envelopes[i].dead = true;
            }
        }
#envelopes {
  height: 100%;
  width: 100%
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/paper.js/0.9.18/paper-full.min.js"></script>

<script type="text/paperscript" canvas="envelopes" resize>
         var envelopeHeight = 100;
        var fallRange = 8.5;
        var fallMidpoint = 75;
        var maxOnScreen = 6;
        var frequency = 4500; //time between falls
        var initialVelocity = 0.2;
        
        var gravity = 0.6;
        var friction = 0.97;
        var dampening = 2;
        
        var lastEnvelope = 0;
        var envelopes = [];
        
        function onFrame() {
            var onScreen = 0;
            for (var i = 0; i < envelopes.length; i++) {
                if (!envelopes[i].dead) {
                    onScreen++;
                }
                envelopes[i].updateSelf();
            }
            if (Date.now() > lastEnvelope + frequency && onScreen <= maxOnScreen) {
                var dropX = view.bounds.width * (((Math.random() * fallRange) + fallMidpoint) / 100);
                var envelope = new Path.Rectangle(new Point(dropX, -envelopeHeight), 100, envelopeHeight);
                envelope.position = new Point(dropX, -envelopeHeight);
                envelope.fillColor = {
                    hue: Math.random() * 360,
                    saturation: 1,
                    brightness: 1
                }
                envelope.velocity = initialVelocity;
                envelope.removed = false;
                envelope.updateSelf = function() {
                    if (this.bounds.bottom > view.bounds.height + 1000) {
                        this.remove();
                        envelopes.splice(i,1);
                        return;
                    };
                    function bounce() {
                        if (this.velocity > dampening) {
                            this.velocity -= dampening;
                        } else {
                            this.velocity = 0;
                        }
                        this.velocity = -this.velocity;
                    };          
        
                    if (this.bounds.bottom >= view.bounds.height && !this.dead) {
                        bounce.apply(this);
                    }
        
                    for (var i = 0;i<envelopes.length;i++) {
                        var bottomLeft = new Point(this.bounds.left, this.bounds.bottom);
                        var bottomRight = new Point(this.bounds.left + this.bounds.width, this.bounds.bottom);
                        if (envelopes[i].hitTest(bottomLeft) && envelopes[i].id!=this.id) {
                            bounce.apply(this);
                        };
                        if (envelopes[i].hitTest(bottomRight) && envelopes[i].id!=this.id) {
                            bounce.apply(this);
                        };
                    }
        
                    this.position.y += this.velocity;
                    this.velocity += gravity;
                    this.velocity *= friction;          
                }
                envelopes.push(envelope);
                lastEnvelope = Date.now();
            };
        };
        
        function onMouseDown() {
            for (var i = 0;i<envelopes.length;i++) {
                envelopes[i].dead = true;
            }
        }
       </script>

<canvas id="envelopes"></canvas>

  1. I'm not sure if it would be better to make the envelope into an extension of the Rectangle constructor.
  2. The physics seem a bit dodgy, it works well most of the time, but sometimes the boxes overlap and sometimes the bouncing doesn't work properly.
var envelopeHeight = 100;
var fallRange = 8.5;
var fallMidpoint = 75;
var maxOnScreen = 6;
var frequency = 4500; //time between falls
var initialVelocity = 0.2;

var gravity = 0.6;
var friction = 0.97;
var dampening = 2;

var lastEnvelope = 0;
var envelopes = [];

function onFrame() {
    var onScreen = 0;
    for (var i = 0; i < envelopes.length; i++) {
        if (!envelopes[i].dead) {
            onScreen++;
        }
        envelopes[i].updateSelf();
    }
    if (Date.now() > lastEnvelope + frequency && onScreen <= maxOnScreen) {
        var dropX = view.bounds.width * (((Math.random() * fallRange) + fallMidpoint) / 100);
        var envelope = new Path.Rectangle(new Point(dropX, -envelopeHeight), 100, envelopeHeight);
        envelope.position = new Point(dropX, -envelopeHeight);
        envelope.fillColor = {
            hue: Math.random() * 360,
            saturation: 1,
            brightness: 1
        }
        envelope.velocity = initialVelocity;
        envelope.removed = false;
        envelope.updateSelf = function() {
            if (this.bounds.bottom > view.bounds.height + 1000) {
                this.remove();
                envelopes.splice(i,1);
                return;
            };
            function bounce() {
                if (this.velocity > dampening) {
                    this.velocity -= dampening;
                } else {
                    this.velocity = 0;
                }
                this.velocity = -this.velocity;
            };          

            if (this.bounds.bottom >= view.bounds.height && !this.dead) {
                bounce.apply(this);
            }

            for (var i = 0;i<envelopes.length;i++) {
                var bottomLeft = new Point(this.bounds.left, this.bounds.bottom);
                var bottomRight = new Point(this.bounds.left + this.bounds.width, this.bounds.bottom);
                if (envelopes[i].hitTest(bottomLeft) && envelopes[i].id!=this.id) {
                    bounce.apply(this);
                };
                if (envelopes[i].hitTest(bottomRight) && envelopes[i].id!=this.id) {
                    bounce.apply(this);
                };
            }

            this.position.y += this.velocity;
            this.velocity += gravity;
            this.velocity *= friction;          
        }
        envelopes.push(envelope);
        lastEnvelope = Date.now();
    };
};

function onMouseDown() {
    for (var i = 0;i<envelopes.length;i++) {
        envelopes[i].dead = true;
    }
}
Tom
  • 431
  • 1
  • 4
  • 10