An Orrery in pure CSS3 (ok, Sass generated). Still WIP. Having great fun with it!
A Pen by Tady Walsh on CodePen.
An Orrery in pure CSS3 (ok, Sass generated). Still WIP. Having great fun with it!
A Pen by Tady Walsh on CodePen.
<div class="system"> | |
<div class="sun"></div> | |
<div class="mer-path"></div> | |
<div class="mer"></div> | |
<div class="ven-path"></div> | |
<div class="ven"></div> | |
<div class="ear-path"></div> | |
<div class="ear"><div class="lune"></div></div> | |
<div class="mar-path"></div> | |
<div class="mar"> | |
<div class="pho"></div> | |
<div class="dem"></div> | |
</div> | |
<div class="jup-path"></div> | |
<div class="jup"> | |
<div class="spot"></div> | |
<div class="jove io"></div> | |
<div class="jove eur"></div> | |
<div class="jove gan"></div> | |
<div class="jove cal"></div> | |
</div> | |
<div class="sat-path"></div> | |
<div class="sat"> | |
<div class="f-ring"></div> | |
<div class="a-ring"></div> | |
<div class="b-ring"></div> | |
<div class="c-ring"></div> | |
</div> | |
<div class="ura-path"></div> | |
<div class="ura"> | |
<div class="e-ring"></div> | |
</div> | |
<div class="nep-path"></div> | |
<div class="nep"> | |
<div class="spot"></div> | |
</div> | |
<div class="plu-path"></div> | |
<div class="plu"></div> | |
</div> |
/** | |
* I was out the other evening looking at Venus with the setting sun and thought, I wonder where all the planets | |
* are in relation to each other right now. I knew what an Orrery was, but I'd never built one. So, given my mate | |
* Donovan's (@donovanh: http://cssanimation.rocks/) penchant for CSS animation, I thought I'd give it a go | |
* building one in pure CSS. | |
* | |
* Many thanks to @aidandore and @iandevlin too for suggestions and improvements | |
* | |
* Chin up Pluto. You'll always be a planet to me... | |
* | |
* Tady: http://tady.me | |
* @tadywankenobi | |
*/ | |
/** | |
* Move in a circle without wrapper elements | |
* Idea by Aryeh Gregor, simplified by Lea Verou, borrowed by me! | |
*/ | |
@mixin vp-anim($radius,$pname, $deg: 360deg) { | |
@-webkit-keyframes rot-#{$pname} { | |
from { | |
-webkit-transform: rotate(0deg) | |
translatey(-$radius) | |
rotate(0deg); | |
} | |
to { | |
-webkit-transform: rotate(360deg) | |
translatey(-$radius) | |
rotate(-$deg); | |
} | |
} | |
@-moz-keyframes rot-#{$pname} { | |
from { | |
-moz-transform: rotate(0deg) | |
translatey(-$radius) | |
rotate(0deg); | |
} | |
to { | |
-moz-transform: rotate(360deg) | |
translatey(-$radius) | |
rotate(-$deg); | |
} | |
} | |
@-keyframes rot-#{$pname} { | |
from { | |
transform: rotate(0deg) | |
translatey(-$radius) | |
rotate(0deg); | |
} | |
to { | |
transform: rotate(360deg) | |
translatey(-$radius) | |
rotate(-$deg); | |
} | |
} | |
} | |
$baseUnit: 0.1; // Speed of Orrery. At 1, 1 sec = 1 day | |
$sunRad: 72px; // Radius of the sun, added to orbit radii | |
$rFactor: 2; // Adding a radius factor so orbit radii are easier to observe | |
$middleOffset: 800px; | |
$scale: 0.75; // Play around with changing this to change the visible motion default: 0.75 | |
$middle: $middleOffset*(1/$scale); | |
$planets: ( | |
('mer',8.8s*$baseUnit,(6px*$rFactor)+$sunRad,1.75px,#888), | |
('ven',22.5s*$baseUnit,(9px*$rFactor)+$sunRad,2.75px,#f5f9be), | |
('ear',36.5s*$baseUnit,(15px*$rFactor)+$sunRad,3.5px,#4b94f9), | |
('mar',68.7s*$baseUnit,(23px*$rFactor)+$sunRad,3px,#dd411a), | |
('jup',433.2s*$baseUnit,(78px*$rFactor)+$sunRad,35px,#eaad3b), | |
('sat',1075.9s*$baseUnit,(145px*$rFactor)+$sunRad,29px,#d6cd93), | |
('ura',3068.7s*$baseUnit,(288px*$rFactor)+$sunRad,13px,#bfeef2), | |
('nep',6019s*$baseUnit,(450px*$rFactor)+$sunRad,12px,#363ed7), | |
('plu',9046.5s*$baseUnit,(587px*$rFactor)+$sunRad,1.5px,#963) | |
); | |
body { | |
background-color: #012; | |
background-image: url('https://cssanimation.rocks/starwars/images/bg.jpg'); | |
background-size: 33%; | |
background-repeat: repeat; | |
min-height: 2700px * $scale; | |
} | |
.system { | |
position:relative; | |
top: 0; left: 0; | |
width:100%; | |
height:100%; | |
-webkit-transform: scale($scale); | |
-moz-transform: scale($scale); | |
transform: scale($scale); | |
} | |
.sun { | |
width: $sunRad*2; | |
height:$sunRad*2; | |
border-radius: $sunRad; | |
position:absolute; | |
top: $middle; | |
left:50%; | |
margin: -$sunRad; | |
//background-color: yellow; | |
background-image: url('http://sdo.gsfc.nasa.gov/assets/img/latest/latest_256_HMIIF.jpg'); | |
background-size:$sunRad*2; | |
background-repeat:no-repeat; | |
} | |
@each $planet in $planets { | |
$name: nth($planet, 1); | |
$orb: nth($planet,2); | |
$rad: nth($planet,3); | |
$prad: nth($planet,4); | |
$col: nth($planet,5); | |
@include vp-anim($rad,$name); | |
.#{$name} { | |
width: $prad * 2; | |
height: $prad*2; | |
border-radius:50%; | |
background-color:$col; | |
position: absolute; | |
top: $middle; | |
left: 50%; | |
margin: -$prad; | |
-webkit-animation: rot-#{$name} $orb infinite linear; | |
-moz-animation: rot-#{$name} $orb infinite linear; | |
animation: rot-#{$name} $orb infinite linear; | |
z-index:200; | |
} | |
.#{$name}-path { | |
$orbitPath: $rad*2; | |
width: $orbitPath; | |
height: $orbitPath; | |
border-radius:50%; | |
z-index:100; | |
position:absolute; | |
top: $middle; | |
left:50%; | |
margin:-($orbitPath / 2); | |
border: solid 1px #444; | |
} | |
} | |
@include vp-anim(7px,lune); | |
.lune { | |
width:2px; | |
height:2px; | |
background-color: #fff; | |
position:absolute; | |
$lunOrb: 2.7s * $baseUnit; | |
top: 50%; | |
left:50%; | |
margin:-1.5px; | |
-webkit-animation: rot-lune $lunOrb infinite linear; | |
-moz-animation: rot-lune $lunOrb infinite linear; | |
animation: rot-lune $lunOrb infinite linear; | |
} | |
.mar { | |
background-image: repeating-linear-gradient( | |
to bottom, | |
#fff, | |
#fff 1px, | |
transparent 1px, | |
transparent 5px | |
); | |
} | |
@include vp-anim(7px,pho); | |
@include vp-anim(9px,dem); | |
.pho, .dem { | |
width:1px; | |
height:1px; | |
background-color: #fff; | |
position:absolute; | |
top: 50%; | |
left:50%; | |
} | |
.pho { | |
$phoOrb: 1.5s * $baseUnit; // Should be 0.33s but spins like it's having a fit. | |
margin:-1px; | |
-webkit-animation: rot-pho $phoOrb infinite linear; | |
-moz-animation: rot-pho $phoOrb infinite linear; | |
animation: rot-pho $phoOrb infinite linear; | |
} | |
.dem { | |
$demOrb: 2s * $baseUnit; // Should be 1.25s, spins too fast | |
margin:-1px; | |
-webkit-animation: rot-dem $demOrb infinite linear; | |
-moz-animation: rot-dem $demOrb infinite linear; | |
animation: rot-dem $demOrb infinite linear; | |
} | |
$jupR: 35px; | |
.jove { | |
width:2px; | |
height:2px; | |
background-color: #fff; | |
position:absolute; | |
top: $jupR; | |
left:50%; | |
} | |
@include vp-anim($jupR + 4px,io); | |
@include vp-anim($jupR + 6px,eur); | |
@include vp-anim($jupR + 10px,gan); | |
@include vp-anim($jupR + 18px,cal); | |
.io { | |
$jioOrb: 2s*$baseUnit; | |
-webkit-animation: rot-io $jioOrb infinite linear; | |
-moz-animation: rot-io $jioOrb infinite linear; | |
animation: rot-io $jioOrb infinite linear; | |
} | |
.eur { | |
$jeurOrb: 3.5s*$baseUnit; | |
-webkit-animation: rot-eur $jeurOrb infinite linear; | |
-moz-animation: rot-eur $jeurOrb infinite linear; | |
animation: rot-eur $jeurOrb infinite linear; | |
} | |
.gan { | |
$jganOrb: 7s*$baseUnit; | |
-webkit-animation: rot-gan $jganOrb infinite linear; | |
-moz-animation: rot-gan $jganOrb infinite linear; | |
animation: rot-gan $jganOrb infinite linear; | |
} | |
.cal { | |
$jcalOrb: 16.5s*$baseUnit; | |
-webkit-animation: rot-cal $jcalOrb infinite linear; | |
-moz-animation: rot-cal $jcalOrb infinite linear; | |
animation: rot-cal $jcalOrb infinite linear; | |
} | |
.jup { | |
background-image: repeating-linear-gradient( | |
6deg, | |
#797663 22px, | |
#e1dcde 16px, | |
#c3a992 30px, | |
#e9ece2 30px | |
); | |
} | |
.spot { | |
position:absolute; | |
width:16px;height:12px; | |
border-radius: 8px / 6px; | |
top: $jupR+10; | |
left:50%; | |
background-color:#bc833b; | |
box-shadow: 0px 0px 5px #e1dcde; | |
border:solid 1px #e1dcde; | |
box-sizing:border-box; | |
-moz-box-sizing:border-box; | |
-webkit-box-sizing:border-box; | |
z-index:300; | |
.nep & { | |
background-color:darken(#343ec5, 10%); | |
border: 0; | |
box-shadow: none; | |
top: 50%; | |
left:45%; | |
width: 10px; | |
height: 6px; | |
margin: -2px; | |
border-radius: 5px / 3px; | |
border-left:solid 1px #7ed6fe; | |
} | |
} | |
$satD: 29px * 2; | |
div[class$="-ring"] { | |
border-radius: 50%; | |
position:absolute; | |
top: 50%; | |
left:50%; | |
opacity: 0.7; | |
$tilt: 45deg; | |
-webkit-transform: rotatex($tilt); | |
-moz-transform: rotatex($tilt); | |
transform: rotatex($tilt); | |
} | |
.a-ring { | |
$ringD: $satD + 61; | |
$ringW: 5px; | |
border:solid $ringW #96866f; | |
width: $ringD; | |
height: $ringD; | |
margin: -($ringD / 2) - $ringW; | |
} | |
.b-ring { | |
$ringD: $satD + 46; | |
$ringW: 10px; | |
border:solid $ringW #554c3c; | |
width: $ringD; | |
height: $ringD; | |
margin: -($ringD / 2) - $ringW; | |
} | |
.c-ring { | |
$ringD: $satD + 37; | |
$ringW: 9px; | |
border:solid $ringW #574f4a; | |
width: $ringD; | |
height: $ringD; | |
margin: -($ringD / 2) - $ringW; | |
} | |
.f-ring { | |
$ringD: $satD + 75; | |
$ringW: 2px; | |
border:solid $ringW #908e8d; | |
width: $ringD; | |
height: $ringD; | |
margin: -($ringD / 2) - $ringW; | |
} | |
$uraD: 13px * 2; | |
.e-ring { | |
$ringD: $uraD + 50; | |
$ringW: 7px; | |
border:solid $ringW #908e8d; | |
width: $ringD; | |
height: $ringD; | |
margin: -($ringD / 2) - $ringW; | |
$tilt: 0deg; | |
$axis: 89deg; | |
-webkit-transform: rotatex($tilt) rotatey($axis) !important; | |
-moz-transform: rotatex($tilt) rotatey($axis) !important; | |
transform: rotatex($tilt) rotatey($axis) !important; | |
} | |
.plu, .plu-path { | |
top: $middle + 287.4px; | |
} |