Home > Net >  How to animate spining wheel in CSS?
How to animate spining wheel in CSS?

Time:02-11

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.

CodePudding user response:

Note, following answer content may be subject to edits in the near future, and is only intended as a starting point to assist the OP's author.

I believe what ya may need for turning the crank is the transform-origin CSS property.

In regards to rotate() direction, the StackExchange -- How to determine direction of rotation in CSS3 transitions? answers may be of use. TLDR; positive numbers rotate clockwise, and negative numbers rotate anticlockwise, however this may be modified via from configuration.

I'll dig into the source a bit over the next few minutes and attempt a more complete answer.


Updates

You were very close to a solution! From what I spotted the #handle element was missing an animation property/value, and the @keyframes handle_rotate needed defined.

Here's a bit of code that should get ya a little closer to a full solution...

:root {
    --time: 5s;
    --handle-translate-x: 291.59718px;
    --handle-translate-y: 210.10511px;
    --handle-rotate-start: 20deg;
    --handle-transform-origin-x: 4.742px;
    --handle-transform-origin-y: 15.799px;
}
/* ... */
#handle {
    animation: handle_rotate var(--time) linear infinite;
    transform-origin: var(--handle-transform-origin-x) var(--handle-transform-origin-y);
    transform:
        translate(var(--handle-translate-x), var(--handle-translate-y))
        rotate(var(--handle-rotate-start));
}
/* ... */
@keyframes handle_rotate {
    to {
        transform:
            translate(var(--handle-translate-x), var(--handle-translate-y))
            rotate(calc(var(--handle-rotate-start)   360deg));
    }
}

... Though be aware values still need a bit of fiddling with to get all the parts to sync-up with one another. In this case I'd suggest leveraging CSS Custom Properties (variables)

CodePudding user response:

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>

  • Related