我如何使用我的旧iPhone 4制作GPS供电的天气时钟

目录
文章目录隐藏
  1. 获取时间
  2. 定位用户位置
  3. 如何获取天气
  4. 是否需要向你展示我的 API 密钥
  5. 是否可以全屏模式

我的第一部苹果智能手机是 iPhone 4s。我还记得在当时它是最酷的东西的时候探索它的能力的兴奋感。最后,当然,我换了一个新的型号,旧的 iPhone,仍然在薄荷状态,收集了两年的灰尘。真是浪费!

但果真如此吗?我突然想到,我可以重新利用旧苹果手机为我们的走廊创造一个有用的天气时钟。

我如何使用我的旧 iPhone 4 制作 GPS 供电的天气时钟

在这个过程中,我发现重用旧设备不仅有趣而且经济,还可以加深您对 web 标准的理解。在本教程中,我将展示如何创建一个小 web 页面来显示基于当前 GPS 位置的日期、时间和当前天气条件。我们将一起从公共 API 检索天气数据,并将 API 密钥隐藏在 PHP 文件中以确保安全性。最后,我们将查看如何添加清单文件和元标记,以便用户可以将页面保存到设备的主屏幕,然后将其作为独立应用程序启动,包括自定义图标。

获取时间

现在的时间

时钟的 HTML 有一些占位符文本,我们将最终替换它们。此时我们需要创建一个剧中的<main>容器元素和两个用来存放日期和时间的<time>语义元素,<span>标记在第二个<time>元素运行的秒之间,元素中的 datetime 属性将使用 JavaScript 动态更新。

<main id="container" class="daymode">
  <time id="date" datetime="" class="clocktext">Someday, Anymonth 15, 20XX</time>
  <time id="time" datetime="" class="clocktext">12:00<span>:00 PM</span></time>
</main>

一点响应式设计是关键。我们希望它能很好地适应 iPhone 4s 屏幕或任何其他小型智能手机的竖屏和横屏模式。当然,我们还希望它能在桌面 web 浏览器上正常工作。但是,我们不能使用任何前沿的 CSS 或 JavaScript,因为像我的 iPhone 4s 这样的老设备无法理解它。

我通过创建特定于一天中的时间的样式来完成一个档次,我们甚至可以利用媒体查询来暗化在最新 MacOS 中打开暗模式的 Mac 用户的日间风格。

标签通过 2em 的宽度将运行的秒和冒号很好地包裹,该宽度足够容纳两个大写字母(即 AM 和 PM)。最终的 CSS 需要的是媒体查询,以便在时钟处于横向模式时提升字体大小,或实际上任何视口都大于 480px 的设备。

下面是我们正在查看的基本样式,为了简洁起见,删除了最终应用程序中的更多装饰样式:

/* Base nighttime styles */
.nightmode {
  background-color: #121212;
  color: #fff;
}

/* Base daytime styles */
.daymode {
  background-color: #87ceeb;
  color: #333;
}

/* Target MacOS users who have Dark Mode enabled */
@media (prefers-color-scheme: dark) {
  .daymode {
    background-color: #003;
    color: #ffc;
  }
}

/* Used to wrap any lines of text  */
.clocktext {
  display: block;
  margin: 0;
  padding: 1px 0 0 0;
  text-align: center;
  white-space: nowrap;
  width: 100%;
}

#date {
  font-size: 1.3rem;
  padding-top: 15px;
}

#time {
  font-size: 5rem;
  margin: 1px 0 0 0;
}

#time span {
  display: inline-block;
  font-size: 1.5rem;
  line-height: 1.5;
  margin: 0 0 0 0.5em;
  padding: 0;
  text-align: left;
  vertical-align: baseline;
  white-space: normal;
  width: 2em;
}

@media (min-width: 480px){
  #date {font-size: 2rem;}
  #time {font-size: 8rem;}
  #time span {
    font-size: 2rem;
    line-height: 2;
  }
}

