5

У меня есть гифка работы кулачкового механизма:

введите сюда описание изображения

Эта иллюстрация показывает, что при равномерном вращении вала с насаженным на него кулачком, толкатель получает неравномерные возвратно-поступательные движения толкателя, который скользит по поверхности кулачка.

Я воспроизвел основные контуры этого механизма.

введите сюда описание изображения

Вот мой код статичного механизма:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="xMinYMin meet"  id="svg4" width="390" height="700" viewBox="0 0 390 700">
       &lt;!-- Верхняя скоба --&gt;

<g stroke="#717171" stroke-width="2" fill="none" > <path d="M149 124h87v31h-87z" /> <path d="M175 124v31" /> <path d="M210 124v31" /> <path d="M161 134c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z"/> <path d="M221 134c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z" /> </g>

         &lt;!-- Нижняя скоба --&gt;

<g stroke="#717171" stroke-width="2" fill="none" >

<path id="circ910" d="M221 278c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z"/> <path id="circ912" d="M161 278c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z"/> <path d="M149 267h87v31h-87z" /> <path d="M175 267v31" id="path916" /> <path d="M210 267v31" id="path918" /> </g> <!-- Кулачок --> <g ig="gr1" stroke-width="2" > <path id="cam" fill="#8080B1" stroke="black" fill-opacity="0.5" d="M83 493c1-7 19-23 32-30 10-6 34-10 34-10 6-5 35-42 59-48 21-5 60-15 65-10 11 15 21 57 10 93-4 12-10 27-18 39-5 8-14 14-20 22l-5 9s-4 12-4 19c0 20-9 36-20 49-10 12-32 26-39 24-6-1-21-18-28-30-13-21-22-38-24-69 0-4-19-14-26-23-7-10-17-27-16-35z" />

<circle id="path866" cx="192" cy="514.6" r="36" fill="#5F5F5F" stroke="black" stroke-width="2" /> <circle id="spindle" cx="192" cy="514.6" r="20" fill="#4A4A4A" stroke="black" stroke-width="2" /> <!-- Шпонка --> <path id="key" fill="#151515" stroke="black" d="M189 491h6v10h-6v-10h6" />

  &lt;/g&gt;  
                  &lt;!-- Толкатель --&gt;

<path id="kernel" d="M184 60v340l8 14 7-14V59z" fill="none" stroke="red" stroke-width="2" >

</path> <circle id="path868" cx="192" cy="514.4" r="144.7" opacity=".5" fill="none" fill-opacity="1" stroke="#1515B1" stroke-width="2" />

</svg>

Попробовал сделать анимацию кулачкового механизма:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" preserveAspectRatio="xMinYMin meet"  id="svg4" width="390" height="700" viewBox="0 0 390 700">
       &lt;!-- Верхняя скоба --&gt;

<g stroke="#717171" stroke-width="2" fill="none" > <path d="M149 124h87v31h-87z" /> <path d="M175 124v31" /> <path d="M210 124v31" /> <path d="M161 134c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z"/> <path d="M221 134c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z" /> </g>

         &lt;!-- Нижняя скоба --&gt;

<g stroke="#717171" stroke-width="2" fill="none" >

<path id="circ910" d="M221 278c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z"/> <path id="circ912" d="M161 278c3 0 6 3 6 6s-3 6-6 6c-2 0-5-4-5-6 0-3 3-6 5-6z"/> <path d="M149 267h87v31h-87z" /> <path d="M175 267v31" id="path916" /> <path d="M210 267v31" id="path918" /> </g>

      &lt;g  ig="gr1"  stroke-width="2"   &gt;  
                &lt;!-- Кулачок --&gt;

