Взаимодействие двух заряженных тел (Закон Кулона) — различия между версиями

Материал из Department of Theoretical and Applied Mechanics
Перейти к: навигация, поиск
Строка 12: Строка 12:
 
*Активное бесконечное построение позволяет следить за телами без остановки, но автоматически меняет начальные параметры в таблицах
 
*Активное бесконечное построение позволяет следить за телами без остановки, но автоматически меняет начальные параметры в таблицах
 
*Активная кнопка слежения за телом перемещает его и фиксирует в центре поля, анимация происходит относительно зафиксированного тела
 
*Активная кнопка слежения за телом перемещает его и фиксирует в центре поля, анимация происходит относительно зафиксированного тела
*Обратная прор
+
*Обратная анимация позволяет совершить перемотку от последнего положения тел до начальных условий
 +
 
 
== Разработка ==
 
== Разработка ==
 
<div class="mw-collapsible mw-collapsed">
 
<div class="mw-collapsible mw-collapsed">

Версия 11:27, 19 декабря 2016

Виртуальная лаборатория > Динамика взаимодействующих частиц

Управление

Работа с полем

  • Возможность перетаскивания тел и рабочего пространства по клику левой кнопкой мыши
  • Прорисовка векторов скоростей по клику правой кнопкой мыши
  • Приближение к точке колёсиком мыши (отдаление)
  • При клике по любому телу во время паузы скорости обоих тел сохраняются в таблицу

Кнопки

  • Кнопка "Запустить" - запуск анимации с начальными параметрами, взятыми из таблицы
  • Кнопка "Остановить/Продолжить" - пауза и продолжение уже запущенной анимации
  • Активное бесконечное построение позволяет следить за телами без остановки, но автоматически меняет начальные параметры в таблицах
  • Активная кнопка слежения за телом перемещает его и фиксирует в центре поля, анимация происходит относительно зафиксированного тела
  • Обратная анимация позволяет совершить перемотку от последнего положения тел до начальных условий

Разработка

Текст программы на языке JavaScript (разработчик Дрепин Михаил):

Файл "Code.js"

  1 window.addEventListener("load", program_code, false);
  2 var ctx; //Поле canvas_example
  3 var xx1, yy1, xx2, yy2; //координаты тела в данный момент - в статичной прорисовке
  4 var koef; //коэфициент масштаба для прорисовки сетки, тел и т.д.
  5 var w; //ширина поля
  6 var h;//высота поля
  7 var vrem=1;//переменная для изменения скорости отрисовки
  8 var kx=0, ky=0; //переменная положения мыши для вектора скорости
  9 var dx=0, dy=0;  //сдвиг поля - обнуляемая переменная
 10 var px=0, py=0; //сдвиг поля (по X и по Y)
 11 var intervalID; //переменная для функции setInterval (для остановки прорисовки)
 12 var Switch=0; //переменная для кнопки - бесконечное/конечное постоение
 13 var second_obj=0; //переменная-индикатор слежения за 1 телом ("==1" - активно, "==0" - отключено)
 14 var first_obj=0; //переменная-индикатор слежения за 2 телом ("==1" - активно, "==0" - отключено)
 15 var idi=0; //переменная для кнопок запустить/пауза/продолжить: 
 16 /*idi: 
 17   "==2" - активна прорисовка анимации (нажата "Запустить");
 18   "==1" - анимация приостановлена (нажата "Остановить");
 19   "==0" - анимация полностью остановлена или не запускалась
 20 */
 21 var n = 0; //номер активного элемента массива (X/Y/T/V/A)
 22 var t = []; //массив "времени"
 23 var T=100; //максимальное "время"
 24 var a1x = a2x = a1y = a2y = []; //массивы ускорений
 25 var vx1 = vx2 = vy1 = vy2 = []; //массивы скоростей
 26 var x1 = y1 = x2 = y2 = []; //массивы координат
 27 var m1, m2 //масса синего/красного тела
 28 var q1, q2, q; //Заряды тел (синего/красного/произведение)
 29 const kulkof=8.9875517873681764*3.1; //Н·м2/Кл2 - константа закона Кулона
 30 const dt=0.0001; // Шаг времен
 31 document.oncontextmenu = function (e) {return false}; //Заблокировать контекстное меню
 32 function program_code() {     //Часть кода, запускающаяся при загрузке страницы
 33   ctx = canvas_example.getContext("2d");
 34   w = canvas_example.width;
 35   h = canvas_example.height;
 36   //параметры для слайдера "Масштаба"
 37   Slider_01.value = 20;
 38     Slider_01.min = 5;
 39     Slider_01.max = 90;
 40     Slider_01.step = 1;
 41   Text_01.value = Slider_01.value; 
 42   koef = parseFloat(Slider_01.value);
 43   canvas_static_func(); //первоначальная прорисовка
 44   time_code(); //включение временного масштаба
 45   start.onclick = Start; //клик по кнопке Запустить
 46   pauseplay.onclick = PausePlay; //клик по кнопке Остановить/Продолжить
 47   telo1.onclick = first_t; //слежение за синим телом
 48   telo2.onclick = second_t; //слежение за красным телом
 49   switcher.onclick = SwitchForInf; //клик по кнопке "бесконечная прорисовка"
 50   returner.onclick = function(){ //Обратная прорисовка
 51     if (vrem>0) { //если выключена
 52       vrem=-vrem; //включить
 53       document.getElementById("returner").innerHTML= "Включить прямую анимацию";
 54       if (idi==1&&n!=0) { //если включена пауза
 55         document.getElementById("text_u").innerHTML= "Остановить ";
 56         animation(); idi=2;
 57       }
 58     } else if (vrem<0) { //если включена
 59         vrem=-vrem; //выключить
 60         document.getElementById("returner").innerHTML= "Включить обратную анимацию";
 61     }
 62   }
 63   /*Изменение масштаба колёсиком*/
 64   var elem = document.getElementById('canvas_example');
 65     if (elem.addEventListener) { //если в окне canvas_example крутится колёсико (для разных браузеров)
 66       if ('onwheel' in document) {
 67         // IE9+, FF17+
 68         elem.addEventListener("wheel", onWheel);
 69       } else if ('onmousewheel' in document) {
 70         // устаревший вариант события
 71         elem.addEventListener("mousewheel", onWheel);
 72       } else {
 73         // Firefox < 17
 74         elem.addEventListener("MozMousePixelScroll", onWheel);
 75       }
 76     } else { // IE8-
 77       elem.attachEvent("onmousewheel", onWheel);
 78     }
 79   function onWheel(e) {
 80     //e = e || window.event;
 81     // deltaY, detail содержат пиксели
 82     // wheelDelta не дает возможность узнать количество пикселей
 83     // onwheel || MozMousePixelScroll || onmousewheel
 84     var m = mouseCoords(e); //получаем координаты курсора
 85     var delta = e.deltaY || e.detail || e.wheelDelta;
 86     var wheel_x=(m.x-20)/koef; //разность координаты по оси X положения курсора мыши и координаты левого края
 87     var wheel_y=(h-m.y-20)/koef; //разность координаты по оси Y положения курсора мыши и координаты нижнего края
 88     Slider_01.value = Slider_01.value - delta/100; //меняем ползунок слайдера масштаба
 89     Text_01.value=Slider_01.value; //меняем численное значение в окне масштаба
 90       if (first_obj==0&&second_obj==0) { //если не включено слежение за телами
 91         px=px*(koef-delta/100)/koef+m.x-wheel_x*(koef-delta/100)-20;
 92         py=py*(koef-delta/100)/koef+m.y-(h-(wheel_y*(koef-delta/100)))+20;
 93         /*сдвиги по Х и Y складывается из старого сдвига и сдвига, 
 94         необходимого для фиксации положения точки с некоторыми координатами,
 95         соответствующими положению мыши*/
 96       }
 97       if (idi!=2) {
 98         xx1=(xx1-20)*(koef-delta/100)/koef+20;
 99         yy1=h-((h-(yy1+20))*(koef-delta/100)/koef+20);
100         xx2=(xx2-20)*(koef-delta/100)/koef+20;
101         yy2=h-((h-(yy2+20))*(koef-delta/100)/koef+20);
102         canvas_static_func(); // запускаем статичную рисовалку
103       }
104       koef=parseFloat(Slider_01.value); //обновляем коэфициент масштаба
105       e.preventDefault ? e.preventDefault() : (e.returnValue = false); //отменяем прокрутку страницы
106     }
107   Text_01.oninput = function() {
108     if (!this.checkValidity()) return;
109     document.getElementById('Slider_01').value = this.value;
110     app.set_01(this.value);
111   };
112   Slider_01.oninput = function() {
113     document.getElementById('Text_01').value = this.value;
114     app.set_01(this.value);
115   }
116    Text_02.oninput = function() {
117     if (!this.checkValidity()) return; 
118     app2.set_02(this.value);
119     document.getElementById('Slider_01').value = this.value;
120   };
121   Slider_02.oninput = function() {
122     app2.set_02(this.value);
123     document.getElementById('Text_02').value = this.value;
124   }; 
125   if (idi!=2){ //Если не включена прорисовка, то изменение координат в таблице влечёт изменение положения тел на экране
126     iksi.onchange = canvas_static_func;
127     igri.onchange = canvas_static_func;
128     iksii.onchange = canvas_static_func;
129     igrii.onchange = canvas_static_func;
130   }
131 }
132 function SwitchForInf() {    //переключатель "бесконечное/конечное постоение"
133   if (Switch==0) {
134     document.getElementById("switch_span").innerHTML= "Выключить бесконечное построение";
135     Switch=1;
136   } else {
137     document.getElementById("switch_span").innerHTML= "Включить бесконечное построение";
138     Switch=0;    
139   };
140 }
141 function first_t() {    //функция слежения за синим телом
142   if (first_obj==0) { //слеженеие за синим телом не активно
143     first_obj=1; //включить слежение за синим телом
144     second_obj=0; //отключить слежение за красным
145     document.getElementById("text_sl_1").innerHTML= "Прекратить слежение";
146     document.getElementById("text_sl_2").innerHTML= "Следить за красным телом";
147   } else { //слеженеие за синим телом активно
148     first_obj=0; //отключить слежение за синим телом
149   document.getElementById("text_sl_1").innerHTML= "Следить за синим телом";      
150   };
151   if (idi!=2&&first_obj==1) { //если не включена анимация
152     px=w/2-xx1; //смещение по X - такое, что 1 тело по центру
153     py=h/2-yy1; //смещение по Y - такое, что 1 тело по центру
154     canvas_static_func(); //нарисовать
155   }
156 }
157 function second_t() {    //функция слежения за красным телом
158   if (second_obj==0) { //слеженеие за красным телом не активно
159     second_obj=1;  //включить слежение за красным телом
160     first_obj=0; //отключить слежение за синим
161     document.getElementById("text_sl_2").innerHTML= "Прекратить слежение";
162     document.getElementById("text_sl_1").innerHTML= "Следить за синим телом";
163   }
164   else { //слеженеие за красным телом активно
165     second_obj=0;  //отключить слежение за красным телом
166     document.getElementById("text_sl_2").innerHTML= "Следить за красным телом";      
167   }
168       
169    if (idi!=2&&second_obj==1) {
170     px=w/2-xx2;
171     py=h/2-yy2;
172     canvas_static_func();
173    }
174 }
175 function save() {    //сохранение активных элементов (участвуют в анимации) массивов в таблицы
176   skor(); //скорости
177   koord(); //координат
178 }
179 function koord() {    //сохранение координат
180   iksi.value = Math.round(x1[20*n]*100)/100;
181   igri.value = Math.round(y1[20*n]*100)/100;
182   iksii.value = Math.round(x2[20*n]*100)/100;
183   igrii.value = Math.round(y2[20*n]*100)/100;  
184 }
185 function skor () {    //сохранение скоростей
186   vxi.value = Math.round(vx1[20*n]*10000)/10000;
187   vyi.value = Math.round(vy1[20*n]*10000)/10000;
188   vxii.value = Math.round(vx2[20*n]*10000)/10000;
189   vyii.value = Math.round(vy2[20*n]*10000)/10000;
190 }
191 function mouseCoords(e) { //функция, определяющая координаты мыши (в пикселях) внутри canvas_example
192         var m = [];
193         var rect = canvas_example.getBoundingClientRect(); //координаты canvas_example относительно окна
194         m.x = e.clientX - rect.left; //координаты курсора в canvas_example по X: положение курсора - расстояние до поля слева
195         m.y = e.clientY - rect.top; //координаты курсора в canvas_example по Y: положение курсора - расстояние до поля сверху
196         return m;
197 }
198 function canvas_static_func() {    //прорисовка во время паузы
199   var click; //Идентификатор клика
200   /*click:
201   "==1" - клик ЛКМ по синему телу
202   "==2" - клик ЛКМ по красному телу
203   "==3" - клик ЛКМ по полю
204   "==4" - клик ПКМ по синему телу
205   "==5" - клик ПКМ по красному телу
206   */
207   var vxx1, vxx2, vyy1, vyy2; //координаты скорости при прорисовке векторов
208   var r = 15; //условный радиус обоих тел
209     var xShift, yShift;
210   ctx = canvas_example.getContext("2d");
211   koef = parseFloat(Text_01.value);
212   m1=parseFloat(mi.value); //1а.е.м = 1.6606*10^-27 кг - масса для создания масштаба
213   m2=parseFloat(mii.value); //1а.е.м = 1.6606*10^-27 кг - масса для создания масштаба
214   if (idi!=1) {    //если не включена пауза, то при вызове canvas_static_func() сохранить значения из таблицы
215     xx1 = 20+parseFloat(iksi.value)*koef;
216     yy1 = h-(20+parseFloat(igri.value)*koef);  
217     xx2 = 20+parseFloat(iksii.value)*koef;
218     yy2 = h-(20+parseFloat(igrii.value)*koef); 
219   }
220   if (idi!=2) {    //если не включена анимация, то при вызове canvas_static_func() запускать отрисовку
221     draw_static();
222   }
223   this.set_01 = function(input) {
224     var Old_koef=koef; //переменная для сохранения значения koef до его изменения
225     koef=parseFloat(Slider_01.value); //меняем koef
226     if (first_obj==0&&second_obj==0) { //если не включено слежение за телами
227         px=px*koef/Old_koef+w/2-(w/2-20)*koef/Old_koef-20;
228         py=py*koef/Old_koef+h/2-(h-(h/2-20)*koef/Old_koef)+20;
229         /*сдвиги по Х и Y складывается из старого сдвига и сдвига, 
230         необходимого для фиксации положения центра*/
231         koef=parseFloat(Slider_01.value); //обновляем коэфициент масштаба
232       }
233     if (idi!=2) {
234       xx1=(xx1-20)*koef/Old_koef+20;
235       yy1=h-((h-(yy1+20))*koef/Old_koef+20);
236       xx2=(xx2-20)*koef/Old_koef+20;
237       yy2=h-((h-(yy2+20))*koef/Old_koef+20);
238       draw_static();
239     }
240   }
241 // функция запускается при нажатии клавиши мыши  
242     function draw_static() {  
243     koef=parseFloat(Slider_01.value);
244     /*Учитываем  условие слежения за синим/красным телом*/
245      if (second_obj==1) {
246       px=w/2-(dx+xx2);
247       py=h/2-(dy+yy2);
248     }
249     if (first_obj==1) {
250       px=w/2-(dx+xx1);
251       py=h/2-(dy+yy1);
252     }
253     
254     field(); //нарисовать поле, сетку и расставить числа на осях
255     ctx.beginPath();    //синий шарик
256         ctx.arc(dx+xx1+px, dy+yy1+py, (m1/(m1+m2)+2/3)*koef/4, 0, 2 * Math.PI);
257     ctx.fillStyle="#6666ff";
258     ctx.closePath();
259         ctx.fill();
260     
261     ctx.beginPath();    //красный шарик
262         ctx.arc(dx+xx2+px, dy+yy2+py, (m2/(m1+m2)+2/3)*koef/4, 0, 2 * Math.PI);
263     ctx.fillStyle="red";
264     ctx.closePath();
265         ctx.fill();
266     
267     /* Прорисовка векторов в визуальном редакторе по ПКМ */
268     if(click==5||click==4) {
269       ctx.beginPath();
270         var xxx1=xx2+px;
271         var xxx2=vxx2+px;
272         var yyy1=yy2+py;
273         var yyy2=vyy2+py;
274         var path=Math.sqrt((xxx2-xxx1)*(xxx2-xxx1)+(yyy2-yyy1)*(yyy2-yyy1)); //длина вектора скорости
275         var x0=xxx1+(1-Math.pow(path,-0.45))*(xxx2-xxx1);
276         var y0=yyy1+(1-Math.pow(path,-0.45))*(yyy2-yyy1);
277         ctx.moveTo(xxx1, yyy1); //начало вектора - координаты тела
278         ctx.lineTo(xxx2, yyy2);
279         ctx.lineTo(x0+(Math.pow(path,0.3))*(yyy2-yyy1)/path, y0-(Math.pow(path,0.3))*(xxx2-xxx1)/path);
280         ctx.lineTo(x0-(Math.pow(path,0.3))*(yyy2-yyy1)/path, y0+(Math.pow(path,0.3))*(xxx2-xxx1)/path);
281         ctx.lineTo(xxx2, yyy2);
282       ctx.closePath();
283       ctx.fillStyle="#000";
284       ctx.fill();
285       ctx.strokeStyle="#000";
286       ctx.stroke();
287     }
288     if(click==4||click==5) {
289       ctx.beginPath();
290         var xxx1=xx1+px;
291         var xxx2=vxx1+px;
292         var yyy1=yy1+py;
293         var yyy2=vyy1+py;
294         var path=Math.sqrt((xxx2-xxx1)*(xxx2-xxx1)+(yyy2-yyy1)*(yyy2-yyy1));
295         var x0=xxx1+(1-Math.pow(path,-0.45))*(xxx2-xxx1);
296         var y0=yyy1+(1-Math.pow(path,-0.45))*(yyy2-yyy1);
297         ctx.moveTo(xxx1, yyy1);
298         ctx.lineTo(xxx2, yyy2);
299         ctx.lineTo(x0+(Math.pow(path,0.3))*(yyy2-yyy1)/path, y0-(Math.pow(path,0.3))*(xxx2-xxx1)/path);
300         ctx.lineTo(x0-(Math.pow(path,0.3))*(yyy2-yyy1)/path, y0+(Math.pow(path,0.3))*(xxx2-xxx1)/path);
301         ctx.lineTo(xxx2, yyy2);
302         ctx.fillStyle="#000";
303       ctx.closePath();
304       ctx.fill();
305       ctx.strokeStyle="#000";
306       ctx.stroke();
307     }
308     }
309     canvas_example.onmousedown = function(e) {    // функция запускается при нажатии клавиши мыши
310         var m = mouseCoords(e);
311         var xc1 = xx1+px - m.x;
312         var yc1 = yy1+py - m.y;
313     var rLen1 = xc1 * xc1 + yc1 * yc1;
314     var xc2 = xx2+px - m.x;
315         var yc2 = yy2+py - m.y;
316         var rLen2 = xc2 * xc2 + yc2 * yc2;
317     if (e.which == 1) { //клик ЛКМ
318       if (rLen1 <= r * r) {
319         if (n!==0&&idi!=2) skor();  //Сохранение скорости из массива при клике по телу во время паузы
320         if (rLen1>rLen2) {
321           xShift = xx2 - m.x;
322           yShift = yy2 - m.y;
323           click=2; //Перемещение красного тела
324           canvas_example.onmousemove = mouseMove;
325         } else { 
326           xShift = xx1 - m.x;
327           yShift = yy1 - m.y;
328           click=1; //Перемещение синего тела
329           canvas_example.onmousemove = mouseMove;
330         }
331       }  else if (rLen2 <= r * r) {
332         if (n!==0&&idi!=2) skor();  //Сохранение скорости из массива при клике по телу во время паузы
333         if (rLen2>rLen1) {
334           xShift = xx1 - m.x;
335           yShift = yy1 - m.y;
336           click=1; //Перемещение синего тела
337           canvas_example.onmousemove = mouseMove;
338         }  else { 
339           xShift = xx2 - m.x;
340           yShift = yy2 - m.y;
341           click=2; //Перемещение красного тела
342           canvas_example.onmousemove = mouseMove;
343         }
344       }
345       else {
346         click=3; //Клик по полю 
347         kx=m.x;
348         ky=m.y;
349         canvas_example.onmousemove = mouseMove;
350       }
351     } 
352     if (e.which == 3){ //Клик ПКМ
353       if (rLen1 <= r * r) { //попадание по синему телу
354         if (rLen1>rLen2) { //расстояние до центра красного меньше, чем расстояние дл центра синего
355           click=5; //Изменение вектора скорости красного тела
356           canvas_example.onmousemove = mouseMove;
357         } else { 
358           click=4; //Изменение вектора скорости синего тела
359           canvas_example.onmousemove = mouseMove;
360         }
361       }
362       else if (rLen2 <= r * r) { //попадание по красному телу
363         if (rLen2>rLen1) { //расстояние до центра синего меньше, чем расстояние дл центра красного
364           click=4; //Изменение вектора скорости синего тела
365           canvas_example.onmousemove = mouseMove;
366         } else { 
367           click=5; //Изменение вектора скорости красного тела
368           canvas_example.onmousemove = mouseMove;
369         }
370       }
371     }
372 };
373     canvas_example.onmouseup = function() {
374     if (click==3) {
375       px=px+dx;
376       py=py+dy;
377       kx=ky=dx=dy=0;
378     }
379         canvas_example.onmousemove = null;
380     }; 
381   function mouseMove(e) {
382     var m = mouseCoords(e);
383     if (idi!=2) {
384       if (click==1) {
385         xx1 = m.x + xShift;
386         yy1 = m.y + yShift;  
387         iksi.value = (xx1-20)/koef;
388         igri.value = (h-(yy1+20))/koef;
389         draw_static();
390         };
391       if (click==2) {
392         xx2 = m.x + xShift;
393         yy2 = m.y + yShift;
394         iksii.value = (xx2-20)/koef;
395         igrii.value = (h-(yy2+20))/koef;
396         draw_static();
397         };
398     }
399       if (click==3) {
400         dx=m.x-kx;
401         dy=m.y-ky;
402         if (idi!=2) draw_static();
403         };
404     if (idi!=2) {
405       if (click==4) {
406         vxx1 = m.x-px;
407         vyy1 = m.y-py;
408         vxi.value = (vxx1-xx1)/koef;
409         vyi.value = (yy1-vyy1)/koef;
410         iksi.value = (xx1-20)/koef;
411         igri.value = (h-(yy1+20))/koef;
412         iksii.value = (xx2-20)/koef;
413         igrii.value = (h-(yy2+20))/koef;
414         draw_static();
415         };
416       if (click==5) {
417         vxx2 = m.x-px;
418         vyy2 = m.y-py;
419         vxii.value = (vxx2-xx2)/koef;
420         vyii.value = (yy2-vyy2)/koef;
421         iksi.value = (xx1-20)/koef;
422         igri.value = (h-(yy1+20))/koef;
423         iksii.value = (xx2-20)/koef;
424         igrii.value = (h-(yy2+20))/koef;
425         draw_static();
426       };
427     };
428   };
429 }
430 function time_code() {    //временной масштаб
431   Slider_02.min = 1;
432     Slider_02.max = 90;
433     Slider_02.step = 1;
434   Slider_02.value = 1;
435   Text_02.value = Slider_02.value;
436   this.set_02 = function(input) {      //запуск при изменении значения set_02
437     vrem=Math.sign(vrem)*parseFloat(Slider_02.value); //изменение переменной скорости отрисовки
438   }
439 }
440 function Start() {    //Старт по кнопке "Запустить"
441    buttonStart(); //функция, сообщающая, что старт совершён по кнопке "Запустить": порядок прорисовки прямой, с нулевого элемента
442    animation(); //запуск анимации
443 }
444 function buttonStart() {    //Изменение значений кнопок и идентификатора idi
445 document.getElementById("returner").innerHTML= "Включить обратную анимацию";
446 document.getElementById("text_u").innerHTML= "Остановить ";
447 idi=2;
448 }
449 function PausePlay() {    //Кнопка "Остановить/Продолжить"
450   if (idi!=1) { //Если пауза не включена
451     clearInterval(intervalID); //остановить прорисовку
452     document.getElementById("text_u").innerHTML= "Продолжить";
453     idi=1; //активировать паузу
454   }
455   else { //иначе
456     if (n!==0) { //если уже была включена анимация, следовательно кнопка нажата во время паузы
457       document.getElementById("text_u").innerHTML= "Остановить "; animation(); idi=2; //запустить прорисовку
458     };
459     if (n==0) {  //если анимация не включалась
460     document.getElementById("text_u").innerHTML= "Остановить "; idi=0;
461     }
462   }
463 }    
464 function animation(){    //анимация движения частиц
465   clearInterval(intervalID);  //обнулять старый интервал при каждом новом запуске анимации
466   koef=parseFloat(Slider_01.value);
467   function A(a1,a2,b1,b2,q,m) { //рассчёт ускорения тела из условия взаимного притяжения/отталкивания двух тел по закону Кулона
468     var r=Math.sqrt((a2-a1)*(a2-a1)+(b2-b1)*(b2-b1));
469     return (-1)*kulkof*q*(a2-a1)/(m*r*r*r)
470   };
471   if (idi==2) { //если система запущена по кнопке "Запустить"
472     physics(); //сосчитать массивы координат и скоростей
473     n=0; //обнулить переменную, пробегающую массивы
474   }
475   function physics() {
476     vrem=Math.abs(vrem);
477     t = [];
478     a1x = []; a2x = [];  a1y = []; a2y = [];
479     vx1 = []; vx2 = [];  vy1 = []; vy2 = [];
480     x1 = []; y1 = []; x2 = []; y2 = [];
481     t[0]=0;
482     x1[0]=parseFloat(iksi.value);
483     y1[0]=parseFloat(igri.value);
484     x2[0]=parseFloat(iksii.value);
485     y2[0]=parseFloat(igrii.value);
486     vx1[0]=parseFloat(vxi.value);
487     vy1[0]=parseFloat(vyi.value);
488     vx2[0]=parseFloat(vxii.value);
489     vy2[0]=parseFloat(vyii.value);
490     m1=parseFloat(mi.value);
491     m2=parseFloat(mii.value);
492     q1=parseFloat(qi.value);
493     q2=parseFloat(qii.value);
494     q=q1*q2;
495     a1x[0]= A(x1[0],x2[0],y1[0],y2[0],q,m1);
496     a1y[0]= A(y1[0],y2[0],x1[0],x2[0],q,m1);
497     a2x[0]= A(x2[0],x1[0],y1[0],y2[0],q,m2);
498     a2y[0]= A(y2[0],y1[0],x1[0],x2[0],q,m2);
499     var i=0;
500     while (t[i] < T) {
501       vx1[i+1]= vx1[i]+a1x[i]*dt;
502       vy1[i+1]= vy1[i]+a1y[i]*dt;
503       vx2[i+1]= vx2[i]+a2x[i]*dt;
504       vy2[i+1]= vy2[i]+a2y[i]*dt;
505       
506       x1[i+1] = x1[i]+vx1[i+1]*dt;
507       y1[i+1] = y1[i]+vy1[i+1]*dt;
508       x2[i+1] = x2[i]+vx2[i+1]*dt;
509       y2[i+1] = y2[i]+vy2[i+1]*dt;
510       
511       a1x[i+1]= A(x1[i+1],x2[i+1],y1[i+1],y2[i+1],q,m1);
512       a1y[i+1]= A(y1[i+1],y2[i+1],x1[i+1],x2[i+1],q,m1);
513       a2x[i+1]= A(x2[i+1],x1[i+1],y1[i+1],y2[i+1],q,m2);
514       a2y[i+1]= A(y2[i+1],y1[i+1],x1[i+1],x2[i+1],q,m2);
515       t[i+1]=t[i]+dt;
516       i=i+1;
517     }
518   }
519   function draw_dinamic() {
520     field(); //Прорисовка поля
521     /*Учитываем  условие слежения за синим/красным телом*/
522     if (second_obj==1) {
523       px=w/2-(dx+20+x2[20*n]*koef);
524       py=-h/2-(dy-(20+y2[20*n]*koef));
525     }
526     if (first_obj==1) {
527       px=w/2-(dx+20+x1[20*n]*koef);
528       py=-h/2-(dy-(20+y1[20*n]*koef));
529     }
530     ctx.beginPath(); //рисуем синее тело
531     ctx.arc(px+dx+20+x1[20*n]*koef,py+dy+h-(20+y1[20*n]*koef), 1/4*(m1/(m1+m2)+2/3)*koef, 0, Math.PI*2);
532       ctx.fillStyle = "#6666ff";
533     ctx.closePath();
534     ctx.fill();
535 
536     ctx.beginPath(); //рисуем красное тело
537     ctx.arc(px+dx+20+x2[20*n]*koef,py+dy+h-(20+y2[20*n]*koef), 5*(m2/(m1+m2)+2/3)*koef/20, 0, Math.PI*2);
538       ctx.fillStyle = "red";
539     ctx.closePath();
540     ctx.fill();
541     
542     xx1=20+x1[20*n]*koef; yy1=h-(20+y1[20*n]*koef); //положение синего тела сохраняем для визуального редактирования
543     xx2=20+x2[20*n]*koef; yy2=h-(20+y2[20*n]*koef); //положение красного тела сохраняем для визуального редактирования
544     n=n+vrem;
545   }
546   function control() { 
547     draw_dinamic(); 
548     if(n>=(t.length/20-vrem)){ 
549     n=n-vrem; //возвращаем n к значению, соответствующему одному из последних существующих элементов массива
550       if (Switch==1) { //Если включено бексконечное построение 
551         save(); //Сохранять последнее значение координат и скоростей
552         idi=2; //Инициировать запуск системы по кнопке
553         animation(); //И запускать прорисовку с новыми элементами массивов
554       } else if (Switch==0) { //Если выключено бексконечное построение
555         clearInterval(intervalID); //остановить прорисовку
556         draw_dinamic(); //Нарисовать положение в последний момент 
557         idi=0; //Инициировать остановку анимации
558       } 
559     } 
560     if (n<0) { //Если перемотка привела к началу массивов/начальному положению системы тел
561       clearInterval(intervalID); //остановить прорисовку
562       n=0; //Вернуться к начальным условиям
563       vrem=-vrem; //нормальный ход времени
564       draw_dinamic();  //Нарисовать тела
565       idi=0; //Инициировать остановку анимации
566     } 
567   }
568   intervalID=setInterval(control,1); //Выполнение "control", обеспечивающего прорисовку анимации, через 1 миллисекунду
569 }
570 function field() {    //Функция прорисовки поля
571   ctx.clearRect(0, 0, w, h);
572   ctx.fillStyle = 'white';
573   ctx.fillRect(20, 20, w-40, h-40);
574   ctx.strokeStyle = 'black';
575   ctx.strokeRect(20, 20, w-40, h-40);
576   ctx.fillStyle = 'black';
577   ctx.font = "bold italic 20px Times";
578   ctx.fillText("x", w-15, h - 20);
579   ctx.fillText("y", 15, 15);
580   ctx.font = "italic 16px Times";
581   ctx.fillText(0, 1.5, h - 4.5);
582   /*Сетка*/
583   for (var k = dx+px+20+koef; k < w-20; k = k+koef) {
584     ctx.beginPath();
585     if (k>20) {
586       ctx.moveTo(k,h-20);
587       ctx.lineTo(k,20);
588       ctx.strokeStyle = "#cab0b0";
589       ctx.closePath();
590       ctx.stroke();
591     }
592   }
593   for (var k = dx+px+20; (k > 0); k = k-koef) {
594     ctx.beginPath();
595     if (k>20&&k<w-20) {
596       ctx.moveTo(k,h-20);
597       ctx.lineTo(k,20);
598       ctx.strokeStyle = "#cab0b0";
599       ctx.closePath();
600       ctx.stroke();
601     }
602   }
603   for (var u = dy+h+py-20-koef; u >20 ; u = u-koef) {
604     ctx.beginPath();
605     if (u<h-20) {
606       ctx.strokeStyle = "#cab0b0";
607       ctx.moveTo(20, u);
608       ctx.lineTo(w-20, u);
609       ctx.closePath();
610       ctx.stroke();
611       }
612     }
613   for (var u = dy+h+py-20; u < h ; u = u+koef) {
614     ctx.beginPath();
615     if (u<h-20&&u>20) {
616       ctx.strokeStyle = "#cab0b0";
617       ctx.moveTo(20, u);
618       ctx.lineTo(w-20, u);
619       ctx.closePath();
620       ctx.stroke();
621       }
622     }
623   /* "+5" нумерация */
624   if (koef<20) {
625   for (var oy = dy+py+h-15-5*koef, ooy=5; oy>20; oy=oy-5*koef) {
626     ctx.fillStyle = 'black';
627         ctx.font = "italic 16px Times";
628     if (oy<h-20) {
629       ctx.fillText(ooy, 1.5, oy);
630       }
631     ooy+=5;
632     }
633    for (var oy = dy+h+py-15, ooy=0; oy<h; oy=oy+5*koef) {
634     ctx.fillStyle = 'black';
635         ctx.font = "italic 16px Times";
636     if (oy<h-20&&oy>20) {
637       ctx.fillText(ooy, 1.5, oy);
638       }
639     ooy-=5;
640     } 
641   for (var ox = dx+px+15.5+5*koef, oox=5; ox<w-20; ox=ox+5*koef) {
642     ctx.fillStyle = 'black';
643         ctx.font = "italic 16px Times";
644     if (ox>20) {
645       ctx.fillText(oox, ox, h-2);
646       }
647     oox+=5;
648     }
649    for (var ox = dx+px+15.5, oox=0; ox>0; ox=ox-5*koef) {
650     ctx.fillStyle = 'black';
651         ctx.font = "italic 16px Times";
652     if (ox>20&&ox<w-20) {
653       ctx.fillText(oox, ox, h-2);
654       }
655     oox-=5;
656     }
657   }
658   /* Простая нумерация */
659   else {
660   for (var oy = dy+h+py-15-koef, ooy=1; oy>20; oy=oy-koef) {
661     ctx.fillStyle = 'black';
662         ctx.font = "italic 16px Times";
663     if (oy<h-20) {
664     ctx.fillText(ooy, 1.5, oy);
665       }
666     ooy++;
667     }
668   for (var ox = dx+px+15.5+koef, oox=1; ox<w-20; ox=ox+koef) {
669     ctx.fillStyle = 'black';
670         ctx.font = "italic 16px Times";
671     if (ox>20) {
672       ctx.fillText(oox, ox, h-2);
673       }
674     oox++;
675     if (oox>100) {    //После X=100 единицы масштаба наносятся через 5 клеток
676       ox=ox+4*koef;
677       oox=oox+4;
678       }
679     }
680   for (var oy = dy+h+py-15, ooy=0; oy<h; oy=oy+koef) {
681     ctx.fillStyle = 'black';
682         ctx.font = "italic 16px Times";
683     if (oy<h-20&&oy>20) {
684       ctx.fillText(ooy, 1.5, oy);
685       }
686     ooy--;
687     }
688   for (var ox = dx+px+15.5, oox=0; ox>0; ox=ox-koef) {
689     ctx.fillStyle = 'black';
690         ctx.font = "italic 16px Times";
691     if (ox>20&&ox<w-20) {
692       ctx.fillText(oox, ox, h-2);
693       }
694     oox--;
695     if (oox<-10) {    //До X=-10 единицы масштаба наносятся через 5 клеток
696       ox=ox-4*koef;
697       oox=oox-4;
698       }
699     }
700   }
701 }

