I'm working on a css animation that uses 'cogs and chains', but am unable to create a 'smooth' border rotation sequence.
You can see in this fiddle How (currently) I'm using a pseudo element to generate a 'rotation' effect. This is done by 'switching' between a dashed white and dashed gold colored border, making it seem like the 'border is rotating'.
What I have
#one{
-webkit-animation: rotateClockwiseAnimation 5s linear infinite; /* Safari 4+ */
-moz-animation: rotateClockwiseAnimation 5s linear infinite; /* Fx 5+ */
-o-animation: rotateClockwiseAnimation 5s linear infinite; /* Opera 12+ */
animation: rotateClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */
}
#two{
-webkit-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Safari 4+ */
-moz-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Fx 5+ */
-o-animation: rotateAntiClockwiseAnimation 5s linear infinite; /* Opera 12+ */
animation: rotateAntiClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */
position:absolute;
top:30px;
left:42px;
width:80px;
}
@-webkit-keyframes rotateClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-moz-keyframes rotateClockwiseAnimation{
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-o-keyframes rotateClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes rotateClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-webkit-keyframes rotateAntiClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(-360deg); }
}
@-moz-keyframes rotateAntiClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(-360deg); }
}
@-o-keyframes rotateAntiClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(-360deg); }
}
@keyframes rotateAntiClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(-360deg); }
}
/******************************************************************************/
.chain{
height:70px;
width:80%;
border:5px dashed gold;
border-radius:30px;
position:absolute;
top:30px;
left:40px;
-webkit-animation: switchGoldBlackBorder 0.8s infinite; /* Safari 4+ */
-moz-animation: switchGoldBlackBorder 0.8s infinite; /* Fx 5+ */
-o-animation: switchGoldBlackBorder 0.8s infinite; /* Opera 12+ */
animation: switchGoldBlackBorder 0.8s infinite; /* IE 10+, Fx 29+ */
}
@-webkit-keyframes switchBlackGoldBorder {
0% { border: 5px dashed transparent; }
49% { border: 5px dashed transparent; }
50% { border: 5px dashed gold; }
100% { border: 5px dashed gold; }
}
@-moz-keyframes switchBlackGoldBorder{
0% { border: 5px dashed transparent; }
49% { border: 5px dashed transparent; }
50% { border: 5px dashed gold; }
100% { border: 5px dashed gold; }
}
@-o-keyframes switchBlackGoldBorder {
0% { border: 5px dashed transparent; }
49% { border: 5px dashed transparent; }
50% { border: 5px dashed gold; }
100% { border: 5px dashed gold; }
}
@keyframes switchBlackGoldBorder {
0% { border: 5px dashed transparent; }
49% { border: 5px dashed transparent; }
50% { border: 5px dashed gold; }
100% { border: 5px dashed gold; }
}
.chain:after{
content:"";
position:absolute;
height:70px;
border-radius:30px;
width:100%;
top:-5px;
left:-5px;
border:5px solid gold;
z-index:-1;
-webkit-animation: switchBlackGoldBorder 0.8s infinite; /* Safari 4+ */
-moz-animation: switchBlackGoldBorder 0.8s infinite; /* Fx 5+ */
-o-animation: switchBlackGoldBorder 0.8s infinite; /* Opera 12+ */
animation: switchBlackGoldBorder 0.8s infinite; /* IE 10+, Fx 29+ */
}
@-webkit-keyframes switchGoldBlackBorder {
0% { border: 5px solid gold; }
49% { border: 5px solid gold; }
50% { border: 5px solid white; }
100% { border: 5px solid white; }
}
@-moz-keyframes switchGoldBlackBorder{
0% { border: 5px solid gold; }
49% { border: 5px solid gold; }
50% { border: 5px solid white; }
100% { border: 5px solid white; }
}
@-o-keyframes switchGoldBlackBorder {
0% { border: 5px solid gold; }
49% { border: 5px solid gold; }
50% { border: 5px solid white; }
100% { border: 5px solid white; }
}
@keyframes switchGoldBlackBorder {
0% { border: 5px solid gold; }
49% { border: 5px solid gold; }
50% { border: 5px solid white; }
100% { border: 5px solid white; }
}
<svg id="one" style="width:50px" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
<defs>
<circle id="c" cx="50" cy="50" r="30" stroke="#808080" fill="none" stroke-width="25"/>
<path id="d" stroke="#808080" stroke-width="16" d="M50 0, V15 M50 100, V85 M0 50, H15 M100 50, H85"/>
</defs>
<use xlink:href="#c"/>
<use xlink:href="#d"/>
<use xlink:href="#d" transform="rotate(45, 50, 50)"/>
</svg>
<svg id="two" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
<use xlink:href="#one"/>
</svg>
<div class="chain"></div>
So, the lower section of the snippet, you can see how I've generated the 'rotating chain effect' through using keyframes.
What I would like
My overall wish would be to generate something like:
Think of a cross section of a conveyor belt, and how the 'gears at the end drive the belt'. I'm trying to reproduce that. (i.e. the dashed border's gold bits should be within the troughs of the gear, and 'be pulled' by it)
#one{
-webkit-animation: rotateClockwiseAnimation 5s linear infinite; /* Safari 4+ */
-moz-animation: rotateClockwiseAnimation 5s linear infinite; /* Fx 5+ */
-o-animation: rotateClockwiseAnimation 5s linear infinite; /* Opera 12+ */
animation: rotateClockwiseAnimation 5s linear infinite; /* IE 10+, Fx 29+ */
border:5px dashed gold;
border-radius:50%;
}
@-webkit-keyframes rotateClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-moz-keyframes rotateClockwiseAnimation{
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@-o-keyframes rotateClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes rotateClockwiseAnimation {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
<svg id="one" style="width:50px" xmlns="http://www.w3.org/2000/svg" viewbox="0 0 100 100">
<defs>
<circle id="c" cx="50" cy="50" r="30" stroke="#808080" fill="none" stroke-width="25"/>
<path id="d" stroke="#808080" stroke-width="16" d="M50 0, V15 M50 100, V85 M0 50, H15 M100 50, H85"/>
</defs>
<use xlink:href="#c"/>
<use xlink:href="#d"/>
<use xlink:href="#d" transform="rotate(45, 50, 50)"/>
</svg>
but with the gold dashes to fit within the gear's troughs, as well as being 80% width of the screen (if that makes sense).
In the end, I would like to generate something like this image portrays:
See how i want the chain to 'rotate'?
My Current Issues
- Since the animation is 'hacked' via the use of a pseudo element, I've found it quite hard to actually sync the rotation of this 'chain'.
- I'm still learning keyframe animation, so I'm sure that isn't the best method for this
- Again, svg is a new concept for me, so bear with my reluctance to use it (hence why I'm using css for the 'chain')
- In the end, I want to 'make it look like' the gear is turning the chain, but right now they look like completely (and badly done) separate element animations
Here is a different method on how to achieve cog animation using CSS. This method has been tested in IE11, IE10, Firefox, Chrome, Opera and Safari.
box-shadow
to produce the inner circle. The teeth are produced by child elements (normal + pseudo) which are rotated around the axis.Bonus: Here is the whole animation with an on/off switch :) Click (pull) the chain handle to switch the animation on or off.
Original Answer: (Doesn't work on Firefox due to dashed border bug and dashes are more closer in IE making it look ugly).
You could achieve the border rotation animation by using a combination of the following:
border-radius: 50%
) with dashed border on either side to form the curved part of the border.linear-gradient
. The background of this element (other than the gradient on top and bottom) is a solid color which is sort of a drawback. This solid color is used to hide half of the circular element on either side.background-position
of the gradient backgrounds.box-shadow
. The cog is rotated in such a way that the chain's border is always in between the cog's border.Cog and chain animation :
I totaly refactored the code (CSS and HTML), it is now :
DEMO
The approach is the same, animating the rotation angle for the cogs and
dash-offset
for the chain path. I tweaked the timing between both animations to make it look as if the cogs are pulling the chain.Browser support :
As IE doesn't support the svg animate element I also made this version of the animation with the snap.svg library that also supports IE9 and over (tested in IE9 with crossbrowsertesting).
DEMO with IE support
Original answer :
You could use an other svg dashed path and animate the dash-offset property with a keyframe animation.
This can and should be simplified/tweaked for a "real world" use :
<svg>
tag (this would make it simpler and both cogs + chain could resize together)How about this approach? I'm using SVG for both the gears and the conveyor. The gears rotate as per your example, but I am using
stroke-dasharray
and animatingstroke-dash-offset
to make the conveyor belt move.It took a bit of fiddling to get the conveyor length and dash timing right, which you would need to tweak again if you change the gear size or conveyor length.
Using Canvas
The shapes (cog and chain) and the marching ants animation effect (dashed border) can also be achieved by using a Canvas drawing. The browser support for Canvas is quite good.
While Canvas has the disadvantage of being raster based (as opposed to SVG, which is shape based), it is not a big problem as long as the canvas is not scaled too much. Canvas is expected to be better when handling a large number of objects and real-time animations. Here is an interesting article from the MSDN on when to use Canvas or SVG.
Construction of Shapes
The following are the key parts/shapes involved in this animation:
Chain: The chain is created by drawing two horizontal lines (using the
lineTo
commands) that are connected at either end by semicircles (drawn using thearc
command). The dashed border effect is achieved by usingsetLineDash
method for the stroke.setLineDash
method takes two parameters where the first represents the length of the dash and the second represents the gap between dashes.Below snippet shows the minimal code required to create the chain:
Cogs: All the three cogs (left, right and top) are achieved using the same commands and the only difference is their positioning on screen (and radius for the top one).
The spokes or teeth of the cogs are created by using the
arc
command (as done for the semicircles of the chain), and use the same radius. TheirlineDashoffset
is adjusted such that they occupy the exact space left between the dashes of the chain.The body of the cogs are made up of two circles with the outer one having a bigger radius than the inner. The
evenodd
fill parameter is used to set the background color (tan
in this example) only on the outer circle and leaving the inner one transparent.Below snippet shows the minimal code required to create the cogs:
Animation
The animation is achieved by shifting the
lineDashOffset
of the strokes in each frame of the animation. The animation itself is triggered using thewindow.requestAnimationFrame
method which calls the paint function (passed as an argument) at regular intervals. The rate of callback is generally 60 times per second (quoting MDN). Shifting the offset during every repaint of the canvas gives the appearance of it being animated.The animation can be stopped at any point of time by calling the
cancelAnimationFrame
method. This can be done either based on some user interaction (like click, hover etc), or based on a time-out.Complete Picture
Putting all the parts together, the below snippet provides the complete picture of the chain and cogs along with the animation:
User Interaction
As mentioned earlier, user interaction can also be added using event listeners (
addEventListener
). If the action needs to be triggered by user action on a specific shape or section of the canvas then thepointInPath
method can be used to verify if the point is within the required section of the canvas.Here is the link to a CodePen demo which has these user interactions (and some other extra stuff) also added.
Note: Explanation on how to add user interactions etc is beyond the scope of this particular answer. However, if you need any help you can find me in this chat room.
You could try and edit cog so it fits better instead of tweaking div border to fall into place on cog. Its easier to manipulate graphics than css.
And from there maybe to split chain animation in three or four parts to make it more robust.
Then you could tweak speeds of cog and chain to match, hide a half of chain, add onto it div only with top and bottom border and do the same but opposite on other end. (using cliping, position and z-index).
Something like this:
In theory at least, that would be my approach (not to mention that I would use JS instead of this workflow).
Note: I have remade the whole animation in box-shadow, because using dashed borders doesn't have consistent output on all the browsers.
Working
.. and Works cross-browser.
FF 5+, GC 4+, IE9+, Safari 4+, Opera 12.1+
You can try this using box-shadow :
To make the gears teeth, use box-shadow with negative spread radius. The size of my gear was
50px
for example, so to get roundbox-shadow
withd=8px
, I used-46px
as spread radius.I positioned the teeth using coordinate geo, and only used 8 teeth to simplify.
Now for the straight conveyor, we need to know the distance between the teeth. We get that by the following :
2*pi*(gear radius) / no. of teeth
=(pi * r) / 4
Mine =
(55 * 3.1415) / 4 = 43
(approx.)I took the radius as 55 because the teeth have 4px radius and are lying 1px away from gear circumference.
For aligning the top and bottom straight conveyors, they need to be translated by any multiple of their distance. For my gear, I translate them by 43px.
The Scaffolding
FIDDLE
Final version
... with gears. The chain currently is
dotteddashed!FIDDLE
Final version (Rounded cog-teeth)
FIDDLE - ROUNDED TEETH
NOTE : To increase the speed of animation, you simply have to decrease the animation duration of each element proportionally.
Fiddle (fast)