关于前端:五十音小游戏中的前端知识

7次阅读

共计 12136 个字符,预计需要花费 31 分钟才能阅读完成。

背景

在日语学习初期阶段,我发现日语五十音的记忆并不是很容易的,片假名的记忆尤其令人劳神。这时我想如果有一个利用能够充分利用碎片工夫,在午休或地铁上随时能够练习五十音该多好。于是搜寻 App Store,的确有很多五十音学习的小软件,然而商店的软件不是含有内购、夹带广告、就是动辄 40M 以上,没找到一个本人称心的利用。于是打算本人写一个,次要介绍本人在开发设计该利用过程中的一些播种。

实现

在线体验地址: https://dragonir.github.io/ka…

实现成果如下,该利用次要分为三个页面:

  • 首页:包含菜单选项(平假名练习、片假名练习、混合练习)、深色模式切换按钮。
  • 答题页:包含残余机会和分数显示区、两头出题区、底部答题按钮。
  • 后果页:后果分数显示和返回首页按钮。

答题逻辑规定是从给出的 4 个答案按钮中选出题目展示区的那个单词对应正确的那个选项,利用依据点击给出错对反馈并进行记分,谬误 10 次后游戏完结,加载后果页。游戏逻辑实现不是本文的次要内容,因而前面不再赘述。本文后续次要内容是此次小游戏开发流程波及到的前端常识的介绍。

深色模式 ⚪⚫

随着 Windows 10MacOsAndroid 等零碎陆续推出深色模式,浏览器也开始反对检测零碎主题色配置,越来越多的网页利用都配置了深色模式切换性能。为了优化 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 对象具备属性 matchesmedia,办法 addListenerremoveListener

应用 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 ManifestService Worker 来实现装置和离线等性能。

特点:

  • 渐进式:实用于选用任何浏览器的所有用户,因为它是以渐进式加强作为外围主旨来开发的。
  • 自适应:适宜任何机型:桌面设施、挪动设施、平板电脑或任何将来设施。
  • 连贯无关性:可能借助于服务工作线程在离线或低质量网络情况下工作。
  • 离线推送:应用推送音讯告诉,可能让咱们的利用像 Native App 一样,晋升用户体验。
  • 及时更新:在服务工作线程更新过程的作用下时刻放弃最新状态。
  • 安全性:通过 HTTPS 提供,以避免窥探和确保内容不被篡改。

配置页面参数

在我的项目根目录增加文件 manifest.webmanifestmanifest.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"
    }
  ]
}

参数阐明

  • nameWeb App 的名称,也是保留到桌面上时利用图标的名称。
  • short_namename 过长时,将会应用 short_name 代替 name 显示,是 Web App 的简称。
  • start_url:指定了用户关上该 Web App 时加载 URLURL 会绝对于 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/appm…

配置 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 的款式,有 DefaultBlackBlack-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 常识能够拜访文章开端链接 🔗

在根目录增加 sw.js,定义缓存信息和办法

// 定义缓存的 key 值
const cacheName = 'kana-v1';
// 定义须要缓存的文件
const staticAssets = [
  './',
  './index.html',
  './assets/css/main.css',
  './assets/js/main.js',
  './assets/images/bg.png'
  // ...
];

// 监听 install 事件,装置实现后,进行文件缓存
self.addEventListener('install', async e => {
  // 找到 key 对应的缓存并且取得能够操作的 cache 对象
  const cache = await caches.open(cacheName);
  // 将须要缓存的文件加进来
  await cache.addAll(staticAssets);
  return self.skipWaiting();});

// 监听 activate 事件来更新缓存数据
self.addEventListener('activate', e => {
  // 保障第一次加载 fetch 触发
  self.clients.claim();});

// 监听 fetch 事件来应用缓存数据:self.addEventListener('fetch', async e => {
  const req = e.request;
  const url = new URL(req.url);
  if (url.origin === location.origin) {e.respondWith(cacheFirst(req));
  } else {e.respondWith(networkAndCache(req));
  }
});

async function cacheFirst(req) {
  // 判断以后申请是否须要缓存
  const cache = await caches.open(cacheName);
  const cached = await cache.match(req);
  // 有缓存就用缓存,没有就从新发申请获取
  return cached || fetch(req);
}

async function networkAndCache(req) {const cache = await caches.open(cacheName);
  try {
    // 缓存报错还间接从新发申请获取
    const fresh = await fetch(req);
    await cache.put(req, fresh.clone());
    return fresh;
  } catch (e) {const cached = await cache.match(req);
    return cached;
  }
}

sw.js 中采纳的规范的 web worker 的编程形式,因为运行在另一个全局上下文中 (self),这个全局上下文不同于 window,所以采纳 self.addEventListener()

