Search code examples
javascriptcssanimationcss-transforms

How to animate spining wheel in CSS?


I don't know how to form the question. I have old animation that was done in Flash, that I'm not able to translate to CSS animation.

This is an animation of a Spinning Wheel.

The question is: How can I implement the CSS animation for the crank/handle that drives the wheel? It should work like a Piston but connected to a circe in id="rotator" (inside SVG). The handle is located at 0,0 on the image above. Both small circles should match when the rotator is rotating. I already tried to use translate, and used transform-origin but I have no idea how to implement the animation. I have Action Script code as a reference, but I don't know how to map it to CSS or SCSS.

I was able to convert SWF into SVG, using pyswf. And I have a hard time understanding the math behind the logic I've created long ago and translating it into CSS (I've also tried with JS approach).

The code in Action Script I've extracted long ago, probably some decompiler from the swf file.

// copyright (c) 2006 Jakub "JCubic" Jankiewicz
var
    factor1 = 180.0 / Math.PI,              // współczynniki dla zamiany
    factor2 = Math.PI / 180.0;              // radiany stopinie i odwrotnie
    
// radiany na katy
function rad2deg(rad){
    return rad * factor1;
}
// katy na radiany
function deg2rad(deg){
    return deg * factor2;
}
// dlugosc odcinka
function line_length(x1, y1, x2, y2){
    return Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}
// funkcja testowa przenoszaca krzyzyk
function show(x, y){ cross._x = x; cross._y = y; }  
// funkcja testowa rysująca linie
function line_(x1, y1, x2, y2, ang){
    line._height = Math.sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
    line._x = (x2 + x1) / 2;
    line._y = (y2 + y1) / 2;
    line._rotation = ang;
}

function getSpeed(){
    return step;
}

function setSpeed(value){
    // inicjacja zmiennych
    var Ox = 0, 
        Oy = 360,                               // punkt zaczepienia
        r = 83.0,                               // promien odleglosc od rotatora    
        angle = 0, 
        step = value,
        rad = deg2rad(step),                    // cosinnus i sinus stopnia 
        sinA = Math.sin(rad),                   // jednostkowego
        cosA = Math.cos(rad),
        sinAlpha;
    
    onEnterFrame = function() { 
        if (angle == 0) {
            napedzacz._x = 0;       // poczatek obrotu 
            napedzacz._y = r;   
        } else {
            Bx = napedzacz._x * cosA + napedzacz._y * sinA;
            By = napedzacz._y * cosA - napedzacz._x * sinA;
            napedzacz._x = Bx;
            napedzacz._y = By;
            sinAlpha = (Ox - Bx) / line_length(Bx, By, Ox, Oy);
            napedzacz._rotation = - rad2deg(Math.asin(sinAlpha));
            kolo._rotation = 180 - angle;
            szpulka._rotation = 180 - angle * 2;
            rotator._rotation = 180 - angle;
        }
        angle = (angle+step) % 360;
    }
}

It has Polish comments but they don't explain how to modify the code.

Here is my CodePen demo where I've tried to create SVG+CSS animation. I was able to rotate part of the spinning wheel, but don't know how to animate the handle (that is used to drive the wheel).

I've wanted to use SCSS and trigonometry functions (included in Pen) to generate every frame of the animation, but I'm not sure how I should go about it.

I have the original SWF file but I'm not able to play it, since the only SWF player I've found doesn't execute code. And I'm not able to install Gnash on Fedora (even that I've written article how to do that long go, the solution doesn't work anymore). That's why I want to create something modern with SVG.

If you have something to open the SWF file here is the link to the original file:

https://jcubic.pl/kolowrotek.swf

the problem is that constants in the Action Script code use different coordinate systems and different scales, I have no idea how to map that code into JavaScript (I was able to run it, but it has issues, there are two commented-out lines in Pen). I also have no idea why the animation rotate in different direction than CSS, I was not able to reverse it. I would prefer not to use JavaScript since it will hard to match CSS animation with JavaScript, and doing aninmation in JavaScript make some delay when handle is in wrong position.


Solution

  • This is a basic implementation of the movement that you want.

    I have set only keyframes at every 90 deg , it will get better as you add values.

    I have guessed this values, bout you should calculate them and set the appropiate values in the counter rotation.

    #wheel {
      width: 300px;
      height: 300px;
      border-radius: 100%;
      border: solid 1px blue;
      animation: rotate 15s infinite linear;
    }
    
    #dot {
      width: 5px;
      height: 5px;
      border-radius: 100%;
      border: solid 1px red;
      position: absolute;
      top: 2px;
      left: 150px;
    }
    
    #arm {
      width: 500px;
      height: 5px;
      border: solid 1px green;
      position: absolute;
      top: 2px;
      left: 150px;
      transform-origin: 0px 0px;
      animation: crotate 15s infinite linear;
    }
    
    @keyframes rotate {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    
    @keyframes crotate {
      0% {
        transform: rotate(15deg);
      }
      25% {
        transform: rotate(-90deg);
      }
      50% {
        transform: rotate(-195deg);
      }
      75% {
        transform: rotate(-270deg);
      }
      99.99% {
        transform: rotate(-345deg);
      }
      100% {
        transform: rotate(15deg);
      }
    }
    <div id="wheel">
      <div id="dot"></div>
      <div id="arm"></div>
    </div>