<path id="cam" fill="#8080B1" stroke="black" fill-opacity="0.5" d="M83 493c1-7 19-23 32-30 10-6 34-10 34-10 6-5 35-42 59-48 21-5 60-15 65-10 11 15 21 57 10 93-4 12-10 27-18 39-5 8-14 14-20 22l-5 9s-4 12-4 19c0 20-9 36-20 49-10 12-32 26-39 24-6-1-21-18-28-30-13-21-22-38-24-69 0-4-19-14-26-23-7-10-17-27-16-35z" />
<!-- Вал --> <circle id="path866" cx="192" cy="514.6" r="36" fill="#5F5F5F" stroke="black" stroke-width="2" /> <circle id="spindle" cx="192" cy="514.6" r="20" fill="#4A4A4A" stroke="black" stroke-width="2" />

  &lt;path id="key" fill="#151515" stroke="black" d="M189 491h6v10h-6v-10h6"  /&gt;  
                         &lt;!-- Анимация вращения вала с кулачком --&gt;
   &lt;animateTransform attributeName="transform" type="rotate" begin="0s" dur="2s" values="0 192 514.6;360 192 514.6" repeatCount="indefinite" /&gt;  

 &lt;/g&gt;  
                        &lt;!-- Толкатель --&gt;

<path id="kernel" d="M184 60v340l8 14 7-14V59z" fill="none" stroke="red" stroke-width="2" > <!-- Анимация перемещения толкателя --> <animateTransform attributeName="transform" type="translate" begin="0s" dur="2s" calcMode="linear" values="0 0;0 30;0 0" repeatCount="indefinite" />
</path> <circle id="path868" cx="192" cy="514.4" r="144.7" fill="none" fill-opacity="1" stroke="#1515B1" stroke-width="2" />

</svg>

Из работы программы видно, что хоть время начала анимаций и их продолжительность совпадает, но не получается добиться согласованной работы вращения кулачка и возвратно-поступательного движения толкателя.

Как согласовать анимацию вращения кулачка и неравномерное движение толкателя, которое зависит от профиля поверхности кулачка.

Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384

1 Answers1

5

Отобразил только ключевые элементы. Пути переделал так, чтобы ось вращения диска с кулачками была на [0,0]. Для толкателя нулевой точкой сделал нижний кончик.

После отрисовки кулачка находим точку пересечения перебором координат y (при неизменной x равной центру вращения) пока эта точка не совпадет с контуром пути: ctx.isPointInStroke(cam,x,y).

render();

function render(){

// Описание путей диска с кулачками (cam) и игольчатого толкателя (kernel)
const cam_path = 'M-39.89 -11.27c0.39,-2.73 7.4,-8.97 12.47,-11.7 3.9,-2.33 13.25,-3.89 13.25,-3.89 2.34,-1.95 13.64,-16.37 23,-18.71 8.18,-1.95 23.38,-5.85 25.33,-3.9 4.29,5.85 8.19,22.22 3.9,36.25 -1.56,4.68 -3.9,10.52 -7.01,15.2 -1.95,3.12 -5.46,5.46 -7.8,8.58l-1.95 3.5c0,0 -1.56,4.68 -1.56,7.41 0,7.8 -3.51,14.03 -7.79,19.1 -3.9,4.68 -12.48,10.13 -15.21,9.35 -2.33,-0.39 -8.18,-7.01 -10.91,-11.69 -5.07,-8.18 -8.57,-14.81 -9.35,-26.89 0,-1.56 -7.41,-5.46 -10.14,-8.97 -2.73,-3.9 -6.62,-10.52 -6.23,-13.64l0 0z';
const kernel_path = 'M-4 -70c2.67,0 5.33,0 8,0 0,21.54 0,43.08 0,64.62 -1.34,1.79 -2.67,3.58 -4,5.38 -1.33,-1.8 -2.67,-3.59 -4,-5.38 0,-21.54 0,-43.08 0,-64.62z';

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const w = canvas.width = 600;
const h = canvas.height = 190;

const cam = new Path2D(cam_path);       // Путь кулачка
const kernel = new Path2D(kernel_path); // Путь толкателя

let angle = 0;  // Угол вращения сцены (для кулачка)      

animate();      // Функция будет вызывать сама себя на каждом кадре (помещать в очередь на отрисовку)

function animate(){
    ctx.clearRect(0,0,w,h);                 // Очистка холста

    ctx.save();                            // Сохраняем настройки контекста
    ctx.translate(w/2, 129);               // Центр вращения
    ctx.rotate(angle * Math.PI/180);       // Вращаем полотно
    ctx.fillStyle = 'rgb(128, 128, 177)';  // Указываем стиль заливки
    ctx.fill(cam);                         // Заливаем путь кулачка
    ctx.stroke(cam);                       // Контур кулачка

    ctx.beginPath();                       // Внешняя окружность (вал?)
    ctx.arc(0,0,60,0,Math.PI*2,true);
    ctx.strokeStyle = 'rgb(100,0,180)';
    ctx.stroke();

    ctx.beginPath();                       // Шток (ось)
    ctx.arc(0,0,16,0,Math.PI*2,true);
    ctx.fillStyle = 'grey';
    ctx.strokeStyle = 'black';
    ctx.fill();
    ctx.stroke();
    ctx.beginPath();
    ctx.arc(0,0,10,0,Math.PI*2,true);
    ctx.stroke();

    ctx.fillStyle = 'black';                // Шпонка
    ctx.fillRect(7,-2,6,4);

    angle = angle%360 + 2;                  // Изменение угла за кадр

    let kernel_position = 0                 // Позиция кончика толкателя (инициализируем 0)
    while(!ctx.isPointInStroke(cam,w/2,kernel_position)){   // Пока точка не совпадет с контуром кулачка
        kernel_position++;                                  // Добавляем к позиции по Y
    }
    ctx.restore();                          // Возвращаем настройки контекста (translate, rotate, fillStyle, strokeStyle)

    ctx.save();
    ctx.translate(w/2, kernel_position);    // Позиция кончика толкателя
    ctx.fillStyle = 'red';
    ctx.fill(kernel);                       // Заливка толкателя
    ctx.stroke(kernel);                     // Контур толкателя
    ctx.restore();

    requestAnimationFrame(animate);         // Помещаем в очередь на отрисовку на следующем кадре
}

}

    <canvas></canvas>
