horizontal-motion.js (5020B)
1 const App = { 2 data: function() { 3 return { 4 // Input data 5 mass: 5, // The object's mass (Kg) 6 force: 5, // The force acting on the object (N) 7 staticFriction: 0, // The coefficient of static friction 8 kineticFriction: 0, // The coefficient of kinetic friction 9 gravity: 9.8, // The acceleration due to gravity (m/s/s) 10 11 // Output data 12 time: 0, // The time (s) 13 position: 0, // The object's current position (m) 14 velocity: 0, // The object's current velocity (m/s) 15 16 // Simulation properties 17 active: false, // Whether the simulation is active 18 refreshRate: 0.01, // The simulation refresh rate (s) 19 intervalId: null, // The value returned by setInterval 20 infoVisible: false, 21 } 22 }, 23 computed: { 24 /** 25 * The force of static friction when the object is at rest 26 */ 27 staticFrictionForce: function() { 28 let maxForce = this.staticFriction * this.mass * this.gravity; 29 if (Math.abs(this.force) <= maxForce) return -1 * this.force; 30 else return Math.sign(this.force) * -1 * maxForce; 31 }, 32 33 /** 34 * The force of kinetic friction when the object is in motion 35 */ 36 kineticFrictionForce: function() { 37 let value = this.kineticFriction * this.mass * this.gravity; 38 if (this.velocity > 0) return -1 * value; 39 else if (this.velocity < 0) return value; 40 else if (this.force > 0) return -1 * value; 41 else if (this.force < 0) return value; 42 else return 0; 43 }, 44 45 /** 46 * The net force on the object 47 */ 48 netForce: function() { 49 if (this.velocity === 0 && (Math.abs(this.staticFrictionForce) >= Math.abs(this.force) || Math.abs(this.kineticFrictionForce) >= Math.abs(this.force))) { 50 // The object is at rest and cannot overcome friction 51 return 0; 52 } 53 else { 54 return this.force + this.kineticFrictionForce; 55 } 56 }, 57 58 /** 59 * The acceleration of the object 60 */ 61 acceleration: function() { 62 return this.netForce / this.mass; 63 }, 64 65 /** 66 * The position of reference frame indicators 67 */ 68 references: function() { 69 return [ 70 (-1 * this.position) % 1, // 1m 71 (-1 * this.position) % 0.1, // 0.1m 72 ]; 73 }, 74 }, 75 methods: { 76 /** 77 * Handle a keyup event (implements keyboard shortcuts) 78 * @param {object} e - The event args 79 */ 80 keyup: function(e) { 81 if (e.key === "Escape") { 82 if (this.infoVisible) this.infoVisible = false; 83 else window.location.href = "../"; 84 } 85 }, 86 87 /** 88 * Toggle whether the simulation is active 89 */ 90 toggle: function() { 91 this.active = !this.active; 92 if (this.active) this.intervalID = setInterval(this.update, this.refreshRate * 1000); 93 else clearInterval(this.intervalID); 94 }, 95 96 /** 97 * Reset the simulation 98 */ 99 reset: function() { 100 this.time = 0; 101 this.position = 0; 102 this.velocity = 0; 103 }, 104 105 /** 106 * Update the simulation 107 */ 108 update: function() { 109 // Update time 110 this.time += this.refreshRate; 111 112 // Get updated velocity 113 let newVelocity = this.velocity + (this.acceleration * this.refreshRate) 114 115 // Correct velocity for friction 116 if (Math.sign(this.velocity) !== Math.sign(newVelocity)) { 117 // The object was recently at rest 118 this.velocity = 0; 119 120 if (this.acceleration !== 0) { 121 // The object was able to overcome friction 122 this.velocity = newVelocity; 123 } 124 } 125 else { 126 // The object was not recently at rest 127 this.velocity = newVelocity; 128 } 129 130 // Update position 131 this.position += (this.velocity * this.refreshRate); 132 }, 133 }, 134 created: function() { 135 // Add keyup handler 136 window.addEventListener("keyup", this.keyup); 137 }, 138 destroyed: function() { 139 // Remove keyup handler 140 window.removeEventListener("keyup", this.keyup); 141 }, 142 } 143 144 145 146 // Create Vue app 147 function createApp() { 148 // Create app 149 Vue.createApp(App).mount("#app"); 150 151 // Unhide app divs 152 document.getElementById("input").hidden = false; 153 document.getElementById("output").hidden = false; 154 document.getElementById("data").hidden = false; 155 document.getElementById("info").hidden = false; 156 }