<div class="main-app">
<header>
<h1 class="title my-transition">Weather App</h1>
<p class="subtitle my-transition">Simple Weather App using
<a href="https://darksky.net/dev/">Dark Sky API</a>,
<a href="https://darkskyapp.github.io/skycons/">Skycons</a>.</p>
</header>
<section class="currently my-box-shadow my-transition">
<div class="heading">
<div class="title">Currently</div>
<div class="menu">
<span class="item active" unit="F">°F</span>
<span class="item" unit="C">°C</span>
</div>
</div>
<div class="content">
<div class="item location"></div>
<div class="item temp"></div>
<div class="item icon">
<canvas id="current-icon"></canvas>
</div>
<div class="item summary"></div>
<div class="item humidity">
<i class="fa fa-tint"></i>
<span class="detail"></span>
</div>
</div>
</section>
<section class="next-48h my-box-shadow my-transition">
<div class="heading">
<div class="title">Next 48 Hours</div>
<div class="select-style">
<select>
</select>
</div>
</div>
<div class="content">
<div class="item time"></div>
<div class="item temp"></div>
<div class="item icon">
<canvas id="next48h-icon"></canvas>
</div>
<div class="item summary"></div>
<div class="item humidity">
<i class="fa fa-tint"></i>
<span class="detail"></span>
</div>
</div>
</section>
<section class="next-7d my-box-shadow my-transition">
<div class="heading">
<div class="title">Next 7 Days</div>
<div class="select-style">
<select>
</select>
</div>
</div>
<div class="content">
<div class="item day"></div>
<div class="item temp"></div>
<div class="item icon">
<canvas id="next7d-icon"></canvas>
</div>
<div class="item summary"></div>
<div class="item humidity">
<i class="fa fa-tint"></i>
<span class="detail"></span>
</div>
</div>
</section>
<footer>Made with
<i class="fa fa-heart heart"></i> by
<a href="http://about.phamvanlam.com">Lam Pham</a>
</footer>
</div>
<div class="loading">
<div>Please, waiting for retrieving your location and the local weather.</div>
<div class="lds-ellipsis">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://rawgit.com/darkskyapp/skycons/master/skycons.js"></script>
* {box-sizing: border-box;}
body {
margin: 15px;
background-color: #ededed;
color: #404040;
font: normal normal normal 1rem/1.6 Nunito Sans, Helvetica, Arial, sans-serif;
}
header .title{
line-height: 1.2;
font-weight: 700;
font-size: 2.5rem;
margin: 45px auto;
margin-bottom: 0;
text-align: center;
}
header .subtitle {
text-align: center;
font-size: 0.8rem;
margin-bottom: 45px;
color: #606060
}
a {
text-decoration: none;
color: #3da4ab;
cursor: pointer;
-webkit-transition: all 0.35s ease-in-out;
-moz-transition: all 0.35s ease-in-out;
transition: all 0.35s ease-in-out;
}
a:hover {
color: #fe4a49;
}
footer{
text-align: center;
margin-top: 15px;
margin-bottom: 25px;
}
footer .heart {color: #fe4a49;}
p {
margin-top: 0px;
margin-bottom: 0px;
}
.my-box-shadow{
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
-moz-box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
}
.my-transition {
-webkit-transition: all 0.35s ease-in-out;
-moz-transition: all 0.35s ease-in-out;
transition: all 0.35s ease-in-out;
}
section {
width: 100%;
background-color: #fff;
margin-bottom: 15px;
}
section:hover {
box-shadow: 0 2px 5px rgba(0,0,0,0.12), 0 3px 5px rgba(0,0,0,0.24);
-webkit-box-shadow: 0 2px 5px rgba(0,0,0,0.12), 0 3px 5px rgba(0,0,0,0.24);
-moz-box-shadow: 0 2px 5px rgba(0,0,0,0.12), 0 3px 5px rgba(0,0,0,0.24);
}
section::after {
content: "";
clear: both;
display: table;
}
.heading {
position: relative;
background: #3da4ab;
color: #fff;
height: 50px;
display: flex;
display: -webkit-flex;
align-items: center;
justify-content: space-between;
}
.heading .title {
font-weight: 500;
padding-left: 15px;
float: left;
}
.heading .title
.heading .menu {
float: right;
}
.heading .menu .item {
text-align: left;
background-color: #3da4ab;
padding: 10px 15px 10px 0px;
}
.heading .menu .item:not(.active):hover {
text-decoration: underline;
cursor: pointer;
}
.heading .menu .item.active {
color: #f6cd61;
}
.select-style {
border: 1px solid #ccc;
border-radius: 3px;
background: #fafafa;
margin-right: 15px;
text-align: right;
}
.select-style select {
cursor: pointer;
padding: 5px 8px;
border: none;
box-shadow: none;
background: transparent;
background-image: none;
-webkit-appearance: none;
-moz-appearance: none;
font: normal normal normal 1rem/1.6 Nunito Sans, Helvetica, Arial, sans-serif !important;
}
.select-style select:focus {
outline: none;
}
.content {
text-align: center;
padding: 15px;
}
.content .item {
margin-bottom: 5px;
}
.content .location,
.content .time,
.content .day {
text-transform: uppercase;
font-weight: 500;
font-size: 1.15rem;
margin: 15px auto;
}
.content .temp {font-size: 2.5rem;}
.content .icon canvas {
width: 150px;
height: 75px;
}
.main-app {
opacity: 0.1;
}
/* Loading */
.loading {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
display: flex;
display: -webkit-flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.lds-ellipsis {
display: inline-block;
position: relative;
width: 64px;
height: 64px;
}
.lds-ellipsis div {
position: absolute;
top: 27px;
width: 11px;
height: 11px;
border-radius: 50%;
background: #fe4a49;
animation-timing-function: cubic-bezier(0, 1, 1, 0);
}
.lds-ellipsis div:nth-child(1) {
left: 6px;
animation: lds-ellipsis1 0.6s infinite;
}
.lds-ellipsis div:nth-child(2) {
left: 6px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(3) {
left: 26px;
animation: lds-ellipsis2 0.6s infinite;
}
.lds-ellipsis div:nth-child(4) {
left: 45px;
animation: lds-ellipsis3 0.6s infinite;
}
@keyframes lds-ellipsis1 {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
@keyframes lds-ellipsis3 {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes lds-ellipsis2 {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(19px, 0);
}
}
/* End of Loading */
@media (min-width: 576px) {
header .title {
font-size: 2.75rem;
margin: 55px auto;
margin-bottom: 0;
}
}
/* Medium devices (tablets, 768px and up) */
@media (min-width: 768px) {
.heading {font-size: 1.2rem;}
section {
width: 800px;
margin: 30px auto;
}
header .title {
font-size: 4rem;
margin: 70px auto;
margin-bottom: 0px;
}
header .subtitle {
font-size: 1.2rem;
}
footer {font-size: 1.1rem;}
.content .location,
.content .time,
.content .day {
font-weight: 500;
font-size: 2.2rem;
margin: 20px auto;
}
}
/* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
header .title {
font-size: 4.7rem;
margin: 100px auto;
margin-bottom: 0px;
}
.content item {margin-bottom: 20px;}
.content .location,
.content .time,
.content .day {
font-weight: 500;
font-size: 2.75rem;
margin: 25px auto;
}
.content .summary,
.content .humidity {font-size: 1.2rem;}
.content .temp {font-size: 4rem;}
.content .icon canvas {
width: 200px;
height: 100px;
}
}
$(document).ready(() => {
let $currentContent = $(".currently .content");
let $location = $currentContent.find(".location");
let $currentTemp = $currentContent.find(".temp");
let $currentSummary = $currentContent.find(".summary");
let $currentHumidity = $currentContent.find(".humidity .detail");
let $next48hContent = $(".next-48h .content");
let $time = $next48hContent.find(".time");
let $next48hTemp = $next48hContent.find(".temp");
let $next48hSummary = $next48hContent.find(".summary");
let $next48hHumidity = $next48hContent.find(".humidity .detail");
let $next48hSelect = $(".next-48h .heading .select-style select");
let dataNext48h, indexNext48h = 1, next48hSelectInit = false;
let $next7dContent = $(".next-7d .content");
let $day = $next7dContent.find(".day");
let $next7dTemp = $next7dContent.find(".temp");
let $next7dSummary = $next7dContent.find(".summary");
let $next7dHumidity = $next7dContent.find(".humidity .detail");
let $next7dSelect = $(".next-7d .heading .select-style select");
let dataNext7d, indexNext7d = 1, next7dSelectInit = false;
let skycons = new Skycons({"color": "#3da4ab"});
let F2C = F => ((F - 32) * 5 / 9).toFixed();
let C2F = C => (C * 9 / 5 + 32).toFixed();
let currentUnit = "F";
$(".icon").on("click", event => {
let $target = $(event.target);
$target.closest(".heading").find(".menu").toggleClass("responsive");
});
$(".menu .item").on("click", event => {
let $target = $(event.target);
let unit = $target.attr("unit");
$target.closest(".menu").children().removeClass("active");
$target.addClass("active");
if (unit !== currentUnit) {
currentUnit = unit;
switchTemperatureUnit();
}
});
// Automatically enable cross-domain requests when needed
$.ajaxPrefilter(options => {
if (options.crossDomain && $.support.cors) {
options.url = `https://cors-anywhere.herokuapp.com/${options.url}`;
}
});
$.getJSON("https://ipinfo.io/", onLocationGot);
function onLocationGot(info) {
$location.text(`${info.city}, ${info.region}`);
updateWeatherInfo.apply(null, info.loc.split(","));
}
function updateWeatherInfo(lat, lon) {
let secretKey = "9b0af02f1ad5f739d0fd7a754e459238";
let requestURL = `https://api.darksky.net/forecast/${secretKey}/${lat},${lon}`;
$.getJSON(requestURL, info => {
highlightView();
updateWeatherCurrently(info.currently, $currentTemp, $currentSummary, $currentHumidity, "current-icon");
updateWeatherNext48h(info.hourly.data, indexNext48h);
updateWeatherNext7d(info.daily.data, indexNext7d);
});
}
function highlightView() {
$(".loading").css("display", "none");
$(".main-app").css("opacity", 1);
}
function updateWeatherCurrently(infoCurrently, $temp, $summary, $humidity, idIcon) {
let curTemp = infoCurrently.temperature.toFixed();
$temp.text(`${curTemp}°${currentUnit}`);
$temp.attr("value", curTemp);
$summary.text(infoCurrently.summary);
$humidity.text(`${(infoCurrently.humidity * 100).toFixed()}%`);
setWeatherIcon(idIcon, infoCurrently.icon);
}
function updateWeatherNext48h(infoHourlyData, index) {
dataNext48h = infoHourlyData;
let dataChoosen = dataNext48h[index];
let date = new Date(dataChoosen.time * 1000);
let formatDate = date.toLocaleString("en-US", {weekday: 'long', hour: '2-digit', minute:'2-digit'});
$time.text(formatDate);
updateWeatherCurrently(dataChoosen, $next48hTemp, $next48hSummary, $next48hHumidity, "next48h-icon");
if(next48hSelectInit == false) {
let dataSelect = [];
for(let i = 1; i < dataNext48h.length; i += 6) {
date = new Date(dataNext48h[i].time * 1000);
formatDate = date.toLocaleString("en-US", {weekday: 'long', hour: '2-digit', minute:'2-digit'});
dataSelect.push(formatDate);
}
initSelect($next48hSelect, dataSelect, updateWeatherNext48h, 6, dataNext48h);
next48hSelectInit = true;
}
}
function updateWeatherNext7d(infoDailyData, index) {
dataNext7d = infoDailyData;
let dataChoosen = dataNext7d[index];
let date = new Date(dataChoosen.time * 1000);
let formatDate = date.toLocaleString("en-US", {weekday: 'long'});
$day.text(formatDate);
let minTemp = dataChoosen.temperatureMin.toFixed();
let maxTemp = dataChoosen.temperatureMax.toFixed();
$next7dTemp.text(`${minTemp}°${currentUnit} / ${maxTemp}°${currentUnit}`);
$next7dTemp.attr("min", minTemp);
$next7dTemp.attr("max", maxTemp);
$next7dSummary.text(dataChoosen.summary);
$next7dHumidity.text(`${dataChoosen.humidity * 100}%`);
setWeatherIcon("next7d-icon", dataChoosen.icon);
if(next7dSelectInit == false) {
let dataSelect = [];
for(let i = 1; i < dataNext7d.length; i++) {
date = new Date(dataNext7d[i].time * 1000);
formatDate = date.toLocaleString("en-US", {weekday: 'long'});
dataSelect.push(formatDate);
}
initSelect($next7dSelect, dataSelect, updateWeatherNext7d, 1, dataNext7d);
next7dSelectInit = true;
}
}
function initSelect($select, dataSelect, updateFunc, step, data) {
dataSelect.forEach(element => {
$select.append(`<option>${element}</option>`);
});
$select.on("change", (event) => {
let $target = $(event.target);
let index = $target[0].selectedIndex * step + 1;
updateFunc(data, index);
});
}
function setWeatherIcon(id, type) {
if (type === "clear-day") {
skycons.set(id, Skycons.CLEAR_DAY);
} else if (type === "clear-night") {
skycons.set(id, Skycons.CLEAR_NIGHT);
} else if (type === "rain") {
skycons.set(id, Skycons.RAIN);
} else if (type === "snow") {
skycons.set(id, Skycons.SNOW);
} else if (type === "sleet") {
skycons.set(id, Skycons.SLEET);
} else if (type === "wind") {
skycons.set(id, Skycons.WIND);
} else if (type === "fog") {
skycons.set(id, Skycons.FOG);
} else if (type === "cloudy") {
skycons.set(id, Skycons.CLOUDY);
} else if (type === "partly-cloudy-day") {
skycons.set(id, Skycons.PARTLY_CLOUDY_DAY);
} else if (type === "partly-cloudy-night") {
skycons.set(id, Skycons.PARTLY_CLOUDY_NIGHT);
} else {
console.log("Other Weather Icon");
}
skycons.play();
}
function switchTemperatureUnit() {
switchTemperatureUnitByItem($currentTemp);
switchTemperatureUnitByItem($next48hTemp);
// next 7 days
let minTemp = $next7dTemp.attr("min"), maxTemp = $next7dTemp.attr("max");
let newMin, newMax;
if(currentUnit === "F") {
newMin = C2F(minTemp);
newMax = C2F(maxTemp);
}
else {
newMin = F2C(minTemp);
newMax = F2C(maxTemp);
}
$next7dTemp.text(`${newMin}°${currentUnit} / ${newMax}°${currentUnit}`);
$next7dTemp.attr("min", newMin);
$next7dTemp.attr("max", newMax);
}
function switchTemperatureUnitByItem($item) {
let currentTemp = $item.attr("value"), newTemp;
if(currentUnit === "F") newTemp = C2F(currentTemp);
else newTemp = F2C(currentTemp);
$item.text(`${newTemp}°${currentUnit}`);
$item.attr("value", newTemp);
}
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.