对于 JavaScript,我选择了 ES5,因为 ES6 的很多功能在 iOS 9.35 的移动 Safari 浏览器上无法运行,而 iOS 9.35 是 iPhone 4s 上运行的最后一个 iOS 系统。幸运的是,ES5 完全可以胜任这项任务,而且该应用程序也可以在较新的设备上正常运行。

我们需要当前日期和时间(now)、显示日期(dd)的元素、显示时间(td)的元素以及月份和日期的名称的变量。加载页面后,将调用 init 函数每秒更新一次时间(1000 毫秒)。getClockStrings()函数更新 now Date 对象中的值,并返回一个包含日期和时间的 HTML 字符串的对象。然后 updateTime()函数更新 HTML 以显示时间。这里很少使用的一个特性是 Date 对象的 toISOString()方法,该方法将机器可读的日期字符串添加到两个元素的 datetime 属性中。

下面是用 demo 中的 JavaScript 实现代码:

//注意:选择 ES5 而不是 ES6 是为了与旧的移动设备兼容
var now, dd, td;
var months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
var days = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];

document.addEventListener("DOMContentLoaded", init, false);
function init() {
  dd = document.getElementById("date");
  td = document.getElementById("time");
  updateTime();
  setInterval(updateTime,1000);
}

function updateTime() {
  var clockdata = getClockStrings();
  dd.innerHTML = clockdata.datehtml;
  td.innerHTML = clockdata.timehtml;
  dd.dateTime = now.toISOString();
  td.dateTime = now.toISOString();
}

function getClockStrings() {
  now = new Date();
  var year = now.getFullYear();
  var month = months[now.getMonth()];
  var date = now.getDate();
  var day = days[now.getDay()];
  var hour = now.getHours();
  var minutes = now.getMinutes();
  var seconds = now.getSeconds();
  var meridian = hour < 12 ? "AM" : "PM";
  var clockhour = hour > 12 ? hour - 12 : hour;
  if (hour === 0) {clockhour = 12;}
  var clockminutes = minutes < 10 ? "0" + minutes : minutes;
  var clockseconds = seconds < 10 ? "0" + seconds : seconds;
  var datehtml = day + ", " + month + " " + date + ", " + year;
  var timehtml = clockhour + ":" + clockminutes + ":" + clockseconds + " " + meridian + "";
  return {"datehtml":datehtml,"timehtml":timehtml};
}

定位用户位置

Geolocation API 能够准确获取到用户位置的方法。当页面加载时,我们可以这样做,礼貌的提示我们用户去启动定位功能。因此,我们必须在 HTML 中添加一个按钮和一个用来显示接收到的 GPS 信息。

<button id="gpsbutton">Get GPS Location</button>
<div id="gps" class="clocktext infotext"></div>

这里也有它们自己的样式,这里我们给它添加了一个 infotext 类和两个 ID 分别是 gpsbutton 和 gps

最后,我们需要在媒体查询中添加修改后的 infotext 类选择器规则。

再次,简洁的基本样式-装饰样式:

/* The geolocation coordinates upon clicking GPS button */
.infotext {
  font-size: 1.3rem;
  line-height: 1.4;
  padding: 0 5px 0 5px;
  width: auto;
}

/* The button itself */
#gpsbutton {
  -webkit-appearance: none;
  -moz-appearance: none;
  display: block;
  margin: 0 auto;
  width: auto;
  cursor: pointer;
}

#gpsbutton:hover {
  /* Styles for the hover state */
}

@media (min-width: 480px){
  /* Add the rule below to the end of the media query */
  .infotext {font-size: 1.8rem;}
}

JavaScript 需要对纬度、经度、GPS 以及 GPS 进行一些新变量。单击时,GPS 按钮调用 getLocation()函数,该函数测试浏览器中地理位置支持的可用性。如果找到这样的支持,它将调用导航器的 getCurrentPosition 方法。并将引用传递给名为 showPosition 的成功回调函数和名为 geoError 的错误回调函数。

此时,浏览器将请求用户允许获取其 GPS 位置。如果访问者拒绝,则显示适当的消息。如果用户同意,showPosition()函数将显示纬度和经度,并隐藏 GPS 按钮。

