文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

五十音小游戏中的前端知识小结

2024-04-02 19:55

关注

背景

在日语学习初期阶段,我发现日语五十音的记忆并不是很容易的,片假名的记忆尤其令人费神。这时我想如果有一个应用可以充分利用碎片时间,在午休或地铁上随时可以练习五十音该多好。于是搜索 App Store,确实有很多五十音学习的小软件,但是商店的软件不是含有内购、夹带广告、就是动辄 40M 以上,没找到一个自己满意的应用。于是打算自己写一个,主要介绍自己在开发设计该应用过程中的一些收获。

实现

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

实现效果如下,该应用主要分为三个页面:

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

深色模式 ⚪⚫

随着 Windows 10MacOsAndroid 等系统陆续推出深色模式,浏览器也开始支持检测系统主题色配置,越来越多的网页应用都配置了深色模式切换功能。为了优化 50音小游戏 的视觉体验,我也配置了深色样式,实现效果如下:

CSS 媒体查询判断深色模式

prefers-color-scheme 媒体特性用于检测用户是否有将系统的主题色设置为亮色或者暗色。使用语法如下所示:

@media (prefers-color-scheme: value) {} 其中 value 有以下 3 种值,其中:

若结果为 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.jpg" media="(prefers-color-scheme: dark)">
  <img src="light.jpg">
</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 来实现安装和离线等功能。

特点:

配置页面参数

在项目根目录添加文件 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.jpg",
      "sizes": "64x64",
      "type": "image/jpg"
    },
    {
      "src": "assets/images/icon-256x256.jpg",
      "sizes": "256x256",
      "type": "image/jpg"
    }
  ]
}

参数说明:

fullscreen:全屏显示,会尽可能将所有的显示区域都占满。

standalone:浏览器相关 UI(如导航栏、工具栏等)将被隐藏,看起来更像一个 Native App

minimal-ui:显示形式与 standalone 类似,浏览器相关 UI 会最小化为一个按钮,不同浏览器在实现上略有不同。browser:一般来说,会和正常使用浏览器打开样式一致。

需要说明的是,当一些系统的浏览器不支持 fullscreen 时将会显示成 standalone 效果,当不支持 standalone 时,将会显示成 minimal-ui 的效果,以此类推。

description:应用描述。

icons:指定了应用的桌面图标和启动页图像,用数组表示:

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" rel="external nofollow" >
<link rel="stylesheet" type="text/css" href="./assets/css/dark.css" rel="external nofollow" >
<link rel="stylesheet" type="text/css" href="./assets/css/petals.css" rel="external nofollow" >
<link rel="shortcut icon" href="./assets/images/icon-256x256.jpg" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >
<link rel="apple-touch-icon" href="./assets/images/icon-256x256.jpg" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" />
<link rel="apple-touch-icon-precomposed" href="./assets/images/icon-256x256.jpg" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >
<link rel="Bookmark" href="./assets/images/icon-256x256.jpg" rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  rel="external nofollow"  />
<link rel="manifest" href="./manifest.webmanifest" rel="external nofollow" >
<title>かなゲーム</title>

注册使用 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.jpg'
  // ...
];

// 监听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 说明:

注:使用 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);

参数:

id: 可选,在 animate() 里可作为唯一标识的属性: 一个用来引用动画的字符串( DOMStringdelay:可选,开始时间的延迟毫秒数,默认值为 0

direction:可选,动画的运动方向。向前运行 normal、向后运行 reverse、每次迭代后切换方向 alternate,向后运行并在每次迭代后切换方向 alternate-reverse。默认为 normal

duration:可选,动画完成每次迭代的毫秒数,默认值为 0easing:可选,动画随时间变化的频率。接受预设的值包括 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.jpg" 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.jpg') 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/full/WNjEPwW

Dark Mode Support in WebKit https://webkit.org/blog/8840/dark-mode-support-in-webkit

PWA技术理论+实战全解析 https://www.cnblogs.com/yangyangxxb/p/9964959.html

H5 PWA技术 https://zhuanlan.zhihu.com/p/144512343

aspect-ratio https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media/aspect-ratio

Service Worker https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API

Element.animate() https://developer.mozilla.org/zh-CN/docs/Web/API/Element/animate

作者:dragonir 博客地址:https://www.cnblogs.com/dragonir/p/15041977.html

到此这篇关于五十音小游戏中的前端知识的文章就介绍到这了,更多相关前端知识小游戏内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-前端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