I'm trying to create Apple's OS X circle loading animation.
What I have tried so far:
.animation-wrapper {
width: 200px;
height: 200px;
border: 1px solid black;
border-radius: 50%;
position: relative;
overflow: hidden;
filter: brightness(0.8);
-webkit-filter: brightness(0.8);
}
.pie-piece1 {
position: absolute;
width: 50%;
height: 50%;
bottom: 0;
left: 0;
background: linear-gradient(to right, rgba(255, 0, 0, 1) 0%, rgba(255, 255, 0, 1) 100%);
}
.pie-piece2 {
position: absolute;
width: 50%;
height: 50%;
bottom: 0;
right: 0;
background: linear-gradient(to right, rgba(255, 255, 0, 1) 0%, rgba(0, 255, 0, 1) 100%);
}
.pie-piece3 {
position: absolute;
width: 50%;
height: 50%;
top: 0;
left: 0;
background: linear-gradient(to right, rgba(255, 0, 0, 1) 0%, rgba(255, 0, 255, 1) 100%);
}
.pie-piece4 {
position: absolute;
width: 50%;
height: 50%;
top: 0;
right: 0;
background: linear-gradient(to right, rgba(255, 0, 255, 1) 0%, rgba(0, 0, 255, 1) 100%);
}
.rotating-spinners {
position: absolute;
}
.spike {
fill: rgba(22, 22, 22, 0.5);
}
<figure class="animation-wrapper">
<div class="pie-piece1"></div>
<div class="pie-piece2"></div>
<div class="pie-piece3"></div>
<div class="pie-piece4"></div>
<svg class="rotating-spinners" width="100%" height="100%" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="spin-part" class="spike" d="M 65,-40 C 65,-40 80,20 50,50 60,40 50,-40 50,-40Z" />
</defs>
<use x="0" y="0" xlink:href="#spin-part" />
<use x="0" y="0" xlink:href="#spin-part" transform="rotate(60, 50, 50)" />
<use x="0" y="0" xlink:href="#spin-part" transform="rotate(120, 50, 50)" />
<use x="0" y="0" xlink:href="#spin-part" transform="rotate(180, 50, 50)" />
<use x="0" y="0" xlink:href="#spin-part" transform="rotate(240, 50, 50)" />
<use x="0" y="0" xlink:href="#spin-part" transform="rotate(300, 50, 50)" />
</svg>
</figure>
The linear gradients don't seem to line up correctly since i couldn't find a way to make the gradients go in two directions.
Is there a way to create this using only CSS or SVG without mixing them like I have done?
Or are there other solutions I can use like canvas or some kind of image magic?
Here's my effort. The conical gradient is an embedded bitmap image extracted by calculating the maximum value of each pixel in the animated GIF posted by the OP. A semi-opaque black windmill pattern is superimposed on top of that and animated, and a blur filter gets rid of the JPEG artefacts.
(Edit: Added a reflective highlight to make it look a bit more 3D)
I had to do this with a mixture of SVG and CSS gradients which i know is against the request but is what i know. I used some of your original code, mostly the SVG parts for the propeller shapes.
The radial gradient is made using 12
li
elements.These 12 elements can then be blurred together to form the smooth gradient.
I then animated the spin parts to make the effect you require.
Canvas Approach
Since this is a loading animation where the dimensions are probably going to be fixed with almost nil user interactions, Canvas would also be a good option because it doesn't add any extra elements to the DOM. Canvas drawing commands are pretty similar to SVG and the browser support is also not worse.
One disadvantage would be that Canvas does not have its own blur filter (unlike SVG). But that can be overcome by either using CSS blur filter (has very low browser support) or the libraries mentioned in this Stack Overflow thread.
Background Gradient Wheel:
The background gradient wheel is created using an approach similar to the one detailed in my answer here. Basically we find multiple points in the circle and draw lines each of which have different colored stroke. By modifying the
hue
value for every line we can paint the gradient wheel.In the below screenshot the first picture shows how the background would have looked if we had drawn just 24 lines (with a
hue
change of 15 between every line)and the second one is our actual gradient wheel which has 360 lines in total with thehue
being incremented by 1 for each line.Fan:
The fan is created using the same approach as used in your SVG snippet. Path commands are used to draw each spoke. While
use
tag is used in SVG to repeat a shape, loops can be used in Canvas.The main difference here between SVG and Canvas is that Canvas cannot take in transform origin as a parameter for the
rotate
function and so context must be translated to the center point before applying rotations.Finally the canvas must be clipped into a circle because the default shape is a square (as height and width are same). The below screenshot shows the unclipped and clipped versions of the fan.
This fan is then placed on top of the background gradient wheel.
3D effect:
The 3D effect on top is provided by adding a small arc with an higher amount of transparency over the background and the fan.
Below is the screenshot of the full picture without any animation.
Animation:
Animation is added by using the
window.requestAnimationFrame
method which calls the function passed as an argument at regular intervals. This method would generally call the function about 60 times per second (according to MDN). By incrementing the value of thecounter
variable during every iteration and adding it to the angle of the fan's spokes the animation effect can be achieved.SVG Approach
The same approach as described above can be used with SVG also. The only downside would be the no. of extra elements that get added to the DOM both for the background and the fan.
Mixed Approach
Or, if you have no problems with the extra elements for the fan but just want to avoid the 360
line
elements that would get added, you could use a mixture of Canvas (for the background) and SVG for the fans like in the below snippet.Here's my SVG-only version. The background colour wheel isn't perfect, but I think I got fairly close.