js 获取用户地理位置

JavaScript 代码:

// ...
var lat, lon, gd, gpsbutton;
// ...
function init(){
  // ...
  gd = document.getElementById("gps");
  gpsbutton = document.getElementById("gpsbutton");
  gpsbutton.addEventListener("click",getLocation,false);
  // ...
}

function updateTime(){
  // ...
  var sec = now.getSeconds();
  var minutes = now.getMinutes();
  if (sec === 0){
    if (minutes % 5 === 0){
      getLocation(); // Get location every 5 minutes while the app is running
    }
  }
}

function getClockStrings(){
  // ...
}

function getLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showPosition,geoError);
  }else{
    gd.innerHTML = "location unknown";
  }
}

function geoError(){
  gd.innerHTML = "location detection disabled";
}

function showPosition(position) {
  gpsbutton.style.display = "none";
  lat = position.coords.latitude;
  lon = position.coords.longitude;
  gd.innerHTML = "GPS: " + lat.toFixed(2) + " | " + lon.toFixed(2);
}

如何获取天气

向这个时钟添加当前的天气条件是一个有用的特性。幸运的是,在 OpenWeatherMap 上的优秀人员允许免费访问基本天气信息。在那里注册一个免费帐户,您将获得一个 API 密钥,您可以在 web 开发中使用它。普通账户提供了当前的天气和 5 天的天气预报,你可以在任何有效的 GPS 位置上进行预测。只要确保每分钟调用 API 的次数不超过 60 次,否则您将被要求升级到付费帐户。一旦有了 API 键,就用它替换代码示例中的单词 YOUR_API_KEY_HERE,然后将代码粘贴到浏览器的位置栏中。用不同的纬度和经度做实验。你可以在 LatLong 上找到任何主要城市的坐标,坐标以你需要的十进制格式给出。

http://api.openweathermap.org/data/2.5/weather?lat=40.15&lon=-75.21&APPID=YOUR_API_KEY_HERE

HTML 代码:

在 GPS 下方,将以下内容添加到 HTML 中。

<div id="weather" class="clocktext infotext"></div>
<img id="icon" src="https://openweathermap.org/img/w/01n.png" alt="weather icon" />

CSS 代码:

CSS 需要新的天气和图标图像的样式。注意,图标最初设置为 0 不透明度。一旦检索到有效的天气信息,JavaScript 代码中就会更改这一点。

#weather {
  display: block;
  width: auto;
}

#icon {
  display: inline-block;
  opacity: 0;
  vertical-align: top;
  width: 50px;
  height: 50px;
}

@media (min-width: 480px){
  /* Add the rule below to the end of the media query */
  #weather {display: inline-block;}
}

JavaScript 代码:

我们需要添加变量来引用天气 URL、天气(wd)和天气图标。我们还需要决定华氏温度或摄氏温度。usephp 的布尔值现在应该设置为 false。稍后我们将讨论在 PHP 文档中隐藏 API 密钥。locationrequired 布尔值将帮助我们避免在用户请求天气和地理定位 api 之前调用它们。日落和日出时间变量将允许我们根据一天中的时间改变时钟的外观。iconurl 值提供了检索天气图标所需的 URL 的词干。我们还需要在 updateTime 函数中使用 0 到 14 之间的随机数,这样我们的所有用户就不会在每季度的同一分钟请求天气预报。如果您有自己的图标集,可以更改 iconurl 的 URL 值。PNG 图标的文件名可以在 OpenWeatherMap。org 上找到。

// ...
var weatherurl, wd, icon, weatherminute;
var temperaturescale = "F"; // 设置为 F 或 C(华氏或摄氏)
var usephp = false; // 设置为 true 以使用 PHP 文档隐藏 api 密钥
var locationRequested = false;
var sunsettime = 0;
var sunrisetime = 0;
var iconurl = "https://openweathermap.org/img/w/";