Cache APIService Worker 提供用来操作缓存的的接口,这些接口基于 Promise 实现,包含 CacheCache StorageCache 间接和申请打交道,为缓存的 Request / Response 对象对提供存储机制,CacheStorage 示意 Cache 对象的存储实例,咱们能够间接应用全局的 caches 属性拜访 Cache API

** Cache 相干 API 阐明:

  • Cache.match(request, options):返回一个 Promise 对象,resolve 的后果是跟 Cache 对象匹配的第一个曾经缓存的申请。
  • Cache.matchAll(request, options):返回一个 Promise 对象,resolve 的后果是跟 Cache 对象匹配的所有申请组成的数组。
  • Cache.addAll(requests):接管一个 URL 数组,检索并把返回的 response 对象增加到给定的 Cache 对象。
  • Cache.delete(request, options):搜寻 key 值为 requestCache 条目。如果找到,则删除该 Cache 条目,并且返回一个 resolvetruePromise 对象;如果未找到,则返回一个 resolvefalsePromise 对象。
  • Cache.keys(request, options):返回一个 Promise 对象,resolve 的后果是 Cache 对象 key 值组成的数组。

注:应用 request.clone()response.clone() 是因为 requestresponse 是一个流,只能耗费一次。缓存时曾经耗费一次,再发动 HTTP 申请还要耗费一次,此时应用 clone 办法克隆申请。

至此,当已装置的 Service Worker 页面被关上时,便会触发 Service Worker 脚本更新。当上次脚本更新写入 Service Worker 数据库的工夫戳与本次更新超过 24 小时,便会触发 Service Worker 脚本更新。当 sw.js 文件扭转时,便会触发 Service Worker 脚本更新。更新流程与装置相似,只是在更新装置胜利后不会立刻进入 active 状态,更新后的 Service Worker 会和原始的 Service Worker 独特存在,并运行它的 install,一旦新 Service Worker 装置胜利,它会进入 wait 状态,须要期待旧版本的 Service Worker 进 / 线程终止。

更多 Server Worker 进阶常识能够查看文章开端链接 🔗

实现成果

PC 端 🖥️Windows上,在浏览器中首次关上利用后会有装置提醒,点击装置图标之后进行装置,桌面和开始菜单中会生成利用快捷方式,点击快捷方式就能够关上利用。

Mac 💻:下面 chromiumn 内核 的浏览器(chromeopera、新版edge)也是相似的。装置之后会在 launchpad 中生成快捷方式。

挪动端 📱iPhone。浏览器中抉择保留到桌面,就可生成桌面图标,点击图标关上离线利用。

樱花飘落动画 🌸

为加强视觉效果和趣味性,于是在页面减少了樱花 🌸 飘落的成果。飘落成果动画次要应用到 Element.animate() 办法。

Elementanimate() 办法是一个创立新 Animation 的便捷办法,将它利用于元素,而后运行动画。它将返回一个新建的 Animation 对象实例。一个元素上能够利用多个动画成果。你能够通过调用此函数取得这些动画成果的一个列表:Element.getAnimations()

根本语法

var animation = element.animate(keyframes, options);

