在日语学习初期阶段,我发现日语五十音的记忆并不是很容易的,片假名的记忆尤其令人费神。这时我想如果有一个应用可以充分利用碎片时间,在午休或地铁上随时可以练习五十音该多好。于是搜索 App Store
,确实有很多五十音学习的小软件,但是商店的软件不是含有内购、夹带广告、就是动辄 40M
以上,没找到一个自己满意的应用。于是打算自己写一个,主要介绍自己在开发设计该应用过程中的一些收获。
在线体验地址 https://dragonir.github.io/kanaApp/
实现效果如下,该应用主要分为三个页面:
答题逻辑规则是从给出的 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
:指定了应用的桌面图标和启动页图像,用数组表示:
manifest
文件,也可以使用绝对路径。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
知识可以访问文章末尾链接