it was a real pleasure to build this solar system to make an appropriate understandable size and distance scale
Last active
January 8, 2024 15:25
-
-
Save mrjuice01/d13e053f1915d5e5b80185065038dfef to your computer and use it in GitHub Desktop.
Solar System Scale Model
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <template> | |
| <div id="app"> | |
| <!-- MAIN CONTENT --> | |
| <div class="sun" :style="sunStyle()"></div> | |
| <div | |
| class="planet_container" | |
| :class="`planet_container-${mode}`" | |
| v-for="(p, i) in planetsFiltered" | |
| :style="planetContainerStyle(i)" | |
| @click="onClickPlanetContainer(i)" | |
| > | |
| <div | |
| class="planet" | |
| :class="`planet-${mode}`" | |
| :style="planetStyle(i)" | |
| @click="onClickPlanet(i)" | |
| > | |
| <div | |
| class="satellite_container" | |
| v-for="(s, j) in p.satellites" | |
| :style="satelliteContainerStyle(i, j)" | |
| > | |
| <div class="satellite" :style="satelliteStyle(i, j)"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- INFORMATION --> | |
| <div class="information-container"> | |
| <p class="name">{{ selectedPlanet.name }}</p> | |
| <p> | |
| Diameter : {{ parseNumeriqueSpace(selectedPlanet.diameter * 1000) }} km | |
| </p> | |
| <p> | |
| Revolution : {{ parseNumeriqueSpace(selectedPlanet.rotationTime) }} days | |
| </p> | |
| <p>Sun Distance : {{ parseNumeriqueSpace(selectedPlanet.ua * 150) }} Mkm</p> | |
| </div> | |
| <!-- ACTION --> | |
| <div class="action-container"> | |
| <div> | |
| <span v-for="m in modes"> | |
| <button :disabled="m === mode" @click="switchMode(m)">{{ m }}</button> | |
| </span> | |
| </div> | |
| <div v-if="mode !== 'WIDTH' || speedReal"> | |
| <input type="range" step="1" min="1" max="1000" v-model="speedRatio" /> | |
| <input type="number" step="1" min="1" max="1000" v-model="speedRatio" /> | |
| <span>days / second</span> | |
| </div> | |
| <div> | |
| <button v-if="mode === 'WIDTH'" @click="speedReal = !speedReal"> | |
| {{ speedReal ? "Speed Real" : "Speed Fake" }} | |
| </button> | |
| </div> | |
| <div v-if="mode === 'DISTANCE'"> | |
| <button :disabled="planetsFilteredLength <= 4" @click="zoomIn"> | |
| Zoom IN | |
| </button> | |
| <button | |
| :disabled="planetsFilteredLength >= planets.length" | |
| @click="zoomOut" | |
| > | |
| Zoom OUT | |
| </button> | |
| </div> | |
| </div> | |
| <!-- SIGNATURE --> | |
| <div class="author-container"> | |
| <div class="picture"></div> | |
| <div class="title">@SylvainGarnot</div> | |
| </div> | |
| </div> | |
| </template> | |
| <script> | |
| export default { | |
| // ################################################## | |
| // DATA ############################################# | |
| // ################################################## | |
| data() { | |
| return { | |
| mode: null, | |
| modes: ["WIDTH", "SPEED", "DISTANCE"], | |
| speedRatio: 50, | |
| speedReal: false, | |
| sun: { | |
| diameter: "1392", | |
| colors: ["gold", "orange"] | |
| }, | |
| selectedPlanetId: 3, | |
| planetsFilteredLength: 0, | |
| planets: [ | |
| { | |
| name: "mercure", | |
| diameter: "4.7428794", | |
| rotationTime: "87.969", | |
| ua: "0.47", | |
| colors: ["gold", "darkBlue"] | |
| }, | |
| { | |
| name: "venus", | |
| diameter: "12.104", | |
| rotationTime: "224.701", | |
| ua: "0.7", | |
| colors: ["forestgreen", "springGreen"] | |
| }, | |
| { | |
| name: "earth", | |
| diameter: "12.742", | |
| rotationTime: "365.256", | |
| ua: "1", | |
| colors: ["white", "blue"], | |
| backgroundUrl: | |
| "https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/6d358ffa-5177-4b84-963f-9a1957b5a8d0/d8vp0fw-994cd783-be4b-46d3-86a8-2a30d4791003.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzZkMzU4ZmZhLTUxNzctNGI4NC05NjNmLTlhMTk1N2I1YThkMFwvZDh2cDBmdy05OTRjZDc4My1iZTRiLTQ2ZDMtODZhOC0yYTMwZDQ3OTEwMDMucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.0DhwRtmDbKrm-cI67M7nD6zVeH6xKg1nbgFZlUSDs5s", | |
| satellites: [ | |
| { | |
| name: "moon", | |
| diameter: "3.4742", | |
| rotationTime: "29.53", | |
| colors: ["white", "grey"] | |
| } | |
| ] | |
| }, | |
| { | |
| name: "mars", | |
| diameter: "6.779", | |
| rotationTime: "686.980", | |
| ua: "1.666", | |
| colors: ["darkred", "red"] | |
| }, | |
| { | |
| name: "jupiter", | |
| diameter: "139.82", | |
| rotationTime: "4332.6", | |
| ua: "5.2", | |
| colors: ["#876f51", "#9c661f"], | |
| satellites: [ | |
| { | |
| name: "europe", | |
| diameter: "3.1216", | |
| rotationTime: "3.551", | |
| colors: ["brown", "pink"] | |
| }, | |
| { | |
| name: "ganymede", | |
| diameter: "5.2644", | |
| rotationTime: "7.15", | |
| colors: ["blue", "cyan"] | |
| }, | |
| { | |
| name: "callisto", | |
| diameter: "4.8206", | |
| rotationTime: "16.689", | |
| colors: ["orange", "grey"] | |
| } | |
| ] | |
| }, | |
| { | |
| name: "Saturne", | |
| diameter: "116.46", | |
| rotationTime: "10759.2", | |
| ua: "9.5", | |
| colors: ["#b09f74", "#b8902a"], | |
| satellites: [ | |
| { | |
| name: "Thétys", | |
| diameter: "1.066", | |
| rotationTime: "1.89", | |
| colors: ["white", "grey"] | |
| }, | |
| { | |
| name: "Dioné", | |
| diameter: "1.1234", | |
| rotationTime: "2.74", | |
| colors: ["white", "white"] | |
| }, | |
| { | |
| name: "Rhéa", | |
| diameter: "1.5286", | |
| rotationTime: "4.52", | |
| colors: ["white", "grey"] | |
| }, | |
| { | |
| name: "Titan", | |
| diameter: "5.1495", | |
| rotationTime: "15.95", | |
| colors: ["gold", "white"] | |
| }, | |
| { | |
| name: "Japet", | |
| diameter: "1.4712", | |
| rotationTime: "79.33", | |
| colors: ["springGreen", "cyan"] | |
| } | |
| ] | |
| }, | |
| { | |
| name: "uranus", | |
| diameter: "50.724", | |
| rotationTime: "30688.4", | |
| ua: "19.2", | |
| colors: ["#a3bebf", "#387a7d"], | |
| satellites: [ | |
| { | |
| name: "titania", | |
| diameter: "0.7884", | |
| rotationTime: "8.7", | |
| colors: ["#387a7d", "#a3bebf"] | |
| } | |
| ] | |
| }, | |
| { | |
| name: "neptune", | |
| diameter: "49.244", | |
| rotationTime: "60181.3", | |
| ua: "30.1", | |
| colors: ["#7f8dc7", "#01146b"], | |
| satellites: [ | |
| { | |
| name: "triton", | |
| diameter: "2.7068", | |
| rotationTime: "5.877", | |
| colors: ["forestgreen", "#7f8dc7"] | |
| }, | |
| { | |
| name: "néréide", | |
| diameter: "0.340", | |
| rotationTime: "360.14", | |
| colors: ["#01146b", "#7f8dc7"] | |
| } | |
| ] | |
| } | |
| ] | |
| }; | |
| }, | |
| // ################################################## | |
| // COMPUTED ######################################### | |
| // ################################################## | |
| computed: { | |
| selectedPlanet() { | |
| return this.planets[this.selectedPlanetId]; | |
| }, | |
| totalPlanetsWidth() { | |
| let result = 0; | |
| for (let i = 0; i < this.planetsFiltered.length; i++) { | |
| result += parseFloat(this.planetsFiltered[i].diameter); | |
| } | |
| return result; | |
| }, | |
| containerMargin_modeWidth() { | |
| return parseFloat(this.totalPlanetsWidth * 0.0075); | |
| }, | |
| planetMargin_modeWidth() { | |
| return parseFloat( | |
| (this.totalPlanetsWidth / this.planetsFiltered.length) * 0.045 | |
| ); | |
| }, | |
| sunWidth(index) { | |
| return parseFloat( | |
| (this.sun.diameter / this.totalPlanetsWidth) * | |
| (100 - | |
| this.containerMargin_modeWidth * 2 - | |
| this.planetMargin_modeWidth * 2 * this.planetsFiltered.length) | |
| ); | |
| }, | |
| planetsFiltered() { | |
| const result = []; | |
| for (let i = 0; i < this.planetsFilteredLength; i++) { | |
| result.push(this.planets[i]); | |
| } | |
| return result; | |
| } | |
| }, | |
| // ################################################## | |
| // WATCH ############################################ | |
| // ################################################## | |
| watch: {}, | |
| // ################################################## | |
| // MOUNTED ########################################## | |
| // ################################################## | |
| mounted() { | |
| this.mode = this.modes[0]; | |
| this.planetsFilteredLength = this.planets.length; | |
| }, | |
| // ################################################## | |
| // METHODS ########################################## | |
| // ################################################## | |
| methods: { | |
| // ACTION ################################################## | |
| switchMode(m) { | |
| this.mode = m; | |
| this.planetsFilteredLength = this.planets.length; | |
| }, | |
| zoomIn() { | |
| if (this.planetsFilteredLength > 4) { | |
| this.planetsFilteredLength--; | |
| } | |
| }, | |
| zoomOut() { | |
| if (this.planetsFilteredLength < this.planets.length) { | |
| this.planetsFilteredLength++; | |
| } | |
| }, | |
| onClickPlanet(i) { | |
| console.log("onClickPlanet", i); | |
| this.selectedPlanetId = i; | |
| }, | |
| onClickPlanetContainer(i) { | |
| if (this.mode !== "WIDTH") { | |
| console.log("onClickPlanetContainer", i); | |
| this.selectedPlanetId = i; | |
| } | |
| }, | |
| parseNumeriqueSpace(value) { | |
| let result = ""; | |
| value = parseInt(value); | |
| value = value.toString(); | |
| for (let i = value.length - 1; i >= 0; i--) { | |
| result = value.charAt(i).concat(result); | |
| if ((value.length - i) % 3 === 0) { | |
| result = " ".concat(result); | |
| } | |
| } | |
| return result; | |
| }, | |
| // MODE WIDTH ################################################## | |
| calcPlanetWidth_modeWidth(diameter) { | |
| return parseFloat( | |
| (parseFloat(diameter) / this.totalPlanetsWidth) * | |
| (100 - | |
| this.containerMargin_modeWidth * 2 - | |
| this.planetMargin_modeWidth * 2 * this.planetsFiltered.length) | |
| ); | |
| }, | |
| planetLeft_modeWidth(index) { | |
| let result = this.containerMargin_modeWidth + this.planetMargin_modeWidth; | |
| for (let i = 0; i < index; i++) { | |
| result += | |
| this.calcPlanetWidth_modeWidth(this.planetsFiltered[i].diameter) + | |
| this.planetMargin_modeWidth * 2; | |
| } | |
| return result; | |
| }, | |
| // SUN ################################################## | |
| sunStyle() { | |
| let width; | |
| let boxShadowWidth; | |
| let unit; | |
| let right; | |
| const color = this.sun.colors[1]; | |
| if (this.mode === "WIDTH") { | |
| width = this.sunWidth; | |
| boxShadowWidth = 10; | |
| unit = "vw"; | |
| right = "65vw"; | |
| boxShadowWidth = 10; | |
| } else if (this.mode === "SPEED") { | |
| width = 2.5; | |
| boxShadowWidth = 0.9; | |
| unit = "vw"; | |
| right = `calc(50% - ${width / 2}${unit});`; | |
| boxShadowWidth = width / 5; | |
| } else if (this.mode === "DISTANCE") { | |
| width = 5; | |
| boxShadowWidth = 5; | |
| unit = "px"; | |
| right = `calc(50% - ${width / 2}${unit});`; | |
| boxShadowWidth = width / 5; | |
| } | |
| return ` | |
| right: ${right}; | |
| top: calc(50% - ${width / 2}${unit}); | |
| height: ${width}${unit}; | |
| width: ${width}${unit}; | |
| box-shadow: 0 0 ${boxShadowWidth}${unit} ${color}; | |
| `; | |
| }, | |
| // PLANET CONTAINER ################################################## | |
| planetContainerStyle(i) { | |
| let left; | |
| let top; | |
| let width; | |
| let height; | |
| let border; | |
| let rotationTime; | |
| let cursor = "auto"; | |
| if (this.mode === "WIDTH") { | |
| top = "10vh"; | |
| left = this.planetLeft_modeWidth(i) + "vw"; | |
| width = | |
| this.calcPlanetWidth_modeWidth(this.planetsFiltered[i].diameter) + "vw"; | |
| height = "80vh"; | |
| border = "0"; | |
| rotationTime = 0; | |
| } else if (this.mode === "SPEED") { | |
| width = (90 / (this.planetsFiltered.length + 1)) * (i + 1); | |
| top = `calc(50% - ${width / 2}vw - 1px)`; | |
| left = `calc(50% - ${width / 2}vw - 1px)`; | |
| width = width + "vw"; | |
| height = width; | |
| border = `solid 1px ${this.planetsFiltered[i].colors[1]}`; | |
| rotationTime = this.planetsFiltered[i].rotationTime / this.speedRatio; | |
| cursor = "pointer"; | |
| } else if (this.mode === "DISTANCE") { | |
| width = | |
| (90 / this.planetsFiltered[this.planetsFiltered.length - 1].ua) * | |
| this.planetsFiltered[i].ua; | |
| top = `calc(50% - ${width / 2}vw - 1px)`; | |
| left = `calc(50% - ${width / 2}vw - 1px)`; | |
| width = width + "vw"; | |
| height = width; | |
| border = `solid 1px ${this.planetsFiltered[i].colors[1]}`; | |
| rotationTime = this.planetsFiltered[i].rotationTime / this.speedRatio; | |
| cursor = "pointer"; | |
| } | |
| return ` | |
| top: ${top}; | |
| left: ${left}; | |
| width: ${width}; | |
| height: ${height}; | |
| border: ${border}; | |
| animation: gravitation ${rotationTime}s linear infinite; | |
| z-index: ${this.planets.length - i}; | |
| cursor: ${cursor}; | |
| `; | |
| }, | |
| // PLANET ################################################## | |
| planetStyle(index) { | |
| let right; | |
| let width; | |
| let boxshadowWidth; | |
| let unit; | |
| let backgroundImage = ""; | |
| if (this.mode === "WIDTH") { | |
| width = this.calcPlanetWidth_modeWidth( | |
| this.planetsFiltered[index].diameter | |
| ); | |
| boxshadowWidth = 1.65; | |
| unit = "vw"; | |
| right = `calc(50% - ${width / 2}${unit})`; | |
| } else if (this.mode === "SPEED") { | |
| width = 2.5; | |
| boxshadowWidth = 0.9; | |
| unit = "vw"; | |
| right = `-${width / 2}${unit}`; | |
| } else if (this.mode === "DISTANCE") { | |
| width = 4; | |
| boxshadowWidth = 2; | |
| unit = "px"; | |
| right = `-${width / 2}${unit}`; | |
| } | |
| const color1 = this.planetsFiltered[index].colors[0]; | |
| const color2 = this.planetsFiltered[index].colors[1]; | |
| if (this.planetsFiltered[index].backgroundUrl) { | |
| backgroundImage = ` | |
| background-position: center; | |
| background-repeat-style: no-repeat; | |
| background-size: ${width * 1.2}${unit}; | |
| background-image: url(${this.planetsFiltered[index].backgroundUrl}); | |
| `; | |
| } | |
| return ( | |
| ` | |
| top: calc(50% - ${width / 2}${unit}); | |
| right: ${right}; | |
| height: ${width}${unit}; | |
| width: ${width}${unit}; | |
| background: radial-gradient(circle at ${width / 3}${unit} ${ | |
| width / 3 | |
| }${unit}, ${color1}, ${color2}); | |
| box-shadow: 0 0 ${boxshadowWidth}${unit} ${color2}; | |
| ` + backgroundImage | |
| ); | |
| }, | |
| // SATELLITE CONTAINER ################################################## | |
| satelliteContainerStyle(i, j) { | |
| if ( | |
| this.planetsFiltered[i]?.satellites && | |
| this.planetsFiltered[i].satellites.length > 0 && | |
| this.planetsFiltered[i].satellites[j] | |
| ) { | |
| let width; | |
| let unit; | |
| let color; | |
| let animationTime; | |
| let animationDelay; | |
| const satellite = this.planetsFiltered[i].satellites[j]; | |
| if (this.mode === "WIDTH") { | |
| width = | |
| this.calcPlanetWidth_modeWidth(this.planetsFiltered[i].diameter) + | |
| 1.5 + | |
| 1.5 * j; | |
| unit = "vw"; | |
| color = satellite.colors[1]; | |
| if (this.speedReal) { | |
| animationTime = satellite.rotationTime / this.speedRatio; | |
| animationDelay = 0; | |
| } else { | |
| animationTime = 5 * (j + 1); | |
| animationDelay = 5 * j; | |
| } | |
| } else if (this.mode === "SPEED") { | |
| width = 2.5 + 1 + 1 * j; | |
| unit = "vw"; | |
| color = satellite.colors[1]; | |
| animationTime = satellite.rotationTime / this.speedRatio; | |
| animationDelay = 0; | |
| } else if (this.mode === "DISTANCE") { | |
| width = 0; | |
| unit = "px"; | |
| color = "transparent"; | |
| animationTime = satellite.rotationTime / this.speedRatio; | |
| animationDelay = 0; | |
| } | |
| return ` | |
| top: calc(50% - ${width / 2}${unit}); | |
| left: calc(50% - ${width / 2}${unit}); | |
| width: ${width}${unit}; | |
| height: ${width}${unit}; | |
| border-color: transparent transparent transparent ${color}; | |
| animation: gravitation ${animationTime}s linear infinite; | |
| animation-delay: -${animationDelay}s; | |
| `; | |
| } | |
| }, | |
| // SATELLITE ################################################## | |
| satelliteStyle(i, j) { | |
| if ( | |
| this.planetsFiltered[i]?.satellites && | |
| this.planetsFiltered[i].satellites.length > 0 && | |
| this.planetsFiltered[i].satellites[j] | |
| ) { | |
| const satellite = this.planetsFiltered[i].satellites[j]; | |
| let width; | |
| let boxshadowWidth; | |
| let unit; | |
| let color1; | |
| let color2; | |
| if (this.mode === "WIDTH") { | |
| width = this.calcPlanetWidth_modeWidth(satellite.diameter); | |
| boxshadowWidth = width * 0.1; | |
| unit = "vw"; | |
| color1 = satellite.colors[0]; | |
| color2 = satellite.colors[1]; | |
| } else if (this.mode === "SPEED") { | |
| width = 5; | |
| boxshadowWidth = 3; | |
| unit = "px"; | |
| color1 = satellite.colors[0]; | |
| color2 = satellite.colors[1]; | |
| } else if (this.mode === "DISTANCE") { | |
| width = 0; | |
| boxshadowWidth = 0; | |
| unit = "px"; | |
| color1 = "transparent"; | |
| color2 = "transparent"; | |
| } | |
| return ` | |
| top: calc(14.5% - ${width / 2}${unit}); | |
| left: calc(14.5% - ${width / 2}${unit}); | |
| height: ${width}${unit}; | |
| width: ${width}${unit}; | |
| background: radial-gradient(circle at ${width / 3}${unit} ${ | |
| width / 3 | |
| }${unit}, ${color1}, ${color2}); | |
| box-shadow: 0 0 ${boxshadowWidth}${unit} ${color2}; | |
| `; | |
| } | |
| } | |
| } | |
| }; | |
| </script> | |
| <style lang="scss"> | |
| body { | |
| overflow: hidden; | |
| background: black; | |
| } | |
| .planet_container, | |
| .planet, | |
| .satellite_container, | |
| .satellite, | |
| .sun { | |
| transition: all ease 2s; | |
| } | |
| .sun { | |
| position: absolute; | |
| border-radius: 50%; | |
| } | |
| .planet_container { | |
| position: absolute; | |
| border-radius: 50%; | |
| transform-origin: center; | |
| .planet { | |
| border-radius: 50%; | |
| position: absolute; | |
| cursor: pointer; | |
| .satellite_container { | |
| position: absolute; | |
| border: solid; | |
| border-width: 1px 0 0 1px; | |
| border-radius: 50%; | |
| .satellite { | |
| position: absolute; | |
| border-radius: 50%; | |
| } | |
| } | |
| } | |
| } | |
| .planet_container-SPEED, | |
| .planet_container-DISTANCE { | |
| transition: all ease 2s, box-shadow ease 0.6s; | |
| &:hover { | |
| box-shadow: 0 0 25px white !important; | |
| .planet { | |
| box-shadow: 0 0 25px white !important; | |
| } | |
| } | |
| } | |
| .planet-WIDTH { | |
| transition: all ease 2s, box-shadow ease 0.6s; | |
| &:hover { | |
| box-shadow: 0 0 25px white !important; | |
| } | |
| } | |
| .action-container { | |
| z-index: 999; | |
| position: absolute; | |
| color: white; | |
| div { | |
| margin-top: 10px; | |
| } | |
| button { | |
| padding: 10px 15px; | |
| margin-left: 10px; | |
| } | |
| input { | |
| margin-left: 13px; | |
| &[type="number"] { | |
| width: 50px; | |
| } | |
| } | |
| } | |
| .information-container { | |
| z-index: 999; | |
| position: absolute; | |
| color: white; | |
| right: 15px; | |
| bottom: 15px; | |
| border: solid 1px grey; | |
| border-radius: 15px; | |
| padding: 0 20px 10px 20px; | |
| font-size: 14px; | |
| .name { | |
| text-transform: uppercase; | |
| text-align: center; | |
| font-size: 24px; | |
| font-weight: 700; | |
| letter-spacing: 0.4rem; | |
| } | |
| } | |
| @keyframes gravitation { | |
| 0% { | |
| transform: rotate(0deg); | |
| } | |
| 100% { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .author-container { | |
| $width: 42px; | |
| position: absolute; | |
| width: 50%; | |
| left: $width/2; | |
| bottom: $width/2; | |
| .picture { | |
| position: absolute; | |
| top: -$width; | |
| margin-top: -12px; | |
| width: $width; | |
| height: $width; | |
| background-size: $width; | |
| background-position: center; | |
| background-repeat-style: "no-repeat"; | |
| background-image: url(https://assets.codepen.io/595576/internal/avatars/users/default.png?format=auto&version=1689877807&width=80&height=80); | |
| } | |
| .title { | |
| font-size: 16px; | |
| letter-spacing: 2px; | |
| color: white; | |
| } | |
| } | |
| </style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment