codepens/visualizing-the-sun-s-path-.../dist/script.js

265 lines
5.6 KiB
JavaScript

const { cos, PI, ceil, random } = Math;
const deg = degrees => {
return PI / 180 * degrees;
};
const maybeNegative = number => {
return random() >= .5 ? -number : number;
};
const times = (howMany, what) => {
return [...Array(howMany).keys()].map(what);
};
const query = (selector) =>
selector[0] === '#' ?
document.getElementById(selector.slice(1)) :
document.querySelectorAll(selector);
const getPlace = (placeList, index) => {
const { name, country, lat } = placeList[index];
const nameAndCountry = [name, country].filter(Boolean);
const hemisphere = lat > 0 ? 'N' : lat < 0 ? 'S' : '';
return {
name: `${nameAndCountry.join(', ')} (${lat}°${hemisphere})`,
lat };
};
const calculateMonth = (monthList, time) => {
const monthLength = 1 / 6;
const monthIndex = time / monthLength;
return monthList[ceil(monthIndex) % 12];
};
const places = [
{ name: 'South Pole', country: null, lat: -90 },
{ name: 'Vostok', country: 'Antarctica', lat: -75 },
{ name: 'Falkland Islands', country: 'UK', lat: -51 },
{ name: 'Christchurch', country: 'New Zealand', lat: -43 },
{ name: 'São Paulo', country: 'Brazil', lat: -23 },
{ name: 'Kampala', country: 'Uganda', lat: 0 },
{ name: 'Dubai', country: 'UAE', lat: 23 },
{ name: 'Istanbul', country: 'Turkey', lat: 41 },
{ name: 'Helsinki', country: 'Finland', lat: 60 },
{ name: 'Tromsø', country: 'Norway', lat: 69 },
{ name: 'Alert', country: 'Canada', lat: 82 },
{ name: 'North Pole', country: null, lat: 90 }];
const months = [
'June',
'July',
'August',
'September',
'October',
'November',
'December',
'January',
'February',
'March',
'April',
'May'];
const daySpeeds = {
paused: 0,
playing: 0.01,
rewinding: -0.01,
forwarding: 0.1 };
const maxAscension = .23;
const state = {
place: 8,
mode: 'playing',
season: 0 // 0 = june, 1 = december
};
const drawing = new Zdog.Illustration({
element: '#zdog-canvas',
dragRotate: true,
zoom: 500,
rotate: { x: -deg(25) } });
const ground = new Zdog.Cylinder({
addTo: drawing,
diameter: 1,
length: .05,
stroke: false,
color: 'forestgreen',
frontFace: 'yellowgreen',
backface: 'rgb(30, 100, 10)',
rotate: { x: deg(90) } });
const clouds = [];
const addRandomCloud = (parent, x) => {
const color = 'rgba(230, 240, 255, .2)';
const z = maybeNegative(random() / 2);
clouds.push(new Zdog.Box({
addTo: parent,
width: .001,
height: .0025,
depth: .001,
stroke: .1,
color,
translate: {
x,
y: -.3,
z } }));
clouds.push(new Zdog.Box({
addTo: parent,
width: .001,
height: .003,
depth: .001,
stroke: .15,
color,
translate: {
x: x + .1,
y: -.3,
z } }));
if (random() > .75) {
clouds.push(new Zdog.Box({
addTo: parent,
width: .001,
height: .0025,
depth: .001,
stroke: .1,
color,
translate: {
x: x + .2,
y: -.3,
z } }));
if (random() > .5) {
clouds.push(new Zdog.Box({
addTo: parent,
width: .0005,
height: .0005,
depth: .0005,
stroke: .07,
color,
translate: {
x: x + .27,
y: -.3,
z } }));
}
}
};
const sunTiltPlane = new Zdog.Anchor({
addTo: drawing });
const sunOrbitPlane = new Zdog.Anchor({
addTo: sunTiltPlane });
const sunAscensionPlane = new Zdog.Anchor({
addTo: sunOrbitPlane });
const sun = new Zdog.Shape({
addTo: sunAscensionPlane,
stroke: .1,
color: '#fd5',
translate: { y: -.5 } });
let cloudX = 0;
let cloudBoost = 2.5;
const loop = () => {
const speed = daySpeeds[state.mode];
const place = getPlace(places, state.place);
const tilt = deg(-place.lat);
if (sunTiltPlane.rotate.z !== tilt) {
sunTiltPlane.rotate.z += (tilt - sunTiltPlane.rotate.z) / 5;
}
sunOrbitPlane.rotate.x += speed;
sunAscensionPlane.translate.x =
cos(PI * state.season) * maxAscension;
state.season += speed / 180;
if (random() < speed * cloudBoost) {
cloudX += (.6 - cloudX) / 10;
cloudBoost -= (cloudBoost - .5) / 10;
addRandomCloud(drawing, cloudX);
}
clouds.forEach(cloud => {
if (cloud) {
cloud.translate.x -= speed / 10;
if (cloud.translate.x < -3) {
cloud.remove();
cloud = null;
}
}
});
query('#place').innerText = place.name;
const month = calculateMonth(months, state.season);
query('#month').innerText = month;
drawing.updateRenderGraph();
requestAnimationFrame(loop);
};
loop();
const onClickSouth = () => {
state.place--;
query('#north').removeAttribute('disabled');
if (!places[state.place - 1]) {
query('#south').setAttribute('disabled', true);
}
};
const onClickNorth = () => {
state.place++;
query('#south').removeAttribute('disabled');
if (!places[state.place + 1]) {
query('#north').setAttribute('disabled', true);
}
};
const playPause = () => {
if (state.mode !== 'paused') {
state.savedMode = state.mode;
state.mode = 'paused';
} else {
state.mode = state.savedMode;
}
query('#play').classList.toggle('hidden');
query('#pause').classList.toggle('hidden');
};
const toggleRewind = () => {
state.mode = state.mode !== 'rewinding' ?
'rewinding' :
'playing';
};
const toggleForward = () => {
state.mode = state.mode !== 'forwarding' ?
'forwarding' :
'playing';
};
query('#south').addEventListener('click', onClickSouth);
query('#north').addEventListener('click', onClickNorth);
query('#play').addEventListener('click', playPause);
query('#pause').addEventListener('click', playPause);
query('#rewind').addEventListener('click', toggleRewind);
query('#forward').addEventListener('click', toggleForward);