Файл "culon.html"

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4   <meta charset="utf-8">
 5   <script src="Code.js"></script>
 6   <script src="bg.js"></script>
 7   <link rel="stylesheet" type="text/css" href="style.css" />
 8   <title>Взаимодействие двух заряженных тел (Закон Кулона)</title>
 9 </head>
10  <body>
11   <canvas id="canvas_bg" class="canvas_bg"></canvas>
12   <div class="main" id="main">
13     <div style="float:left; margin-left: 20px;"> Масштаб:
14       <I>1 м.</I> = <input id="Text_01" style="width: 2.2ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)" > пкс.
15       <input type="range" id="Slider_01" style="width: 100px;" value="20">
16     </div>
17      <div id="workspace"  style="float:right; margin-right: 20px;"><canvas  id="canvas_example" class="canvas_example" width="750" height="550"></canvas>
18     </div>
19   <div style="float:left; text-align: center; margin-left: 20px;">
20     <p>FPS:<input id="Text_02" style="width: 2.2ex;" required pattern="[-+]?([0-9]*\.[0-9]+|[0-9]+)">
21       <input type="range" id="Slider_02" style="width: 100px;">
22     </p><br />
23     <a href="#" id=start class="button">Запустить</a>
24     <a href="#" id=pauseplay class="button"><SPAN id=text_u>Остановить&nbsp;</SPAN></a><br>
25     <a href="#" id=linka class="button28" onclick="save(); canvas_static_func()">
26       Сохранить координаты и скорость тел<br>для последующего запуска</a>  <br>  
27   <h2>Синее тело</h2>
28     <table>
29     <tbody>
30       <tr><th colspan="5">Координаты тела</th></tr>
31       <tr><td><I>X: </I><input id=iksi type=number value=18></td>
32       <td><I> Y: </I><input id=igri type=number value=12.71></td></tr>
33       <tr><th colspan="5">Проекции скорости</th></tr>
34       <tr><td><I>На OX: </I><input id=vxi type=number value=0></td>
35       <td><I> на OY: </I><input id=vyi type=number value=0></td></tr>
36       <tr><th colspan="5">Масса и заряд</th></tr>
37       <tr><td><input id=mi type=number value=1670><I>x 10<sup>-30</sup>кг</I></td>
38       <td><input id=qi type=number value=1><I> x 10<sup>-19</sup>Кл</I></td></tr>
39     </tbody>
40     </table>
41       <h2>Красное тело</h2>
42     <table>
43     <tbody>
44       <tr><th colspan="5">Координаты тела</th></tr>
45       <tr><td><I>X: </I><input id=iksii type=number value=18></td>
46       <td><I> Y: </I><input id=igrii type=number value=18.29></td></tr>
47       <tr><th colspan="5">Проекции скорости</th></tr>
48       <tr><td><I>X:</I><input id=vxii type=number value=2.22><I>x 10<sup>6</sup></I><sub>с</sub><sup>м</sup></td>
49       <td><I>Y:</I><input id=vyii type=number value=0><I>x 10<sup>6</sup></I><sub>с</sub><sup>м</sup></td></tr>
50       <tr><th colspan="5">Масса и заряд</th></tr>
51       <tr><td><input id=mii type=number value=0.91><I>x 10<sup>-30</sup>кг</I></td>
52       <td><input id=qii type=number value=-1><I>x 10<sup>-19</sup>Кл</I></td></tr>
53     </tbody>
54     </table>
55     </div>
56     <div class="follow" style="float: left" >
57         <a style="background: #6666ff!important;" href="#" id=telo1 class="button28"><SPAN id=text_sl_1>Следить за синим телом</span></a>
58         <a style="background: red!important;" href="#" id=telo2 class="button28"><SPAN id=text_sl_2>Следить за красным телом</span></a>
59         <a href="#" id=switcher class="button28"><SPAN id=switch_span>Включить бесконечное построение</span></a>
60         <a href="#" id=returner class="button28"><SPAN id=switch_span>Включить обратную анимацию</span></a>
61       </div>
62   </div>
63 <script type="text/javascript">
64   var app = new canvas_static_func(document.getElementById('canvas_example'));
65   var app2 = new time_code(document.getElementById('canvas_example'));
66 </script>
67 </body>
68 </html>