参数

  • keyframes:关键帧。一个对象,代表关键帧的一个汇合。
  • options:可选项代表动画持续时间的整形数字 (以毫秒为单位), 或者一个蕴含一个或多个工夫属性的对象:

    • id:可选,在 animate() 里可作为惟一标识的属性: 一个用来援用动画的字符串(DOMString
    • delay:可选,开始工夫的提早毫秒数,默认值为 0
    • direction:可选,动画的静止方向。向前运行 normal、向后运行 reverse、每次迭代后切换方向 alternate,向后运行并在每次迭代后切换方向 alternate-reverse。默认为 normal
    • duration:可选,动画实现每次迭代的毫秒数,默认值为 0
    • easing:可选,动画随工夫变动的频率。承受预设的值包含 lineareaseease-inease-outease-in-out及一个自定义值 cubic-bezier,如 cubic-bezier(0.42, 0, 0.58, 1)。默认值为 linear
    • endDelay:可选,一个动画完结后的提早,默认值为 0
    • fill:可选,定义动画成果对元素的影响机会,backwards 动画开始前影响到元素上、forwards 动画实现后影响到元素上、both 两者兼具。默认值为 none
    • iterationStart:可选,形容动画应该在迭代中的哪个点开始。例如,0.5 示意在第一次迭代中途开始,并且设置此值后,具备 2 次迭代的动画将在第三次迭代中途完结。默认为 0.0
    • iterations:可选,动画应该反复的次数。默认为 1,也能够取 infinity 的值,使其在元素存在时反复。

以下代码为本例中的具体实现,HTML中有若干个 .petal 元素,而后 JavaScript 中获取到所有 .petal 元素增加随机动画,css 中增加两种旋转和变形两种动画,实现樱花花瓣飘落的成果。

<div id="petals_container">
  <div class="petal"></div>
  <!-- ... -->
  <div class="petal"></div>
</div>
var petalPlayers = [];
function animatePetals() {var petals = document.querySelectorAll('.petal');
  if (!petals[0].animate) {var petalsContainer = document.getElementById('petals_container');
    return false;
  }
  for (var i = 0, len = petals.length; i < len; ++i) {var petal = petals[i];
    petal.innerHTML = '<div class="rotate"><img src="petal.png"class="askew"></div>';
    var scale = Math.random() * .6 + .2;
    var player = petal.animate([{transform: 'translate3d(' + (i / len * 100) + 'vw,0,0) scale(' + scale + ')',
        opacity: scale
      },
      {transform: 'translate3d(' + (i / len * 100 + 10) + 'vw,150vh,0) scale(' + scale + ')',
        opacity: 1
      }
    ], {duration: Math.random() * 90000 + 8000,
      iterations: Infinity,
      delay: -(Math.random() * 5000)
    });
    petalPlayers.push(player);
  }
}
animatePetals();
.petal .rotate {
  animation: driftyRotate 1s infinite both ease-in-out;
  perspective: 1000;
}
.petal .askew {transform: skewY(10deg);
  display: block;
  animation: drifty 1s infinite alternate both ease-in-out;
  perspective: 1000;
}
.petal:nth-of-type(7n) .askew {
  animation-delay: -.6s;
  animation-duration: 2.25s;
}
.petal:nth-of-type(7n + 1) .askew {
  animation-delay: -.879s;
  animation-duration: 3.5s;
}
/* ... */
.petal:nth-of-type(9n) .rotate {animation-duration: 2s;}
.petal:nth-of-type(9n + 1) .rotate {animation-duration: 2.3s;}
/* ... */
@keyframes drifty {
  0% {transform: skewY(10deg) translate3d(-250%, 0, 0);
    display: block;
  }
  100% {transform: skewY(-12deg) translate3d(250%, 0, 0);
    display: block;
  }
}
@keyframes driftyRotate {
  0% {transform: rotateX(0);
    display: block;
  }
  100% {transform: rotateX(359deg);
    display: block;
  }
}

残缺代码可查看文后链接 🔗

CSS 判断手机横屏

本例 50 音小游戏 利用是针对挪动端开发,未作 pc 端的款式适配,所以能够增加一个横屏疏导页面提醒用户应用竖屏。在 CSS 中判断挪动设施是否处于横屏状态,须要用到 aspect-ratio 进行媒体查问,通过测试 viewport 的宽高比来进行判断。

aspect-ratio宽高比属性被指定为 <ratio> 值来代表 viewport 的宽高比。其为一个范畴,能够应用 min-aspect-ratiomax-aspect-ratio 别离查问最小和最大值。根本语法如下:

/* 最小宽高比 */
@media (min-aspect-ratio: 8/5) {// ...}
/* 最大宽高比 */
@media (max-aspect-ratio: 3/2) {// ...}
/* 明确的宽高比, 放在最下部避免同时满足条件时的笼罩 */
@media (aspect-ratio: 1/1) {// ...}

在利用中的具体实现形式是增加一个 .mod_orient_layer 疏导层并暗藏,当达到最小宽高比时将其显示:

<div class="mod_orient_layer"></div>
.mod_orient_layer {
  display: none;
  position: fixed;
  height: 100%;
  width: 100%;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 999;
  background: #FFFFFF url('landscape.png') no-repeat center;
  background-size: auto 100%;
}
@media screen and (min-aspect-ratio: 13/8) {
  .mod_orient_layer {display: block;}
}

实现成果:

兼容性

上面是本文中波及的几个属性的兼容性视图,在理论生产我的项目中须要留神兼容性适配。

Photoshop 技能

logo 设计

logo 次要由两个元素形成由一个 ⛩️ 图标和日语平假名 形成,都是经典的日系元素,同时对 进行拉伸突变,造成相似 ⛩️ 的暗影,使字母和图形奇妙连贯在一起,使画面谐和。logo 背景色应用利用主题背景色,与页面在无形之中建立联系,造成 全链路 对立格调规范。(编不上来了。。。😂

鸟居原始模型来源于 dribbble:https://dribbble.com

内部链接及参考资料

  • 樱花散落动画完整版 https://codepen.io/dragonir/f…
  • Dark Mode Support in WebKit https://webkit.org/blog/8840/…
  • PWA 技术实践 + 实战全解析 https://www.cnblogs.com/yangy…
  • H5 PWA 技术 https://zhuanlan.zhihu.com/p/…
  • aspect-ratio https://developer.mozilla.org…
  • Service Worker https://developer.mozilla.org…
  • Element.animate() https://developer.mozilla.org…

作者:dragonir 博客地址:https://segmentfault.com/a/11…

正文完
 0