背景
在日语学习初期阶段,我发现日语五十音的记忆并不是很容易的,片假名的记忆尤其令人费神。这时我想如果有一个应用可以充分利用碎片时间,在午休或地铁上随时可以练习五十音该多好。于是搜索 App Store
,确实有很多五十音学习的小软件,但是商店的软件不是含有内购、夹带广告、就是动辄 40M
以上,没找到一个自己满意的应用。于是打算自己写一个,主要介绍自己在开发设计该应用过程中的一些收获。
实现
实现效果如下,该应用主要分为三个页面:
- 首页:包括菜单选项(平假名练习、片假名练习、混合练习)、深色模式切换按钮。
- 答题页:包括剩余机会和分数显示区、中间出题区、底部答题按钮。
- 结果页:结果分数显示和返回首页按钮。
答题逻辑规则是从给出的 4
个答案按钮中选出题目展示区的那个单词对应正确的那个选项,应用根据点击给出错对反馈并进行记分,错误 10
次后游戏结束,加载结果页。游戏逻辑实现不是本文的主要内容,因此后面不再赘述。本文后续主要内容是此次小游戏开发流程涉及到的前端知识的介绍。
深色模式 ⚪⚫
随着 Windows 10
、 MacOs
、 Android
等系统陆续推出深色模式,浏览器也开始支持检测系统主题色配置,越来越多的网页应用都配置了深色模式切换功能。为了优化 50音小游戏
的视觉体验,我也配置了深色样式,实现效果如下:
CSS
媒体查询判断深色模式
prefers-color-scheme
媒体特性用于检测用户是否有将系统的主题色设置为亮色或者暗色。使用语法如下所示:
@media (prefers-color-scheme: value) {}
其中 value
有以下 3
种值,其中:
-
light
:表示用户系统支持深色模式,并且已设置为浅色主题(默认值)。 -
dark
:表示用户系统支持深色模式,并且已设置为深色主题。 -
no-preference
:表示用户系统不支持深色模式或无法得知是否设置为深色模式(已废弃)。
若结果为
no-preference
,无法通过此媒体特性获知宿主系统是否支持设置主题色,或者用户是否主动将其设置为无偏好。出于隐私保护等方面的考虑,用户或用户代理也可能在一些情况下在浏览器内部将其设置为no-preference
。
下面例子中,当系统主题色为深色时 .demo
元素的背景色为 #FFFFFF
;当系统主题色为浅色时,.demo
元素的背景色为 #000000
。
@media (prefers-color-scheme: dark) {
.demo { background: #FFFFFF; }
}
@media (prefers-color-scheme: light) {
.demo { background: #000000; }
}
JavaScript
判断深色模式
window.matchMedia()
方法返回一个新的 MediaQueryList
对象,表示指定的媒体查询 (en-US)字符串
解析后的结果。返回的 MediaQueryList
可被用于判定 Document
是否匹配媒体查询,或者监控一个 document
来判定它匹配了或者停止匹配了此媒体查询。其中 MediaQueryList
对象具有属性 matches
和 media
,方法 addListener
和 removeListener
。
使用 matchMedia
作为判断媒介,也可以识别系统是否支持主题色:
if (window.matchMedia('(prefers-color-scheme)').media === 'not all') {
// 浏览器不支持主题色设置
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches){
// 深色模式
} else {
// 浅色模式
}
另外还可以动态监听系统深色模式的状态,根据系统深色模式的切换做出实时响应:
window.matchMedia('(prefers-color-scheme: dark)').addListener(e => {
if (e.matches) {
// 开启深色模式
} else {
// 关闭深色模式
}
});
或者单独检测深色或浅色模式:
const listeners = {
dark: (mediaQueryList) => {
if (mediaQueryList.matches) {
// 开启深色模式
}
},
light: (mediaQueryList) => {
if (mediaQueryList.matches) {
// 开启浅色模式
}
}
};
window.matchMedia('(prefers-color-scheme: dark)').addListener(listeners.dark);
window.matchMedia('(prefers-color-scheme: light)').addListener(listeners.light);
在50音小游戏中,就是使用 JavaScript
检测系统是否开启深色模式,动态添加 css
类名来自动加载深色模式,同时也提供深浅色切换按钮,可以手动切换主题。
HTML
元素中判断深色模式
页面使用图片元素时,可以直接在 HTML
中判断系统是否开启深色模式。如:
<picture>
<source srcset="dark.png" media="(prefers-color-scheme: dark)">
<img src="light.png">
</picture>
picture
元素允许我们在不同的设备上显示不同的图片,一般用于响应式。HTML5
引入了 <picture>
元素,该元素可以让图片资源的调整更加灵活。<picture>
元素零或多个 <source>
元素和一个 <img>
元素,每个 <source>
元素匹配不同的设备并引用不同的图像源,如果没有匹配的,就选择 <img>
元素的 src
属性中的 url
。
注意:
<img>
元素是放在最后一个<picture>
元素之后,如果浏览器不支持该属性则显示<img>
元素的的图片。
离线缓存
为了能够像原生应用一样可以在桌面生成快捷方式快速访问,随时随地离线使用,50音小游戏
使用了离线缓存技术,它是一个 PWA应用
。下面内容是 PWA离线应用
实现技术的简要描述。
PWA (progressing web app)
,渐进式网页应用程序,是下一代WEB应用模型
。一个PWA
应用首先是一个网页, 并借助于App Manifest
和Service Worker
来实现安装和离线等功能。
特点:
- 渐进式:适用于选用任何浏览器的所有用户,因为它是以渐进式增强作为核心宗旨来开发的。
- 自适应:适合任何机型:桌面设备、移动设备、平板电脑或任何未来设备。
- 连接无关性:能够借助于服务工作线程在离线或低质量网络状况下工作。
- 离线推送:使用推送消息通知,能够让我们的应用像
Native App
一样,提升用户体验。 - 及时更新:在服务工作线程更新进程的作用下时刻保持最新状态。
- 安全性:通过
HTTPS
提供,以防止窥探和确保内容不被篡改。
配置页面参数
在项目根目录添加文件 manifest.webmanifest
或 manifest.json
文件,并在文件内写入如下配置信息,本例中 50音小游戏
的页面参数信息配置如下:
// manifest.webmainifest
{
"name": "かなゲーム",
"short_name": "かなゲーム",
"start_url": "index.html",
"display": "standalone",
"background_color": "#fff",
"description": "かなゲーム",
"icons": [
{
"src": "assets/images/icon-64x64.png",
"sizes": "64x64",
"type": "image/png"
},
{
"src": "assets/images/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
}
]
}
参数说明:
-
name
:Web App
的名称,也是保存到桌面上时应用图标的名称。 -
short_name
:name
过长时,将会使用short_name
代替name
显示,是Web App
的简称。 -
start_url
:指定了用户打开该Web App
时加载URL
。URL
会相对于manifest
文件所在路径。 -
display
:指定了应用的显示模式,它有四个值可以选择:-
fullscreen
:全屏显示,会尽可能将所有的显示区域都占满。 -
standalone
:浏览器相关UI
(如导航栏、工具栏等)将被隐藏,看起来更像一个Native App
。 -
minimal-ui
:显示形式与standalone
类似,浏览器相关UI
会最小化为一个按钮,不同浏览器在实现上略有不同。 -
browser
:一般来说,会和正常使用浏览器打开样式一致。 - 需要说明的是,当一些系统的浏览器不支持
fullscreen
时将会显示成standalone
效果,当不支持standalone
时,将会显示成minimal-ui
的效果,以此类推。
-
-
description
:应用描述。 -
icons
:指定了应用的桌面图标和启动页图像,用数组表示:- sizes:图标大小。通过指定大小,系统会选取最合适的图标展示在相应位置上。
- src:图标路径。相对路径是相对于
manifest
文件,也可以使用绝对路径。 - type:图标图片类型。 浏览器会从
icons
中选择最接近128dp(px = dp * (dpi / 160))
的图片作为启动画面图像。
-
background_color
:指定启动画面的背景颜色,采用相同颜色可以实现从启动画面到首页的平稳过渡,也可以用来改善页面资源正在加载时的用户体验。 -
theme_color
:指定了Web App
的主题颜色。可以通过该属性来控制浏览器UI
的颜色。比如状态栏、内容页中状态栏、地址栏的颜色。
配置信息自动生成工具:https://tomitm.github.io/appmanifest/
配置 HTML
文件
在 index.html
中引入 manifest
配置文件,并在 head
中添加以下配置信息以兼容 iOS系统
<meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="かなゲーム">
<link rel="stylesheet" type="text/css" href="./assets/css/main.css">
<link rel="stylesheet" type="text/css" href="./assets/css/dark.css">
<link rel="stylesheet" type="text/css" href="./assets/css/petals.css">
<link rel="shortcut icon" href="./assets/images/icon-256x256.png">
<link rel="apple-touch-icon" href="./assets/images/icon-256x256.png"/>
<link rel="apple-touch-icon-precomposed" href="./assets/images/icon-256x256.png">
<link rel="Bookmark" href="./assets/images/icon-256x256.png" />
<link rel="manifest" href="./manifest.webmanifest">
<title>かなゲーム</title>
-
apple-touch-icon
: 指定应用图标,类似与manifest.json
文件的icons
配置,也是支持sizes
属性,来供不同场景的选择。 -
apple-mobile-web-app-capable
:类似于manifest.json
中的display
的功能,通过设置为yes
可以进入standalone
模式。 -
apple-mobile-web-app-title
:指定应用的名称。 -
apple-mobile-web-app-status-bar-style
:指定iOS移动设备的状态栏status bar
的样式,有Default
,Black
,Black-translucent
可以设置。
注册使用 Service Worker
在 index.html
中添加如下代码进行server-worker注册:
window.addEventListener('load', () => {
registerSW();
});
async function registerSW() {
if ('serviceWorker' in navigator) {
try {
await navigator.serviceWorker.register('./sw.js');
} catch (e) {
console.log(`SW registration failed`);
}
}
}
使用 serviceWorkerContainer.register()
进行 Service worker
注册,同时添加 try...catch...
容错判断,以保证在不支持 Service worker
的情况下正常运行。另外需要注意的是只有在 https
下,navigator
里才会有 serviceWorker
对象。
Service workers
本质上充当Web
应用程序、浏览器与网络(可用时)之间的代理服务器。旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用采取来适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步API
。了解更多Service workder
知识可以访问文章末尾链接