// ...
function init(){
  //... Add these lines before the updateTime call at the end of the function
  wd = document.getElementById("weather");
  icon = document.getElementById("icon");
  weatherminute = randRange(0,14);
  // ...
}

// Random number utility function
function randRange(min, max) {
  return Math.floor(Math.random()*(max-min+1))+min;
}

接下来,我们将修改 updateTime()函数的最后一个 if 块。我们希望避免对天气和地理定位 API 的不必要调用,因此测试 sec === 0 非常重要,以确保我们不会在给定的一分钟内调用这两个 API 中的任何一个 60 次。我们还希望仅当用户批准了浏览器的地理位置请求时才调用 api。

function updateTime(){
  //...
  if (locationRequested && sec === 0){
    checkForSunset(); // 每分钟检查一次日落
    if (minutes % 15 === weatherminute){
        getWeather(); // 在应用程序运行时,每 15 分钟获取一次天气信息
        // weatherminute 是一个介于 0 和 14 之间的随机数
        // 用户不会同时访问 API
    }
    if (minutes % 5 === 0){
      getLocation(); // 在应用程序运行时,每 5 分钟获取一次位置信息
    }
  }
}

在 showPosition()函数中,请求天气数据的 URL 要么是指向 PHP 文件的相对路径,要么是指向 OpenWeatherMap.org 服务的完整 HTTPS URL。在这两种情况下,我们都将在 URL 的查询字符串中传递纬度和经度。对于 APPID,请用您自己的 API 键替换这里的单词 your_api_key__ 除非您使用下面步骤 4 中讨论的 PHP 解决方案。

function showPosition(position) {
  //...
  if (usephp){
    weatherurl = "clock.php?lat=" + lat + "&lon=" + lon;
  }else{
    weatherurl = "https://api.openweathermap.org/data/2.5/weather?";
    weatherurl += "lat=" + lat + "&lon=" + lon + "&APPID=";
    weatherurl += "YOUR_API_KEY_HERE";
  }
  if (!locationRequested){
    getWeather();
    locationRequested = true;
  }
}

showPosition()函数然后调用 getWeather(),后者更新 weather,以便在检索天气数据时让用户知道发生了什么事情。我选择使用较老的 XMLHttpRequest 标准,因为像 iPhone 4s 这样的老设备不支持 fetch。如果天气数据请求是通过 PHP 文档传递的,那么响应类型将是“document”而不是纯文本,因此我们必须对此进行测试。如果是这样,我们需要的 JSON 对象将位于响应体的 textContent 属性中。否则,我们只需要在 responseText 属性中找到纯文本。然后将数据解析为 JSON 对象并发送给 processWeather()函数。

function getWeather() {
  wd.innerHTML = "getting weather";
  var xhttp = new XMLHttpRequest();
  xhttp.responseType = usephp ? "document" : "text"; 
  // PHP 文件返回文档,而不是纯文本
  xhttp.onreadystatechange = function() {
    if (this.readyState === 4 && this.status === 200) {
      // 使用 PHP 作为数据源时,我们需要' textContent '
      // 返回的文档主体的
      var data = usephp ? xhttp.response.body.textContent : xhttp.responseText;
      processWeather(JSON.parse(data));
    }
  };
  xhttp.open("GET", weatherurl, true);
  xhttp.send();
}

发送给 processWeather()的 JSON 对象如下所示:

{"coord":{"lon":-75.21,"lat":40.15},
"weather":[{"id":701,"main":"Mist","description":"mist","icon":"50n"}],
"base":"stations",
"main":{"temp":276.42,"pressure":1011,"humidity":100,"temp_min":275.15,"temp_max":277.15},"visibility":16093,"wind":{"speed":2.1,"deg":310},"clouds":{"all":90},"dt":1545021480,
"sys":{"type":1,"id":4743,"message":0.1513,"country":"US","sunrise":1545049047,"sunset":1545082605},"id":5190089,"name":"Fort Washington","cod":200}

