background
In the early stage of Japanese learning, I found that the memory of Japanese kana syllabary is not very easy, and the memory of katakana is particularly troublesome. At this time, I thought that if there is an application that can make full use of the fragmented time, it would be great to be able to practice kana syllabary at any time during lunch break or on the subway. So I searched for App Store
learning, but the software in the store does not contain in-app purchases, entrained advertisements, or frequently 40M
, and I did not find a satisfactory application. So I plan to write one myself, mainly introducing some of my gains in the process of developing and designing this application.
achieve
Online experience address: https://dragonir.github.io/kanaApp/
The implementation effect is as follows, the application is mainly divided into three pages:
- Home page: Includes menu options (hiragana practice, katakana practice, mixed practice), dark mode switch button.
- Answer page: including remaining opportunities and score display area, middle question area, bottom answer button.
- Result page: result score display and back to home button.
The answer logic rule is 4
. The application will give feedback and score the error according to the click. After the error 10
times, the game ends and the result page is loaded. The implementation of game logic is not the main content of this article, so I won't repeat it later. The main content of this article is the introduction of the front-end knowledge involved in the development process of this mini game.
Dark mode ⚪⚫
As Windows 10
, MacOs
, Android
and other systems have successively introduced dark mode, browsers have also begun to support the detection of system theme color configuration, and more and more web applications are equipped with dark mode switching functions. In order to optimize 50-tone game, I also configured a dark style, and the effect is as follows:
CSS
Media query judgment dark mode
prefers-color-scheme
media feature is used to detect whether the user has set the theme color of the system to light or dark. The usage syntax is as follows:
@media (prefers-color-scheme: value) {}
where value
has the following 3
values, of which:
light
: Indicates that the user system supports dark mode and has been set as a light theme (default value).dark
: Indicates that the user system supports dark mode and has been set to dark theme.no-preference
: Indicates that the user system does not support dark mode or cannot know whether it is set to dark mode (obsolete).
If the result isno-preference
, it is impossible to know whether the host system supports the theme color setting through this media feature, or whether the user actively set it to no preference. For privacy protection and other considerations, the user or user agent may also set it tono-preference
inside the browser in some cases.
In the following example, when the system relating to a dark color .demo
background color elements is #FFFFFF
; theme color when the system is light, .demo
background color elements is #000000
.
@media (prefers-color-scheme: dark) {
.demo { background: #FFFFFF; }
}
@media (prefers-color-scheme: light) {
.demo { background: #000000; }
}
JavaScript
judge dark mode
window.matchMedia()
method returns a new MediaQueryList
object, which represents the parsed result of the (en-US). The returned
MediaQueryList
can be used to determine Document
matches the media query, or monitor a document
to determine whether it matches or stop matching the media query. The MediaQueryList
object has attributes matches
and media
, and methods addListener
and removeListener
.
Using matchMedia
as the judgment medium, you can also identify whether the system supports theme colors:
if (window.matchMedia('(prefers-color-scheme)').media === 'not all') {
// 浏览器不支持主题色设置
}
if (window.matchMedia('(prefers-color-scheme: dark)').matches){
// 深色模式
} else {
// 浅色模式
}
In addition, you can dynamically monitor the dark mode status of the system, and respond in real time according to the system dark mode switching:
window.matchMedia('(prefers-color-scheme: dark)').addListener(e => {
if (e.matches) {
// 开启深色模式
} else {
// 关闭深色模式
}
});
Or detect the dark or light mode separately:
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);
In a 50-tone game, use JavaScript
detect whether the dark mode is enabled, and dynamically add the css
class name to automatically load the dark mode. It also provides a dark and light color switch button to manually switch themes.
Determine the dark mode in the HTML
When using picture elements on the page, you can directly judge whether the system has enabled the dark mode HTML
Such as:
<picture>
<source srcset="dark.png" media="(prefers-color-scheme: dark)">
<img src="light.png">
</picture>
picture
element allows us to display different pictures on different devices, generally used for responsiveness. HTML5
introduces the <picture>
element, which can make the adjustment of image resources more flexible. <picture>
element has zero or more <source>
elements and one <img>
element. Each <source>
element matches a different device and references a different image source. If there is no match, select <img>
in the src
attribute of the url
.
Note: The<img>
element is placed<picture>
element. If the browser does not support this attribute, the picture of the<img>
Offline cache
In order to be able to generate shortcuts on the desktop for quick access like native applications, and use them offline anytime, anywhere, the 50-tone game uses offline caching technology, which is a
PWA application. The following is a brief description
PWA offline application implementation technology.
PWA (progressing web app)
, a progressive web application, is the next-generation WEB application model. A
PWA
application is first a web page, and with the help ofApp Manifest
andService Worker
to achieve installation and offline functions.
Features:
- Progressive: Suitable for all users who choose any browser, because it is developed with progressive enhancement as its core purpose.
- Adaptive: Suitable for any model: desktop, mobile, tablet or any future device.
- Connection independence: It can work in offline or low-quality network conditions with the help of service worker threads.
- Offline push: Using push message notifications, we can make our apps like
Native App
and improve user experience. - Timely update: Keep up-to-date at all times under the action of the service worker thread update process.
- Security:
HTTPS
by 060f83d87342a2 to prevent snooping and ensure that the content is not tampered with.
Configure page parameters
manifest.webmanifest
or manifest.json
in the project root directory, and write the following configuration information in the file. In this example, 50-tone game is configured as follows:
// 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"
}
]
}
parameter description :
name
:Web App
is also the name of the application icon when it is saved on the desktop.short_name
: Whenname
too long,short_name
will be used instead ofname
, which is the abbreviationWeb App
start_url
Web App
is loaded when the user opens theURL
.URL
will be relative to the path where themanifest
display
: Specifies the display mode of the application, it has four values to choose from:fullscreen
: Full screen display, as much as possible to occupy all the display area.standalone
: Browser-relatedUI
(such as navigation bar, toolbar, etc.) will be hidden, which looks more like aNative App
.minimal-ui
: The display format isstandalone
, the browser-relatedUI
will be minimized to a button, and different browsers have slightly different implementations.browser
: Generally speaking, it will be the same as the normal browser opening style.- It should be noted that when some system browsers do not support
fullscreen
, it will display thestandalone
, when it does not supportstandalone
, it will displayminimal-ui
, and so on.
description
: Application description.icons
: Specifies the desktop icon and startup page image of the application, represented by an array:- sizes: Icon size. By specifying the size, the system will select the most suitable icon to display in the corresponding position.
- src: icon path. The relative path is relative to the
manifest
file, and an absolute path can also be used. - type: Icon picture type. The browser will select the picture closest to
128dp(px = dp * (dpi / 160))
icons
as the splash screen image.
background_color
: Specify the background color of the splash screen. Using the same color can achieve a smooth transition from the splash screen to the home page, and can also be used to improve the user experience when the page resources are loading.theme_color
: The theme colorWeb App
This property can be used to control the color of theUI
For example, the color of the status bar, the status bar in the content page, and the address bar.
Configuration information automatic generation tool: https://tomitm.github.io/appmanifest/
Configure HTML
file
In index.html
introduced in manifest
profile and head
add the following configuration information to compatible iOS system
<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
: Designated application icon, similar to theicons
manifest.json
file, and also supportssizes
attributes for different scenarios.apple-mobile-web-app-capable
: similarmanifest.json
indisplay
function by settingyes
enterstandalone
mode.apple-mobile-web-app-title
: Specify the name of the application.apple-mobile-web-app-status-bar-style
: Specify thestatus bar style of the
Default
,Black
,Black-translucent
can be set.
Register to use Service Worker
index.html
for server-worker registration:
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`);
}
}
}
Use serviceWorkerContainer.register()
to Service worker
, and add try...catch...
fault-tolerant judgment to ensure normal operation Service worker
Also note that only https
under, navigator
there will have serviceWorker
object.
Service workers
essentially acts asWeb
application, the browser, and the network (when available). Designed to create an effective offline experience, it will intercept network requests and take appropriate actions based on whether the network is available, and update resources from the server. It also provides entry to push notifications and access background synchronizationAPI
. To learn more aboutService workder
🔗
at the end of the article.
sw.js
root directory to define the cache information and method
// 定义缓存的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;
}
}
In sw.js
standard employed in web worker
programming, since run in another global context (self)
, the global context is different from window
, so the use of self.addEventListener()
.
Cache API
is Service Worker
provides an interface for the operation of the cache, based on these interfaces Promise
implemented, including Cache
and Cache Storage
, Cache
and dealing directly request, the cached Request / Response
objects provide storage mechanism, CacheStorage
represents Cache
stored instance of an object, we can directly Use the global caches
attribute to access Cache API
.
** Cache
related API
description:
Cache.match(request, options)
: Returns aPromise
objects,resolve
results is withCache
first request object is already cached match.Cache.matchAll(request, options)
: Returns aPromise
objects,resolve
results is withCache
array of all the objects that match the request composed.Cache.addAll(requests)
: Receive aURL
array, retrieve and add the returnedresponse
object to the givenCache
object.Cache.delete(request, options)
: Search for theCache
entrykey
value isrequest
If found, delete theCache
entry, and return aresolve
true
isPromise
; if it is not found, return aresolve
false
isPromise
.Cache.keys(request, options)
: Returns aPromise
objects,resolve
results isCache
objectskey
array value thereof.
Note:request.clone()
andresponse.clone()
are used becauserequest
andresponse
are one stream and can only be consumed once. The cache has been consumed once, and theHTTP
request will be consumed again. At this time, use theclone
method to clone the request.
At this point, when the installed Service Worker
page is opened, the Service Worker
script update will be triggered. When the timestamp written in the Service Worker
database of the last script update and this update exceed 24 hours
Service Worker
script update will be triggered. When the sw.js
file changes, it will trigger the Service Worker
script update. The update process is similar to the installation, except that it will not enter the active
state immediately after the update installation is successful. The updated Service Worker
will coexist with the original Service Worker
and run its install
. Once the new Service Worker
installed successfully, it will enter the wait
state. Wait for the old version of Service Worker
enter/thread termination.
For moreServer Worker
please view the link at the end of the article🔗
achieves the effect :
PC terminal 🖥️ : Windows
, after opening the application for the first time in the browser, there will be an installation prompt, click the installation icon to install, the desktop and start menu will generate application shortcuts, click the shortcut to open the application.
Mac 💻
: The above chromiumn kernel browser (
chrome
, opera
, new version edge
) is similar. After installation, a shortcut launchpad
mobile terminal 📱 : iPhone
. Choose to save to the desktop in the browser to generate a desktop icon, click the icon to open the offline application.
Cherry Blossom 🌸
In order to enhance the visual effect and interest, the effect of falling 🌸
The falling effect animation mainly uses the Element.animate()
method.
Element
of animate()
method is a newly created Animation
convenient method of applying it to the elements, and then run the animation. It will return a newly created Animation
object instance. Multiple animation effects can be applied to an element. You can get a list of these animation effects by calling this function: Element.getAnimations()
.
basic grammar :
var animation = element.animate(keyframes, options);
parameter :
keyframes
: key frame. An object represents a collection of key frames.options
: An optional integer representing the duration of the animation (in milliseconds), or an object containing one or more time attributes:id
: Optional, can be used as a uniquely identified attributeanimate()
DOMString
)delay
: Optional, the delay in milliseconds for the start time, the default value is0
.direction
: Optional, the direction of motion of the animation. Moves forwardnormal
, run backreverse
, after each iteration switching directionalternate
, running backwards and the switching direction after each iterationalternate-reverse
. The default isnormal
.duration
: Optional, the number of milliseconds to complete each iteration of the animation, the default value is0
.easing
: Optional, the frequency of animation changes over time. Accepted preset values includelinear
,ease
,ease-in
,ease-out
,ease-in-out
and a custom valuecubic-bezier
, such ascubic-bezier(0.42, 0, 0.58, 1)
. The default value islinear
.endDelay
: Optional, a delay after the end of the animation, the default value is0
.fill
: Optional. Define the timing of the influence of the animation effect on the element.backwards
affects the element before the animation starts,forwards
affects the element after the animation is completed, andboth
both. The default value isnone
.iterationStart
: Optional, describe at which point in the iteration the animation should start. For example,0.5
means that it starts midway through the first iteration, and after setting this value, an2
iterations will end midway through the third iteration. The default is0.0
.iterations
: Optional, the number of times the animation should be repeated. The default is1
, or you can takeinfinity
to make it repeat when the element exists.
The following code embodied in the present embodiment, HTML
which has several .petal
elements, then JavaScript
acquired all .petal
element to random animation, css addition of both rotation and deformation of the two kinds of animation, to achieve the effect of falling cherry blossom petals.
<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;
}
}
The complete code can be viewed at the following link 🔗
.
CSS
Determine the horizontal screen of the mobile phone
In this example, the 50-tone mini game application is developed for the mobile terminal and has not been adapted to the style of the PC terminal, so a horizontal screen guide page can be added to prompt the user to use the vertical screen. In
CSS
to determine whether the mobile device is in landscape mode, aspect-ratio
needs to be used for media query, which is determined by testing the aspect ratio of viewport
aspect-ratio
aspect ratio attribute is specified as the <ratio>
value to represent the viewport
aspect ratio. It is a range, you can use min-aspect-ratio
and max-aspect-ratio
to query the minimum and maximum values respectively. The basic syntax is as follows:
/* 最小宽高比 */
@media (min-aspect-ratio: 8/5) {
// ...
}
/* 最大宽高比 */
@media (max-aspect-ratio: 3/2) {
// ...
}
/* 明确的宽高比, 放在最下部防止同时满足条件时的覆盖 */
@media (aspect-ratio: 1/1) {
// ...
}
The specific implementation in the application is to add a .mod_orient_layer
guide layer and hide it, and display it when the minimum aspect ratio is reached:
<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;
}
}
Realize the effect:
compatibility
The following is the compatibility view of several attributes involved in this article. In actual production projects, you need to pay attention to compatibility and adaptation.
Photoshop skills
logo design
logo
mainly composed of two elements. It is composed of a ⛩️
icon and the Japanese hiragana あ
, both of which are classic Japanese elements. At the same time, あ
is stretched and gradual to form ⛩️
, which makes letters and graphics cleverly connected to make the picture harmonious. The logo background color uses the application theme background color to establish a connection with the page invisibly, forming a full-link unified style standard. (Can't edit it anymore...😂
⛩
original model ofdribbble
comes from 060f83d8735aa7: 160f83d8735aac https://dribbble.com
External links and reference materials
- Sakura scattered animation full version https://codepen.io/dragonir/full/WNjEPwW
- Dark Mode Support in WebKit https://webkit.org/blog/8840/dark-mode-support-in-webkit
- PWA technical theory + actual combat full analysis https://www.cnblogs.com/yangyangxxb/p/9964959.html
- H5 PWA technology 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
Blog address: 160f83d8735c64 https://segmentfault.com/a/1190000040383870
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。