Файл "bg.js"

 1 window.addEventListener("load", program_code, false);
 2 function program_code() { 
 3 var canvas = document.getElementById('canvas_bg')[0],
 4     ctx = null,
 5     grad = null,
 6     body = document.getElementsByTagName('body')[0],
 7     color = 255; //изначальный цвет фона сайта
 8 
 9 if (canvas_bg.getContext('2d')) {
10   ctx = canvas_bg.getContext('2d');
11   ctx.clearRect(0, 0, 600, 600);
12   ctx.save();
13 
14   // создание радиального градиента
15   grad = ctx.createRadialGradient(0,0,0,0,0,600); 
16   grad.addColorStop(0, '#DFDFDF');
17   grad.addColorStop(1, 'rgb(' + color + ', ' + color + ', ' + color + ')');
18 
19   // сам фон-градиент
20   ctx.fillStyle = grad;
21 
22   first_time(); //при первом запуске
23   function first_time() {
24      var width = window.innerWidth, 
25       height = window.innerHeight, 
26       x = width/2, 
27       y = height/2,
28       rx = 600 * x / width,
29       ry = 600 * y / height;
30       
31     var xc = ~~(256 * x / width);
32     var yc = ~~(256 * y / height);
33 
34     grad = ctx.createRadialGradient(rx, ry, 0, rx, ry, 500);  //размер указателя мышки
35     grad.addColorStop(0, '#478CFB'); //цвет  указателя мышки
36     grad.addColorStop(1, ['rgb(', xc, ', ', (255 - xc), ', ', yc, ')'].join(''));
37     ctx.fillStyle = grad;
38     ctx.fillRect(0,0,600,600);
39   };
40   
41    body.onmousemove = function (event) {
42     var width = window.innerWidth, 
43       height = window.innerHeight, 
44       x = event.clientX, 
45       y = event.clientY,
46       rx = 600 * x / width,
47       ry = 600 * y / height;
48     var xc = ~~(256 * x / width);
49     var yc = ~~(256 * y / height);
50 
51     grad = ctx.createRadialGradient(rx, ry, 0, rx, ry, 500);  //размер указателя мышки
52     grad.addColorStop(0, '#478CFB'); //цвет  указателя мышки
53     grad.addColorStop(1, ['rgb(', xc, ', ', (255 - xc), ', ', yc, ')'].join(''));
54     //ctx.restore();
55     ctx.fillStyle = grad;
56     ctx.fillRect(0,0,600,600);
57     // ctx.save();
58   }; 
59 }
60 }