从这个 JSON 数据中,我们需要获取包含当前条件描述和天气图标文件名的 weather 属性。然后用一个新的 src 属性值更新 html 中 ID 为“icon”的标记,以加载图标图像。温度是主要属性的一部分,但它必须转换为华氏或摄氏(大多数人认为不是开尔文)。完成此操作后,可以将当前条件分配给 weather 的 innerHTML 属性。日出和日落的时间在数据的 sys 属性中找到。一旦有了这些,我们就可以调用 checkForSunset()函数并修改样式以匹配一天中的时间。

function processWeather(data){
  var weather = data["weather"][0];
  icon.src = iconurl + weather.icon + ".png";
  icon.style.opacity = 1;
  var localtemperature = convertTemperature(data["main"].temp).toFixed(0);
  var weatherstring = localtemperature + "°" + temperaturescale + "  " + weather.description;
  wd.innerHTML = weatherstring;
  sunsettime = Number(data["sys"].sunset);
  sunrisetime = Number(data["sys"].sunrise);
  checkForSunset();
}

function checkForSunset(){
  var nowtime = now.getTime()/1000;
  // 如果一天中的时间是在日落之后,则更改表示样式
  // 或者在第二天日出之前
  var isDark = nowtime > sunsettime || nowtime < sunrisetime;
  document.getElementById("container").className = isDark ? "nightmode":"daymode";
}

function convertTemperature(kelvin){
  // 将开尔文温度转换为摄氏或华氏温度
  var celsius = (kelvin - 273.15);
  return temperaturescale === "F" ? celsius * 1.8 + 32 : celsius;
}

是否需要向你展示我的 API 密钥

我使用一次性的 API 密钥创建了一个可用的演示程序。然而,通常情况下,在前端 web 应用程序的代码中以纯文本形式放置 API 键似乎不是一个好主意。其他人可能会复制它并使用您的 API 访问配额。如果您可以访问典型的 web 服务器(CodePen 不支持 PHP),那么您可以将 API 密钥隐藏在 PHP 文件中。下面是一些示例代码。将 API 密钥替换为时钟并上传文件。将 php 放入与主 HTML 文件相同的目录中。PHP 文件充当 OpenWeatherMap API 的代理,我们在演示中使用 OpenWeatherMap API 获取数据。如果它有任何问题获取天气数据,它只是返回一个适当的结构化对象“天气不可用”作为描述和温度转换为 0°f。API 密钥从来没有从服务器传输到浏览器,所以没有什么可窥探的。

如果您知道一种安全的、无服务器的解决方案,可以隐藏 API 密钥(也许是 Netlify 或 Docker ?),我很想听听读者们的想法,因为不必启动我们自己的服务器来存储和访问数据。如果你有什么想法,请插嘴。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Clock Data</title></head>
<body>
<?php
error_reporting(0);
$latitude = "80";
$longitude = "-85";

if (isset($_GET["lat"]) && isset($_GET["lon"])) {
  $latitude = $_GET["lat"];
  $longitude = $_GET["lon"];
}

$endpoint = "http://api.openweathermap.org/data/2.5/weather?";
$apikey = "YOUR_API_KEY_HERE";
$weatherurl = $endpoint . "lat=" . $latitude . "&lon=" . $longitude . "&appid=" . $apikey;
$jsonfile = file_get_contents($weatherurl);

if ($jsonfile !== false){
  echo "$jsonfile";
} else {
  echo '{"weather":[{"description":"Weather Unavailable","icon":"01n"}],"main":{"temp":255.372278}}';
}

?>
</body>
</html>

如果其他人试图从另一个域使用这个 PHP 文件,浏览器应该会抛出如下示例所示的错误。我在 makepages。com 域中加载了一份天气时钟的副本,并试图访问 shearspi 雷麦雷 a.com 域中的 PHP 文件。现在,在典型的商业 web 服务器安装中,默认情况下都采用同源策略。您可能需要确认您正在使用的服务器上的情况。

[Error] Origin https://makepages。com is not allowed by Access-Control-Allow-Origin.
[Error] XMLHttpRequest cannot load https://shearspiremedia。com/demos/clock/clock.php?lat=40.14616446413611&lon=-75.20946717104738 due to access control checks.