Leonid
  • 5,797
  • Хорошее, полезное решение. Особая благодарность за подробные комментарии, облегчающие процесс понимания работы программы. Просьба объяснить подробней вот эту строчку -angle = angle%360 + 2; И ещё просьба, для ,большего реализма- дать по возможности, второй вариант программы, где нет необходимости перерисовывать path, чтобы поместить элементы в нулевые координаты по y Может я что-то не так понял? Но ведь чем-то была вызвана необходимость изначальной перерисовки path – Alexandr_TT Nov 10 '21 at 07:55
  • angle%360 - остаток от деления предыдущего значения angle на 360. Таким образом, при значениях меньше 360 ничего не меняется, при 360 => 0, при 362 => 2 (362 не случится) и так далее. +2 - реальное изменение на 2 градуса за кадр. Все вместе создает бесконечный ряд: 0, 2, 4, ..., 356, 358, 0, 2.... Это стандартный способ создать "замкнутый круг" значений для циклическиx анимаций, например. – Leonid Nov 10 '21 at 12:46
  • Путь я не то чтобы менял, просто для вращения и одновременного перемещения, а также для возможности адекватного масштабирования гораздо лучше если нулевые координаты будут совпадать с осью вращения. В редакторе я просто переместил замкнутую и отмасштабировал ее чтобы она помещалась в малое окно просмотра. Сама фигура не менялась. Толкатель мне проще было перерисовать, и опять же сразу так, чтобы опорная точка при translate совпала с кончиком. Если бы я захардкодил отступ вниз, то при масштабировании пришлось бы менять это значение. А так можно все отмасштабировать без потерь. – Leonid Nov 10 '21 at 12:52
  • спасибо. Вроде понял. А так здорово получилось. Толкатель как приклеенный скользит по профилю кулачка и ускорения, замедления его реально видно. – Alexandr_TT Nov 10 '21 at 12:59
  • посмотри на EnSO вопрос по дугам, мне кажется тебе нетрудно будет ответить https://stackoverflow.com/q/69914561/7394871 – Alexandr_TT Nov 10 '21 at 16:41
  • мне кажется, что уже с этого места идёт ошибка line.setAttribute("stroke-dasharray", length + ' ' + circumference); Не может пробел в stroke-dasharray быть равным длине окружности – Alexandr_TT Nov 10 '21 at 16:52
  • stroke-dasharray по горло достаточно и arc посмотри вот это сборник ответов, если хватит терпения хотя бы поверхностно прочесть со stroke-dasharray не будет проблем https://ru.stackoverflow.com/q/943685/28748 – Alexandr_TT Nov 10 '21 at 17:39