Анимация Кривошипно-шатунного механизма
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
width="1100"
height="600">
<title> Slider-Crank </title>
<defs>
<rect
id="Slidebar"
stroke-width="1"
stroke="black"
fill="silver"
fill-opacity="1"
x="0"
y="-12"
width="300"
height="24"
/>
<g id="Crosshead" stroke-width="1" stroke="black" fill-opacity="1">
<rect
fill="gold"
x="-50"
y="-25"
width="100"
height="50"
/>
<circle cx="0" cy="0" r="15" fill="white"/>
</g>
<g id="Crank" stroke-width="1" stroke="black" fill-opacity="1">
<path fill="silver"
d="M -99.959 40.000
A 40 40 0 0 1 -99.959, -40.000
A 450 450 0 0 0 -9.950, -49.000
A 50 50 0 1 1 -9.950, 49.000
A 450 450 0 0 0 -99.959, 40.000
z"/>
<circle cx="-100" cy="0" r="25" fill="white"/>
<circle cx="0" cy="0" r="30" fill="lightgrey"/>
</g>
<g id="ConRod" stroke-width="1" stroke="black" fill-opacity="0.7">
<path fill="silver"
d="M 12.387 21.715
A 30 30 0 0 1 27.551 17.776
L 453.475 22.035
A 30 30 0 0 1 473.243 29.733
A 40 40 0 0 1 473.243 -29.733
A 30 30 0 0 1 453.475 -22.035
L 27.551 -17.776
A 30 30 0 0 1 12.387 -21.715
A 25 25 0 0 1 12.387 21.715
z"/>
<circle cx="0" cy="0" r="25" fill="silver"/>
<circle cx="0" cy="0" r="15" fill="white"/>
<circle cx="500" cy="0" r="40" fill="silver"/>
<circle cx="500" cy="0" r="25" fill="white"/>
</g>
</defs>
<use xlink:href="#Slidebar" x="150" y="263"/>
<use xlink:href="#Slidebar" x="150" y="337"/>
<use xlink:href="#Crosshead" x="200" y="300">
<animate
attributeName="x"
dur="3s"
values=" 200.000;
207.202;
227.545;
257.557;
292.430;
327.159;
357.557;
380.753;
395.140;
400.000;
395.140;
380.753;
357.557;
327.159;
292.430;
257.557;
227.545;
207.202;
200.000"
repeatCount="indefinite"/>
</use>
<use xlink:href="#Crank" x="800" y="300">
<animateTransform
attributeName="transform" type="rotate" from="0,800,300"
by="360" dur="3s" repeatCount="indefinite"/>
</use>
<use xlink:href="#ConRod" x="200" y="300">
<animate
attributeName="x"
dur="3s"
values=" 200.000;
207.202;
227.545;
257.557;
292.430;
327.159;
357.557;
380.753;
395.140;
400.000;
395.140;
380.753;
357.557;
327.159;
292.430;
257.557;
227.545;
207.202;
200.000"
repeatCount="indefinite"/>
<animateTransform
attributeName="transform" type="rotate" from="0,200,300"
repeatCount="indefinite" dur="3s"
values=" 0.000, 200.000, 300;
-3.922, 207.202, 300;
-7.386, 227.545, 300;
-9.974, 257.557, 300;
-11.359, 292.430, 300;
-11.359, 327.159, 300;
-9.974, 357.557, 300;
-7.386, 380.753, 300;
-3.922, 395.140, 300;
0.000, 400.000, 300;
3.922, 395.140, 300;
7.386, 380.753, 300;
9.974, 357.557, 300;
11.359, 327.159, 300;
11.359, 292.430, 300;
9.974, 257.557, 300;
7.386, 227.545, 300;
3.922, 207.202, 300;
0.000, 200.000, 300"/>
</use>
</svg>
Я пытаюсь воспроизвести эту анимацию движения кривошипного механизма с помощью svg и javascript.
Ползунку нужна анимация перемещения, а кривошипу - анимация вращения, но как я могу получить анимацию шатуна? В svg есть значения, заданные для animate и animateTransform, но есть ли уравнение, которое позволяет нам воспроизвести это?
Примечание переводчика:
То есть нужно универсальное решение, которое позволит при разных входных данных: размеры кривошипа, шатуна и длины хода ползунка реализовать анимацию всего кривошипно-шатунного механизма.
Есть ли у кого-нибудь идеи?
Свободный перевод вопроса crank motion with svg от участника @sonia maklouf.
Ответы (2 шт):
Координаты B можно через тригонометрию Параметрическое уравнение окружности
x= cos(ang_rad)*radius
y= sin(ang_rad)*radius
Координаты C можно погуглить «пересечение окружности и прямой” пересечение окружности радиуса BC и прямой CA. Пересечений будет 2 выбрать где меньший X.
Если без детальной прорисовки, то можно так. Меняем угол вращения колена кривошипа (синяя линия), тем самым получаем точку соединения c шатуном (зеленая). Исходя из длины шатуна находим точку соединения с ползуном (зеленый круг), который движется по постоянному пути (желтая линия).
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width = 600;
const h = canvas.height = 180;
{
const center = [w*2/3,h/2];
const joint_radius = 10;
const crank_len = 80;
const rotation_speed = Math.PI/180;
const conrod_len = 200;
let angle = 0;
ctx.lineWidth = 5;
draw();
function draw(){
ctx.clearRect(0,0,w,h);
ctx.strokeStyle = 'yellow';
ctx.stroke(new Path2D(`M ${center[0]+crank_len-conrod_len} ${center[1]} h ${-crank_len*2}`));
ctx.fillStyle = 'red';
ctx.beginPath();
ctx.arc(...center,joint_radius,0,2*Math.PI);
ctx.fill();
let crank_x = center[0] + Math.cos(angle)*crank_len;
let crank_y = center[1] + Math.sin(angle)*crank_len;
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(crank_x,crank_y,joint_radius,0,2*Math.PI);
ctx.fill();
ctx.strokeStyle = 'blue';
ctx.stroke(new Path2D(`M ${center[0]} ${center[1]} L ${crank_x} ${crank_y}`));
let crosshead_x = crank_x - Math.sqrt(conrod_len**2 - (crank_y-center[1])**2);
let crosshead_y = center[1];
ctx.fillStyle = 'green';
ctx.beginPath();
ctx.arc(crosshead_x,crosshead_y,joint_radius,0,2*Math.PI);
ctx.fill();
ctx.strokeStyle = 'green';
ctx.stroke(new Path2D(`M ${crank_x} ${crank_y} L ${crosshead_x} ${crosshead_y}`));
angle = angle%(2*Math.PI) + rotation_speed;
requestAnimationFrame(draw);
}
}
<canvas></canvas>
UPD Можно таким же образом манипулировать атрибутами в SVG. Некоторые настройки можно менять в разумных пределах (помечены комментариями).
{
const svg = document.querySelector('svg');
const crank = svg.querySelector('#crank');
const crosshead = svg.querySelector('#crosshead');
const conrod = svg.querySelector('#conrod');
const bar_rect = svg.querySelector('#slidebar');
const bar1 = svg.querySelector('#bar1');
const bar2 = svg.querySelector('#bar2');
// Эти параметры можно изменять (в разумных пределах)
const center = [800,150];
const crank_len = 100;
const conrod_len = 500;
const crosshead_len = 100;
const rotation_speed = 1;
// Дальше ничего менять не надо))
bar_rect.setAttribute('width', crank_len*2 + crosshead_len);
bar1.setAttribute('y', center[1] - 37.5);
bar1.setAttribute('x', center[0] - crank_len - conrod_len - crosshead_len/2);
bar2.setAttribute('y', center[1] + 37.5);
bar2.setAttribute('x', center[0] - crank_len - conrod_len - crosshead_len/2);
crosshead.querySelector('rect').setAttribute('x', -crosshead_len/2);
crosshead.querySelector('rect').setAttribute('width', crosshead_len);
let angle = 0;
draw();
function draw(){
crank.setAttribute('transform', `translate(${center[0]},${center[1]}) rotate(${angle+180}, 0, 0) scale(${crank_len/100})`);
let crank_x = center[0] + Math.cos(angle*(Math.PI/180))*crank_len;
let crank_y = center[1] + Math.sin(angle*(Math.PI/180))*crank_len;
let crosshead_x = crank_x - Math.sqrt(conrod_len**2 - (crank_y-center[1])**2);
let crosshead_y = center[1];
crosshead.setAttribute('transform', `translate(${crosshead_x},${crosshead_y})`);
let conrod_angle = Math.acos((crank_x-crosshead_x)/conrod_len)*(180/Math.PI);
conrod_angle = (crank_y > center[1])? conrod_angle : -conrod_angle;
conrod.setAttribute('transform', `translate(${crosshead_x},${crosshead_y}) rotate(${conrod_angle},0,0) scale(${conrod_len/500})`);
angle = angle%(360) + rotation_speed;
requestAnimationFrame(draw);
}
}
<svg viewBox="0 0 1100 600">
<defs>
<rect
id="slidebar"
stroke-width="1"
stroke="black"
fill="silver"
fill-opacity="1"
x="0"
y="-12"
width="300"
height="24"
/>
</defs>
<use id="bar1" xlink:href="#slidebar"/>
<use id="bar2" xlink:href="#slidebar"/>
<g id="crosshead" stroke-width="1" stroke="black" fill-opacity="1">
<rect
fill="gold"
x="-50"
y="-25"
width="100"
height="50"
/>
<circle cx="0" cy="0" r="15" fill="white"/>
</g>
<g id="crank" stroke-width="1" stroke="black" fill-opacity="1">
<path fill="silver"
d="M -99.959 40.000
A 40 40 0 0 1 -99.959, -40.000
A 450 450 0 0 0 -9.950, -49.000
A 50 50 0 1 1 -9.950, 49.000
A 450 450 0 0 0 -99.959, 40.000
z"/>
<circle cx="-100" cy="0" r="25" fill="white"/>
<circle cx="0" cy="0" r="30" fill="lightgrey"/>
</g>
<g id="conrod" stroke-width="1" stroke="black" fill-opacity="0.7">
<path fill="silver"
d="M 12.387 21.715
A 30 30 0 0 1 27.551 17.776
L 453.475 22.035
A 30 30 0 0 1 473.243 29.733
A 40 40 0 0 1 473.243 -29.733
A 30 30 0 0 1 453.475 -22.035
L 27.551 -17.776
A 30 30 0 0 1 12.387 -21.715
A 25 25 0 0 1 12.387 21.715
z"/>
<circle cx="0" cy="0" r="25" fill="silver"/>
<circle cx="0" cy="0" r="15" fill="white"/>
<circle cx="500" cy="0" r="40" fill="silver"/>
<circle cx="500" cy="0" r="25" fill="white"/>
</g>
</svg>