接下来,更新 JavaScript 文件中的 usephp 变量并测试:

var usephp = true; // 设置为 true 以使用 PHP 文档隐藏 api 密钥

是否可以全屏模式

一旦应用程序启动并正常工作,它就可以加载到任何智能手机浏览器上。然而,iPhone 不得不忍受位置栏和页脚的存在。讨厌的东西!

如果能全屏观看就太好了。要做到这一点,我们需要一个清单文件来告诉设备,我们希望将时钟视为一个独立的应用程序,并告诉 Android 设备应用程序图标的位置。这是我保存为 manifest 的 manifest 文件。json 在与 HTML 文件相同的目录中。您可以使用 Web 应用程序清单生成器创建自己的清单文件。一定要调整图标文件名在您自己的清单文件和链接标签在 HTML 中,我们看到这里:

{
  "short_name": "Weather Clock",
  "name": "Weather Clock by Shearspire Media",
  "icons": 
    {
      "src": "icons/launcher-icon-1x.png",
      "type": "image/png",
      "sizes": "48x48"
    },
    {
      "src": "icons/launcher-icon-2x.png",
      "type": "image/png",
      "sizes": "96x96"
    },
    {
      "src": "icons/launcher-icon-128.png",
      "type": "image/png",
      "sizes": "128x128"
    },
    {
      "src": "icons/launcher-icon-152.png",
      "type": "image/png",
      "sizes": "152x152"
    },
    {
      "src": "icons/launcher-icon-4x.png",
      "type": "image/png",
      "sizes": "192x192"
    }
  ],
  "orientation": "landscape",
  "display": "standalone",
  "start_url": "index.html"
}

对于主屏幕图标,我们还需要一组 192px、152px、128px、96px 和 48px 的正方形 PNG 图像。将它们保存到与 HTML 文件相同的文件夹中的 icons 文件夹中。使用清单中找到的文件名。Web 应用程序清单生成器将通过上传一张 512 x 512 像素的图像来创建除 48px 之外的所有所需大小的图标。下面是我制作的简单图标:

weather 图标制作

最后,我们需要在 HTML 文件的头部添加一些元标记和链接标记,以使这一切正常工作。这是完成的索引。html 文件代码,包括所有在代码页上看不到的头标记。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta name="mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <link rel="manifest" href="manifest.json">
  <link rel="apple-touch-icon" sizes="48x48" href="icons/launcher-icon-1x.png">
  <link rel="apple-touch-icon" sizes="96x96" href="icons/launcher-icon-2x.png">
  <link rel="apple-touch-icon" sizes="128x128" href="icons/launcher-icon-128.png">
  <link rel="apple-touch-icon" sizes="152x152" href="icons/launcher-icon-152.png">
  <link rel="apple-touch-icon" sizes="192x192" href="icons/launcher-icon-4x.png">
  <title>Weather Clock by ShearSpire Media</title>
  <script src="clock.js"></script>
  <link rel="stylesheet" type="text/css" href="clock.css">
</head>
<body>
  /* Clock markup */
</body>
</html>

用户现在可以点击 iPhone 上的分享按钮,选择“添加到主屏幕”,会出现一个图标,将时钟作为一个全屏独立应用程序启动。享受吧!”

如果用户的确切位置不是必需的,我们可以完全避免使用 Geolocation API,并使用几个 IP 地址服务中的任意一个来获得大致的位置。在上面的演示中,从 extreme-ip-lookup.com 接收 JSON 对象,以获得用户的大致 GPS 位置。它显示 JSON 中找到的城市和区域值,而不是 GPS 坐标。在这种情况下,用户应该清楚天气位置是邻近的城镇。

「点点赞赏,手留余香」

7

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » 我如何使用我的旧iPhone 4制作GPS供电的天气时钟

3 评论

  1. 学习了,神操作啊

  2. 学习一下,思想不错。

回复 码云 取消回复