Файл "style.css"

  1 h2, h1 {
  2   margin: 10px 0 0 0;
  3   color: #fff;
  4   text-shadow: 1px 1px 1px black, 0 0 0.1em blue;;
  5 }
  6 .button {
  7   font-size: 18px;
  8   font-weight: 700;
  9   color: white;
 10   text-decoration: none;
 11   padding: .4em 1em calc(.4em + 3px);
 12   border-radius: 3px;
 13   background: rgb(64,199,129);
 14   box-shadow: 0 -3px rgb(53,167,110) inset;
 15   transition: 0.2s;
 16 } 
 17 .button:hover { background: rgb(53, 167, 110); }
 18 .button:active {
 19   background: rgb(33,147,90);
 20   box-shadow: 0 3px rgb(33,147,90) inset;
 21 }
 22 a.button28 {
 23   position: relative;
 24   display: inline-block;
 25   font-size: 90%;
 26   font-weight: 700;
 27   color: rgb(209,209,217);
 28   text-decoration: none;
 29   text-shadow: 0 -1px 2px rgba(0,0,0,.2);
 30   padding: .5em 1em;
 31   outline: none;
 32   border-radius: 3px;
 33   background: linear-gradient(rgb(110,112,120), rgb(81,81,86)) rgb(110,112,120);
 34   box-shadow:
 35    0 1px rgba(255,255,255,.2) inset,
 36    0 3px 5px rgba(0,1,6,.5),
 37    0 0 1px 1px rgba(0,1,6,.2);
 38   transition: .2s ease-in-out;
 39    margin-top: 10px;
 40 }
 41 a.button28:hover:not(:active) {
 42   background: linear-gradient(rgb(126,126,134), rgb(70,71,76)) rgb(126,126,134);
 43 }
 44 a.button28:active {
 45   top: 1px;
 46   background: linear-gradient(rgb(76,77,82), rgb(56,57,62)) rgb(76,77,82);
 47   box-shadow:
 48    0 0 1px rgba(0,0,0,.5) inset,
 49    0 2px 3px rgba(0,0,0,.5) inset,
 50    0 1px 1px rgba(255,255,255,.1);
 51 }
 52 input[type="number"] {
 53    border: 1px solid black;
 54    border-radius: 5px;
 55    
 56     color: #000000;
 57     padding: 3px;
 58     margin-top: 2px;
 59     margin-bottom: 2px;
 60     font-size: 16px;
 61     font-family: Verdana;
 62     background: #FFF;
 63   width: 70px;
 64 }
 65 input[type="number"]:focus {
 66   color: #000000;
 67   border: 1px solid #000000
 68 }
 69 canvas.canvas_bg {
 70   position: absolute; /* позиционирование */
 71   top: 0;    /* фиксируем */
 72   left: 0;    /* фиксируем */
 73   height: 100%; /* делаем фон резиновым */
 74   width: 100%;    /* делаем фон резиновым */
 75 }
 76 
 77 
 78 div.main {
 79     position: absolute;
 80     top:0;
 81     left:0;
 82     /*width:100%;
 83     height:100%;*/
 84 }
 85 sub + sup {
 86   margin-left: -0.4em;
 87 }
 88 
 89 /*Таблица*/
 90 table {
 91 border-spacing: 0;
 92 text-align: center;
 93 border-top-right-radius: 10px;
 94 border-top-left-radius: 10px;
 95 }
 96 th {
 97 background: rgba(101, 11, 95, 0.86);
 98 color: white;
 99 text-shadow: 0 1px 1px #2D2020;
100 padding: 0px;
101 }
102 th, td {
103 colspan: 5;
104 border-style: solid;
105 border-width: 0 1px 1px 0;
106 border-color: white;
107 }
108 th:first-child, td:first-child {
109 text-align: center;
110 }
111 th:first-child {
112 }
113 th:last-child {
114 
115 border-right: none;
116 }
117 td {
118 padding: 1px 2px;
119 background: #ecfc95;
120 }
121 tr:last-child td:first-child {
122 border-radius: 0 0 0 10px;
123 }
124 tr:last-child td:last-child {
125 border-radius: 0 0 10px 0;
126 }
127 tr td:last-child {
128 border-right: none;
129 }
130 p{
131 margin:0;
132 }
133 
134 .follow {
135 margin-left: 150px;
136 }


Совместный проект студентов Дрепина Михаила и Калинина Ильи