/** * PhysicsJS by Jasper Palfree <wellcaffeinated.net> * http://wellcaffeinated.net/PhysicsJS * * Supermarket catastrophy */ Physics.behavior('demo-mouse-events', function (parent) { return { init: function (options) { var self = this; this.mousePos = Physics.vector(); this.mousePosOld = Physics.vector(); this.offset = Physics.vector(); this.el = $(options.el).on({ mousedown: function (e) { var offset = $(this).offset(); self.mousePos.set(e.pageX - offset.left, e.pageY - offset.top); var body = self._world.findOne({ $at: self.mousePos }); if (body) { // we're trying to grab a body // fix the body in place body.fixed = true; // remember the currently grabbed body self.body = body; // remember the mouse offset self.offset.clone(self.mousePos).vsub(body.state.pos); return; } self.mouseDown = true; }, mousemove: function (e) { var offset = $(this).offset(); self.mousePosOld.clone(self.mousePos); // get new mouse position self.mousePos.set(e.pageX - offset.left, e.pageY - offset.top); }, mouseup: function (e) { var offset = $(this).offset(); self.mousePosOld.clone(self.mousePos); self.mousePos.set(e.pageX - offset.left, e.pageY - offset.top); // release the body if (self.body) { self.body.fixed = false; self.body = false; } self.mouseDown = false; } }); }, connect: function (world) { // subscribe the .behave() method to the position integration step world.subscribe('integrate:positions', this.behave, this); }, disconnect: function (world) { // unsubscribe when disconnected world.unsubscribe('integrate:positions', this.behave); }, behave: function (data) { if (this.body) { // if we have a body, we need to move it the the new mouse position. // we'll also track the velocity of the mouse movement so that when it's released // the body can be "thrown" this.body.state.pos.clone(this.mousePos).vsub(this.offset); this.body.state.vel.clone(this.body.state.pos).vsub(this.mousePosOld).vadd(this.offset).mult(1 / 30); this.body.state.vel.clamp({ x: -1, y: -1 }, { x: 1, y: 1 }); return; } if (!this.mouseDown) { return; } // if we don't have a body, then just accelerate // all bodies towards the current mouse position var bodies = data.bodies // use a scratchpad to speed up calculations , scratch = Physics.scratchpad(), v = scratch.vector(), body; for (var i = 0, l = bodies.length; i < l; ++i) { body = bodies[i]; // simple linear acceleration law towards the mouse position v.clone(this.mousePos) .vsub(body.state.pos) .normalize() .mult(0.001); body.accelerate(v); } scratch.done(); } }; }); Physics(function (world) { var $win = $(window), viewWidth = $win.width(), viewHeight = 400, renderer = Physics.renderer('canvas', { el: 'viewport', width: viewWidth, height: viewHeight, meta: true, // debug:true, styles: { 'circle': { strokeStyle: 'hsla(60, 37%, 17%, 1)', lineWidth: 1, fillStyle: 'hsla(60, 37%, 57%, 0.8)', angleIndicator: 'hsla(60, 37%, 17%, 0.4)' }, 'convex-polygon': { strokeStyle: 'hsla(60, 37%, 17%, 1)', lineWidth: 1, fillStyle: 'hsla(60, 47%, 37%, 0.8)', angleIndicator: 'none' } } }), edgeBounce, rigidConstraints = Physics.behavior('rigid-constraint-manager', { targetLength: 8 }) // bounds of the window , viewportBounds = Physics.aabb(0, 0, viewWidth, viewHeight); // add the renderer world.add(renderer); // render on each step world.subscribe('step', function () { world.render(); }); world.add(Physics.behavior('demo-mouse-events', { el: '#viewport' })); world.subscribe('integrate:positions', function () { var constraints = rigidConstraints.getConstraints(), c, threshold = 40, scratch = Physics.scratchpad(), v = scratch.vector(), len; for (var i = 0, l = constraints.length; i < l; ++i) { c = constraints[i]; len = v.clone(c.bodyB.state.pos).vsub(c.bodyA.state.pos).norm(); // break the constraint if above threshold if ((!c.bodyA.fixed && !c.bodyB.fixed) && (len - c.targetLength) > threshold) { rigidConstraints.remove(i); } } scratch.done(); // higher priority than constraint resolution }, null, 100); // render world.subscribe('render', function (data) { var renderer = data.renderer, constraints = rigidConstraints.getConstraints(), c; for (var i = 0, l = constraints.length; i < l; ++i) { c = constraints[i]; renderer.drawLine(c.bodyA.state.pos, c.bodyB.state.pos, 'hsla(60, 37%, 27%, 1)'); } }); // add gravity world.add(Physics.behavior('constant-acceleration')); world.pause(); // subscribe to ticker to advance the simulation Physics.util.ticker.subscribe(function (time, dt) { world.step(time); }); // start the ticker Physics.util.ticker.start(); $(function () { // the "cloth" var cloth = []; for (var row = 0, l = 35; row < l; ++row) { for (var col = 0, lcol = 35; col < lcol; ++col) { cloth.push( Physics.body('circle', { x: 8 * col + (viewWidth - l * 8) / 2, y: 8 * row + 10, radius: 4, hidden: true })); if (col > 0) { // horizontal rigidConstraints.constrain(cloth[lcol * row + col - 1], cloth[lcol * row + col]); } if (row > 0) { // vertical rigidConstraints.constrain(cloth[lcol * row + col], cloth[lcol * (row - 1) + col]); } else { cloth[lcol * row + col].fixed = true; } } } // add things to world world.add(cloth); world.add(rigidConstraints); world.unpause(); }); });
<canvas id="viewport"></canvas>
html, body { background: #121212; margin: 0; height: 100%; } .pjs-meta { display: none; } #viewport { position: absolute; top:0; left: 0; bottom: 0; right: 0; }