• Animate bicycle SVG with CSS

    Animate bicycle SVG with CSS

    By: Emil Firšt

    Did you know that front-end development can be fun? Off-course it is fun 🙂 CSS3 allow us to be fully creative by creating cool, outstanding animations and effects. By using simple CSS properties you can give new dimension to your web site and make your users their user experience more friendly and enjoyable.

    In this tutorial we will show you how to make animated SVG bicycle. At the first sight animation looks very complex but, actually, it’s about using and reusing simple CSS properties applied to different elements.

     

    VIEW DEMO

    Draw bicycle

    First we need to create an illustration of bicycle. To draw any illustration is not the easiest if you don’t have enough skills. The one way to do it is to draw over the existing image, off-course, if you want to use it for commercial purposes you need to have a licence. For creating illustrations we recommend to use vector based software like Adobe Illustrator.

    Note that shapes and paths have different behaviours when animating so we recommend to play with paths and shapes first to see how they behave on different CSS properties.

    Before you start drawing you need to know which parts you want to animate. To manipulate elements you need to place each one to a new layer. The good habit is to name layers because when you export SVG the elements will be separated in different XML elements with the ID which is name of the layer in a tool you used. You can also add additional classes to elements if you like.

    Let’s say you want to create an animated line which has direction from left to right. First you need to draw a line with pen tool but you should keep in mind that SVG paths are directional which means that its direction is written in SVG markup and looks like this:

    <path d="M326.5,456.1c0,0-55-37-129,13.5"></path>
    

    So if you want it to go from left to right you should draw it from left to right. If you want to change the direction you can do it by using direct selection tool.

    The other thing is that if you change color of the drawn element that will be also written in code and you can manipulate with it through CSS.

    Create SVG

    When you’re finished with illustration you need to save it as SVG file.

    save-svg-1

    Click save, check embed and confirm with ok:

    save-svg-2

     

    SVG structure

    SVG file contains XML markup. You can see it by opening the file in your favourite text editor. All elements you have drawn in Illustrator are here with its attributes.

    The SVG structure looks like this:

    
    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
    
    
    <style type="text/css">
    	.st2{fill:#504F5F;}
    	.st3{fill:none;stroke:#FE5A5C;stroke-width:28;stroke-miterlimit:10;}
    	.st4{fill:#F3E5C8;}
    	.st5{fill:none;stroke:#504F5F;stroke-width:30;stroke-linecap:round;stroke-miterlimit:10;}
    	.st6{fill:#FBE367;}
    	.st7{fill:#FDC825;}
    	.st8{display:inline;fill:none;stroke:#3EE0D2;stroke-width:22;stroke-linecap:round;stroke-miterlimit:10;}
    	.st9{display:inline;fill:none;stroke:#3EE0D2;stroke-width:22;stroke-miterlimit:10;}
    	.st10{fill:none;stroke:#3EE0D2;stroke-width:22;stroke-linecap:round;stroke-miterlimit:10;}
    	.st11{fill:none;stroke:#89A9AD;stroke-width:30;stroke-linecap:round;stroke-miterlimit:10;}
    	.st12{fill:none;stroke:#456F72;stroke-width:30;stroke-linecap:round;stroke-miterlimit:10;}
    	.st13{fill:none;stroke:#504F5C;stroke-width:30;stroke-linecap:round;stroke-miterlimit:10;}
    	.st14{fill:none;stroke:#3EE0D2;stroke-width:22;stroke-miterlimit:10;}
    	.st15{fill:none;stroke:#504F5C;stroke-width:28;stroke-linecap:round;stroke-miterlimit:10;}
    	.st16{fill:none;stroke:#504F5C;stroke-width:17;stroke-linecap:round;stroke-miterlimit:10;}
    	.st17{fill:none;stroke:#89A9AD;stroke-width:71;stroke-linecap:round;stroke-miterlimit:10;}
    </style>
    
    
    
    <g id="s_circle">
    	<circle class="st2" cx="274.5" cy="571.1" r="29"></circle>
    </g>
    <g id="mudguard-L">
    	<path class="st3 path-line" d="M326.5,456.1c0,0-55-37-129,13.5"></path>
    </g>
    <g id="mudguard-R">
    	<path class="st3 path-line" d="M675.5,456.1c0,0,79-37.5,137.5,26"></path>
    </g>
    <g id="shadows">
    	<ellipse class="st4 shadow-l" cx="274.5" cy="685.9" rx="121" ry="19.2"></ellipse>
    	<ellipse class="st4 shadow-r" cx="725.5" cy="685.9" rx="121" ry="19.2"></ellipse>
    </g>
    <g id="wheels">
    	<circle class="st5 wheel-l" cx="273.5" cy="570.6" r="101"></circle>
    	<circle class="st5 wheel-r" cx="724.5" cy="570.6" r="101"></circle>
    </g>
    <g id="basket">
    	<g>
    		<path class="st6 basket-part1" d="M400.1,300.1H323v51.7C388,351.8,399.2,304.5,400.1,300.1z"></path>
    		<path class="st7 basket-part2" d="M433.5,312l2.6-11.9l0.1-0.3h-36c0,0,0,0.1-0.1,0.3c-0.9,4.4-12.1,51.7-77.1,51.7v19v1h66.5l0,0 c0,0,19-6.2,31-24.7c8.3-12.8,11.7-29.5,11.7-29.5L433.5,312z"></path>
    	</g>
    </g>
    <g id="steeringwheel">
    	<path class="st10 path-line" d="M374.5,371.8c0,0,28.3-5.3,51-44c18.6-31.8,38-34,56-32.7"></path>
    </g>
    <g id="handle">
    	<line class="st11" x1="481.5" y1="295.8" x2="507.8" y2="295.8"></line>
    	<line class="st12" x1="507.8" y1="295.4" x2="507.8" y2="295.4"></line>
    </g>
      <g id="frame1">
    	<line class="st10" x1="277" y1="567.6" x2="374" y2="371.6"></line>
    </g>
    <g id="frame2">
    	<path class="st10 path-line" d="M374.5,382.3c0,55.3,27.7,152,100.3,174.7"></path>
    </g>
    <g id="frame3">
    	<line class="st14" x1="655" y1="392.6" x2="531" y2="562.6"></line>
    </g>
    <g id="back-pedal-bar">
    	<line class="st15 pedal-back" x1="478.5" y1="626.3" x2="510.5" y2="626.3"></line>
    	<line class="st16" x1="494.5" y1="615.1" x2="494.5" y2="576.1"></line>
    </g>
    <g id="pedal-box">
    	<line class="st17" x1="723.5" y1="571.1" x2="458.5" y2="571.1"></line>
    </g>
    <g id="frame4">
    	<line class="st10" x1="724.5" y1="572.3" x2="645.4" y2="408.1"></line>
    </g>
      <g id="seat">
    	<line class="st13" x1="632.5" y1="366.1" x2="709" y2="366.1"></line>
    </g>
    <g id="front-pedal-bar">
    	<line class="st16" x1="494.7" y1="570.6" x2="494.7" y2="529.4"></line>
    	<line class="st15 pedal-front" x1="479.5" y1="516.1" x2="511.5" y2="516.1"></line>
    </g>
    </svg>
    

     

    Start animating

    Frame

    What we want to do first is to animate all elements that we want to look like they are lines drawn from point A to point B and which have common properties like animation-duration, animation-name, animation-fill-mode, animation-delay and animation-iteration-count. Those elements are bicycle frame, pedal box and all paths.

    Take a look at CSS:

    svg .path-line,
    svg #frame1 line,
    svg #frame4 line,
    svg #pedal-box line { 
        opacity:0;
        stroke-dasharray: 700; 
        stroke-dashoffset: 700;
        animation: 2.5s cycle forwards;
        animation-delay: 0.8s;
        animation-iteration-count: 1;
    }
    

    As you can see all paths has “path-line” class and frame is separated on four parts. Frame 1 and frame 4 have common properties. Frame 2 and frame 3 are separated because of different animation-duration and animation-delay. We’ll check them a little bit later.

    To make smooth appearing transition we need to make element invisible so we set initial opacity to zero. After that we have two attributes, stroke-dasharray and stroke-dashoffset.

    Stroke-dasharray attribute allow us to create dashed lines. With stroke-dashoffset attribute we define distance in dash pattern. In practice, we combine those attributes to get kind of lines that suit our needs. We set them to 700 to have solid line.

    Then we have animation property where we bind the animation to an element, set its duration and fill mode. But to use it we should first add some keyframes. With keyframes we define how, where and when will the animation change the element’s style. Keyframes can be added with @keyframes rule and we also should assign animation name to it.

    This is example of cycle animation keyframes:

    @keyframes cycle {
        0%{ opacity:0;
            stroke-dashoffset: 700;
        }
        5%{
            opacity:1;
        }
        100%{
            opacity:1;
            stroke-dashoffset: 0;
        }
    }
    

    As you can see we used percentage of completion to define the points of change. It means: at the starting point (0%) animation is invisible and has stroke-dashoffset value 700. When the animation is 5% complete it become visible and when the animation is complete (100%) stays visible and has stroke-dashoffset value 0. Stroke-dashoffset change value from 700 to 0 which give us the effect of drawing. Note that you can have as many points with changes you want.

    There is also another way of specifying the points of change. It’s about using keywords from and to where from is 0% and to is 100% of animation completion.

    Once you define keyframes you can bind them to more elements.

    So now when we’ve added keyframes let’s explain our animation property from previous example:

    animation: 2.5s cycle forwards;
    

    We have set animation-duration to 2.5 seconds because we wanted it to last that long. We also have assigned animation-name cycle and set animation-fill-mode to forwards. Here we have some new property called animation-fill-mode. It defines how CSS animation should apply styles to its target before and after it’s playing. We set the property value to forwards because we want to apply the property values for the time the animation ended. Check out for more properties on W3Schools.

    Then we set up animation-delay to 0.8 seconds because we want the animation of selected elements to start 0.8 seconds later. Animations applied to other elements will have different animation-delay values to achieve finer animation flow.

    At the end we should define how many times our animation will be played. The property is called animation-iteration-count. The value can be a number (1,2,3,4 etc), infinite (play forever), inherit (inherits the property from parent element) or initial (default value 1).

    Next elements are Frame 2 and Frame 3 which has the same properties but with different values:

    svg #frame2 path {
        opacity:0;
        stroke-dasharray: 700; 
        stroke-dashoffset: 700;
        animation: 2s cycle forwards;
        animation-iteration-count: 1;
        animation-delay: 1.2s;
    }
    
    svg #frame3 line {
        opacity:0;
        stroke-dasharray: 700; 
        stroke-dashoffset: 700;
        animation: 1.5s cycle forwards;
        animation-iteration-count: 1;
        animation-delay: 1.2s;
    }
    

     

    Steering wheel

    After we have animated bicycle frame let’s move to steering wheel. We want it to draw continuing from the frame and rotate for 10 degrees. The CSS code is:

    svg #steeringwheel path {
        animation: 2s cycle forwards, 1.4s steeringwheel-rotate forwards;
        animation-delay: 1.4s, 1.4s;
        transform-origin: 400px 400px;
        animation-iteration-count: 1, 1;
    }
    
    @keyframes steeringwheel-rotate {
        10%{
            transform: rotate(10deg);
        }
        60%{
            transform: rotate(0deg);
        }
    }
    
    

    Steering wheel has applied two animations: cycle and steeringwheel-rotate. In the midst of well known properties you can see transform-origin property not mentioned before. It allows you to change the position of transformed elements. Its values are related to X- and Y- axis so you should play with it until you get what you want. We set it to x400px, y400px.

    Take a look at the @keyframes rule for the animation called steeringwheel-rotate. As its name say it contains keyframes with transform property and rotate method. What we want is to rotate it for 10 degrees when the animation is 10% complete and go back on 0 degree when it’s 60% complete.

    The steering wheel contains a handle so we want to animate it too.

    svg #handle line:nth-child(1) {
        opacity:0;
        stroke-dasharray: 100;  
        stroke-dashoffset: 100;
        animation: 1s handle-line forwards, 1s rotate-handle forwards;
        animation-iteration-count: 1, 1;
        animation-delay: 1.8s, 1.5s;
        transform-origin: 470px 270px; 
    }
    
    svg #handle line:nth-child(2) {
        opacity: 0;
        animation: 0.62s show-handle forwards;
        animation-iteration-count: 1, 1;
        animation-delay: 1.86s;
        transform-origin: 470px 270px; 
    }
    

    Our handle consists of two lines so we need to select each one using :nth-child(). First element has the same properties we explained before and has applied handle-line and rotate-handle animations. handle-line animation give us appearing and drawing line effect and has @keyframe role:

    @keyframes handle-line {
        0%{
            opacity:0;
            stroke-dashoffset: 100;
        }
        5%{
            opacity:1;
            stroke-dashoffset: 0;
        }
        100%{
            opacity:1;
            stroke-dashoffset: 0;
        }
    }
    

    rotate-handle animation rotates has @keyframe role:

    @keyframes rotate-handle {
        0%{
            transform: rotate(10deg) translate(0,11px);
        }
        50%{
            transform: rotate(20deg) translate(5px, 0);
        }
        70%{
            transform: rotate(0deg);
        }
    }
    

    First of all there is translate method which defines translation of the element along the X- and/or Y-axis. At the starting point the handle is rotated for 10 degrees and also translated for 11px along vertical axis. At the 50% the element rotates for 20 degrees and translates for 5px along the horizontal axis. At the 70% the element has rotation angle 0.

    Wheels

    Next elements we want to animate are wheels and their shadows. We want that left and right wheel simultaneously appear from the center and move to the side including their shadow. So first we select left wheel, circle inside of a left wheel and left wheel’s shadow. Then we apply animation wheel-move-left and its properties:

    /* LEFT WHEEL */
    
    svg #wheels .wheel-l,
    svg #s_circle circle,
    svg #shadows .shadow-l { 
        animation: 1.2s wheel-move-left forwards;
        transform-origin: 500px 500px;
    }
    
    @keyframes wheel-move-left {
        0% {
            transform:  translate(100px,0px)  scaleX(0.00) scaleY(0.00) ;
        }
        40% {
            transform:  translate(100px,0px)  scaleX(1.00) scaleY(1.00) ;
        }
        70% {
            transform:  translate(-12px,0px)  scaleX(1.00) scaleY(1.00) ;
        }
        100% {
            transform:  translate(0px,0px)  scaleX(1.00) scaleY(1.00) ;
        }
    }
    

    Here we have scale method with which you can change the width and height of the element.

    Then we select elements of the right wheel, assign them properties and wheel-move-right animation. In general, the principle is the same:

    /* RIGHT WHEEL */
    
    svg #wheels .wheel-r,
    svg #shadows .shadow-r { 
        animation: 1.2s wheel-move-right forwards;
        transform-origin: 500px 500px;
    }
    
    @keyframes wheel-move-right {
        0% {
            transform:  translate(-100px,0px)  scaleX(0.00) scaleY(0.00) ;
        }
        40% {
            transform:  translate(-100px,0px)  scaleX(1.00) scaleY(1.00) ;
        }
        70% {
            transform:  translate(12px,0px)  scaleX(1.00) scaleY(1.00) ;
        }
        100% {
            transform:  translate(0px,0px)  scaleX(1.00) scaleY(1.00) ;
        }
    }
    

     

    Mudguards

    Mudguards are placed above each wheel and they already are animated because both mudguards are paths with class “path-line”. Let me remind you, path-line has applied the cycle animation. Because of that we only need to add a delay on them so they start animating at the time we want:

    svg #mudguard-L path,
    svg #mudguard-R path {
        animation-delay: 1.2s;
    }
    

     

    Basket

    Basket should be placed next to the steering wheel. It is made of paths with classes “basket-part1” and “basket-part2”. We want them to look like they scale from one point. To animate it we used two animations: scale-basket and show-basket.

    Path “basket-part1” has assigned scale-basket which animate it to appear and scale up.

    svg #basket .basket-part1 {
        animation: 0.3s scale-basket forwards;
        opacity: 0;
        animation-iteration-count: 1;
        transform-origin: 400px 350px;
        animation-delay: 1.8s;
    }
    

    Path “basket-part2” has also assigned scale-basket.

    svg #basket .basket-part2 {
        animation: 0.4s scale-basket forwards;
        opacity: 0;
        animation-iteration-count: 1;
        transform-origin: 400px 350px;
        animation-delay: 1.6s;
    }
    
    @keyframes scale-basket {
        0% {
            opacity: 0;
            transform: scale(0);
        }
        100% {
            opacity: 1;
            transform: scale(1);
        }
    }
    

     

    Seat

    The seat has pretty cool animation. It appears, the line is drawing, then it scales a little bit, translates and rotates.

    The animations we use are show-seat and rotate-seat:

    svg #seat line{
        opacity: 0;
        animation: 1.2s show-seat forwards, 1.2s rotate-seat forwards;
        animation-iteration-count: 1, 1;
        animation-delay: 1.6s, 1.6s;
        transform-origin: 620px 370px;
        stroke-dasharray: 100;  
        stroke-dashoffset: 100;
    }
    
    @keyframes show-seat {
        0%{
            opacity: 0;
            stroke-dashoffset: 100;
        }
        5%{
            opacity: 1;
        }
        100%{
            opacity: 1;
            stroke-dashoffset: 0;
        }
    }
    
    @keyframes rotate-seat {
        0%{
            transform: scaleX(0.1) scaleY(0.1) rotate(-180deg) translate(20px,-15px);
        }
        30%{
            transform: scaleX(0.9) scaleY(0.9) rotate(-90deg) translate(40px,-5px);
        }
        50%{
            transform: scaleX(1.0) scaleY(1.0) rotate(0deg) translate(0,0);
        }
        55%{
            transform: scaleX(1.0) scaleY(1.0) rotate(7deg) translate(0,0);
        }
    }
    
    

     

    Pedals

    The bicycle is almost finished! One more part is missing, pedals. You can create spinning animation if you like but we will keep it simple and just made them appear.

    Pedals are made of front and back pedal bars because one pedal is behind the pedal-box element so we need to make structure like that to work. For showing pedals we created an animation called show-pedal-bar.

    svg #front-pedal-bar,
    svg #back-pedal-bar{
        opacity: 0;
        animation: 0.1s show-pedal-bar forwards;
        animation-iteration-count: 1;
        transform-origin: 0 0;
        animation-delay: 1.4s;
    }
    
    @keyframes show-pedal-bar {
        0%{  opacity: 0;}
        100%{  opacity: 1;}
    }
    

     

    And that’s it!
    We hope this post got you intrigued and inspired you to explore the world of CSS animations. 🙂

    • Prateek Sharma

      Awesome, Perfect use of SVG and CSS

    • Pixel Industry

      Thank you, glad you like it and hope you’ll find it useful for your projects 🙂

    • Excelent.. bicycle is up and running on desktop.
      how to breakpoint this into mobile size and not only in desktop size?

    • Emil

      You can simply scale it with css by setting max-height to svg

HAVE A PROJECT FOR US?