Everything is, in fact, quite simple. There are several things to do:
- define handler for clicking on the left mouse button (LMB)
- define handler for unplugging
- define a mouse movement handler
In the handler for pressing the LMB, you need to change the mode (draggin <- true) and save the current position of the center of the canvas (relative to which the pendulum is drawn) and the direction angle from the center of the canvas to the position of the mouse cursor, as well as the current angle of rotation of the pendulum.
In the handler for depressing the LMB, you need to change the mode (draggin <- false) and reset the current speed and acceleration so as not to break the physics of the process.
In the mouse movement handler, you need to understand what mode we are in now (if draggin === true , then you need to handle the movement).
During motion processing, a new direction angle to the cursor is calculated and the difference between the current and initial direction angles is subtracted from the saved position of the pendulum.
You also need to remember to forbid recalculating mechanics when moving the starting point.
My version of the code with comments is given below:
var canvas = document.getElementById("canvas"); var canvas2 = document.getElementById("canvas2"); var ctx = canvas.getContext("2d"); var ctx2 = canvas2.getContext("2d"); var h = canvas.height = 500; var w = canvas.width = 600; var h2 = canvas2.height = 300; var w2 = canvas2.width = 800; var draggin = false; // Π ΡΠ΅ΠΆΠΈΠΌΠ΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ var dragX = 0; // Π¦Π΅Π½ΡΡ ΠΊΠ°Π½Π²Π°ΡΠ°, ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ Π±ΡΠ΄Π΅ΠΌ ΡΡΠΈΡΠ°ΡΡ ΠΏΠΎΠ²ΠΎΡΠΎΡ var dragY = 0; var dragPhi = 0; // Π£Π³ΠΎΠ» ΠΌΠ°ΡΡΠ½ΠΈΠΊΠ° ΠΏΡΠΈ Π½Π°ΠΆΠ°ΡΠΈΠΈ var dragKsy = 0; // ΠΠ°ΡΠ°Π»ΡΠ½ΡΠΉ ΡΠ³ΠΎΠ» ΠΏΡΠΈ mousedown ctx.translate(w / 2, h / 2); ctx2.translate(0, h2 / 2); ctx2.beginPath(); ctx2.moveTo(0, 0); var initPhi = Math.PI * 0.2; var L = 200; var dt = 1 / 60; var g = 1500; var t = 0; bob = { phi: initPhi, v: 0, a: 0 }; //getPos - ΠΎΠΏΡΠ΅Π΄Π΅Π»Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ·ΠΈΡΠΈΠΈ ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΡΠ΅Π½ΡΡΠ° ΡΠ»Π΅ΠΌΠ΅Π½ΡΠ° function getPos(el) { var rect = el.getBoundingClientRect(); return { x: rect.left + rect.width / 2.0, y: rect.top + rect.height / 2.0 }; } // ΠΠΏΡΠ΅Π΄Π΅Π»ΡΠ΅ΠΌ ΡΠ³ΠΎΠ» ΠΏΠΎΠ²ΠΎΡΠΎΡΠ° ΠΎΡΠ½ΠΎΡΠΈΡΠ΅Π»ΡΠ½ΠΎ ΡΠ΅Π½ΡΡΠ° ΠΊΠ°Π½Π²Π°ΡΠ° function calcKsy(evt) { var deltaX = evt.clientX - dragX; var deltaY = evt.clientY - dragY; var Ksy = Math.atan2(deltaY, deltaX); return Ksy; } // ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅ΠΌ ΡΠΎΠ±ΡΡΠΈΠ΅ Π½Π°ΠΆΠ°ΡΠΈΡ Π½Π° Π»Π΅Π²ΡΡ ΠΊΠ½ΠΎΠΏΠΊΡ ΠΌΡΡΠΈ canvas.addEventListener("mousedown", function(evt) { var gpc = getPos(canvas); dragX = gpc.x; dragY = gpc.y; draggin = true; dragPhi = bob.phi; dragKsy = calcKsy(evt); }); // ΠΠΎΠ²ΠΎΡΠ°ΡΠΈΠ²Π°Π΅ΠΌ ΠΌΠ°ΡΡΠ½ΠΈΠΊ Π½Π° ΡΠ³ΠΎΠ» ΠΌΠ΅ΠΆΠ΄Ρ Π½Π°ΡΠ°Π»ΡΠ½ΡΠΌ ΡΠ³Π»ΠΎΠΌ ΠΏΡΠΈ mousedown // ΠΈ ΡΠ΅ΠΊΡΡΠΈΠΌ ΠΏΠΎΡΠ»Π΅ ΠΏΠ΅ΡΠ΅ΠΌΠ΅ΡΠ΅Π½ΠΈΡ ΠΊΡΡΡΠΎΡΠ° canvas.addEventListener("mousemove", function(evt) { if(draggin){ var Ksy = calcKsy(evt); bob.phi = dragPhi - Ksy + dragKsy; } }); // ΠΠ±ΡΠ°Π±Π°ΡΡΠ²Π°Π΅ΠΌ ΡΠΎΠ±ΡΡΠΈΠ΅ ΠΎΡΠΆΠ°ΡΠΈΡ Π»Π΅Π²ΠΎΠΉ ΠΊΠ½ΠΎΠΏΠΊΠΈ ΠΌΡΡΠΈ canvas.addEventListener("mouseup", function(evt) { draggin = false; bob.v = 0; bob.a = 0; }); function drawPendulum() { ctx.beginPath() ctx.arc(Math.sin(bob.phi) * L, Math.cos(bob.phi) * L, 10, 0, 2 * Math.PI) ctx.fill() ctx.moveTo(0, 0) ctx.lineTo(Math.sin(bob.phi) * L, Math.cos(bob.phi) * L) ctx.stroke() } function update() { bob.a = -(g / L) * Math.sin(bob.phi) bob.v += bob.a * dt bob.phi += bob.v * dt t += dt } function drawGraph() { ctx2.lineTo(t * 20, (bob.phi % Math.PI) * 20) ctx2.stroke() } function draw() { ctx.clearRect(-w / 2, -h / 2, w, h) drawPendulum() // ΠΡΠ»ΠΈ ΠΌΡ Π½Π΅ Π² ΡΠ΅ΠΆΠΈΠΌΠ΅ ΠΏΠ΅ΡΠ΅ΡΠ°ΡΠΊΠΈΠ²Π°Π½ΠΈΡ, ΡΠΎ ΠΊΠ°ΡΠ°Π΅ΠΌ ΠΌΠ°ΡΡΠ½ΠΈΠΊ if (draggin === false) { update() } drawGraph() requestAnimationFrame(draw) } draw()
* { padding: 0; margin: 0; } canvas { display: block; margin: 0 auto; cursor: move; cursor: grab; cursor: -moz-grab; cursor: -webkit-grab; }
<canvas id="canvas"></canvas> <canvas id="canvas2" style="background: #eee"></canvas>