chore: up snake demo

This commit is contained in:
xxx
2023-01-03 00:00:05 +08:00
parent b3f8d30991
commit 04daf2f2dc
6 changed files with 616 additions and 0 deletions

Binary file not shown.

414
docs/snake/index.css Normal file
View File

@@ -0,0 +1,414 @@
* {
margin: 0;
padding: 0;
}
@font-face {
font-family: 'Rocher';
src: url('./RocherColorGX.woff2') format('woff2');
}
@font-palette-values --pink {
font-family: Rocher;
base-palette: 1;
}
@font-palette-values --mint {
font-family: Rocher;
base-palette: 7;
}
:root {
--wa-color: #00A39A;
--wa-bg: #2e2e2e;
--wa-bg2: #202020;
--wa-trans: translate(-50%, -50%);
--wa-handle-dir: calc(50% - 30px);
--wa-shadow:
rgb(45 35 66 / 40%) 0 2px 4px,
rgb(45 35 66 / 30%) 0 7px 13px -3px;
}
.tips {
position: absolute;
top: calc(50% - 355px);
left: calc(50% - 392px);
color: var(--wa-bg);
font-family: Rocher;
font-size: 2em;
font-palette: --mint;
}
#box {
background: var(--wa-color);
border-radius: 1em;
box-shadow: 0.2em 0.2em 2em rgba(46, 46, 46, 0.5);
padding: 0.5em;
position: absolute;
top: 50%;
left: 50%;
transform: var(--wa-trans)
}
#game {
background: var(--wa-color);
border-radius: 1em;
display: flex;
flex-direction: column;
position: relative;
}
#game::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow:
10px 10px 30px #006E67,
inset -10px -10px 10px #008880,
inset 10px 10px 10px var(--wa-color);
filter: blur(3px);
}
.game__title {
font-size: 1.2em;
text-align: left;
padding: 0 0.6em;
margin: 0.2em 0;
display: block;
font-family: 'Rocher';
position: relative;
z-index: 1;
}
.game__title sup {
font-size: 0.6em;
color: #fff;
}
#game__screen {
background: var(--wa-bg);
box-shadow: var(--wa-shadow), var(--wa-bg2) 0 -3px 0 inset;
border-radius: 1em;
margin: 0 0.3em 0.3em;
padding: 0.6em;
display: flex;
justify-content: center;
align-items: center;
position: relative;
z-index: 1;
}
#game__screen-content {
width: 320px;
height: 320px;
border-radius: 0.5em;
overflow: hidden;
aspect-ratio: 1;
place-content: center;
background: #8C957B;
}
#game__operate {
flex: 1;
padding: 0.3em;
display: flex;
}
#game__operate-direction,
.game__operate-ghost {
margin: 0.5em;
width: 180px;
height: 180px;
position: relative;
}
#game__operate-direction .direction__item {
align-items: center;
appearance: none;
background: var(--wa-bg);
border-radius: 50%;
border-width: 0;
box-shadow: var(--wa-shadow), var(--wa-bg2) 0 -3px 0 inset;
height: 60px;
width: 60px;
position: absolute;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
white-space: nowrap;
will-change: box-shadow, transform;
}
#game__operate-direction .direction__item::after {
content: '';
position: absolute;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
}
#game__operate-direction .direction__item::before {
content: '';
display: block;
width: 0;
height: 0;
border-style: solid;
border-width: 0 0.5em 0.866em 0.5em;
border-color: transparent transparent #fff transparent;
position: absolute;
top: 50%;
left: 50%;
}
#game__operate-direction--up {
top: 0;
left: var(--wa-handle-dir);
}
#game__operate-direction--up::before {
transform: var(--wa-trans)
}
#game__operate-direction--right {
top: var(--wa-handle-dir);
right: 0;
}
#game__operate-direction--right::before {
transform: var(--wa-trans) rotate(90deg);
}
#game__operate-direction--down {
bottom: 0;
left: var(--wa-handle-dir);
}
#game__operate-direction--down::before {
transform: var(--wa-trans) rotate(180deg);
}
#game__operate-direction--left {
top: var(--wa-handle-dir);
left: 0;
}
#game__operate-direction--left::before {
transform: var(--wa-trans) rotate(270deg);
}
.game__operate-ghost {
border-radius: 50%;
background: rgba(46, 46, 46, 0.8);
box-shadow: var(--wa-shadow), var(--wa-bg2) 0 -3px 0 inset;
}
.game__operate-ghost::before {
content: '';
display: block;
width: 55px;
height: 55px;
background: var(--wa-bg);
position: absolute;
top: 50%;
left: 50%;
transform: var(--wa-trans);
z-index: 1;
}
.game__operate-ghost span {
background: var(--wa-bg);
height: 55px;
width: 55px;
position: absolute;
}
.game__operate-ghost span:nth-child(1) {
top: 10px;
left: 50%;
transform: translateX(-50%);
border-radius: 10px 10px 0 0;
box-shadow: var(--wa-shadow), var(--wa-bg2) -3px 0px 0 inset;
}
.game__operate-ghost span::before {
content: '';
display: block;
width: 0;
height: 0;
border-style: solid;
border-width: 0 10px 16px 10px;
border-color: transparent transparent var(--wa-bg2) transparent;
position: absolute;
top: 50%;
left: 50%;
}
.game__operate-ghost span:nth-child(1)::before {
transform: var(--wa-trans);
}
.game__operate-ghost span:nth-child(2) {
top: 50%;
left: 10px;
transform: translateY(-50%);
border-radius: 10px 0 0 10px;
box-shadow: var(--wa-shadow), var(--wa-bg2) 0px -3px 0 inset;
}
.game__operate-ghost span:nth-child(2)::before {
transform: var(--wa-trans) rotate(270deg);
}
.game__operate-ghost span:nth-child(3) {
top: 50%;
right: 10px;
transform: translateY(-50%);
border-radius: 0 10px 10px 0;
box-shadow: var(--wa-shadow), var(--wa-bg2) -3px 0px 0 inset;
}
.game__operate-ghost span:nth-child(3)::before {
transform: var(--wa-trans) rotate(90deg);
}
.game__operate-ghost span:nth-child(4) {
bottom: 10px;
left: 50%;
transform: translateX(-50%);
border-radius: 0 0 10px 10px;
box-shadow: var(--wa-shadow), var(--wa-bg2) -3px -3px 0 inset;
}
.game__operate-ghost span:nth-child(4)::before {
transform: var(--wa-trans) rotate(180deg);
}
.game__operate-assist {
flex: 1;
display: flex;
position: relative;
}
.game__operate-assist--btns {
position: absolute;
top: 0;
width: 8em;
height: 2em;
}
.game__operate-assist--btns span:nth-child(1)::before,
.game__operate-assist--btns span:nth-child(1)::after {
content: "";
position: absolute;
top: 0;
width: 3em;
height: 2em;
align-items: center;
appearance: none;
background: var(--wa-bg);
border-width: 0;
box-shadow: var(--wa-shadow), var(--wa-bg2) 0 -3px 0 inset;;
box-sizing: border-box;
height: 1em;
width: 3em;
border-radius: 0.5em;
}
.game__operate-assist--btns span:nth-child(1)::before {
left: 0.6em;
}
.game__operate-assist--btns span:nth-child(1)::after {
left: 4.6em;
}
.game__operate-assist--btns span:nth-child(2)::before,
.game__operate-assist--btns span:nth-child(2)::after {
position: absolute;
width: 3em;
height: 2em;
color: var(--wa-bg2);
font-size: 0.6em;
font-family: 'Rocher';
font-palette: --pink;
}
.game__operate-assist--btns span:nth-child(2)::before {
content: "SELECT";
top: 2em;
left: 1.4em;
}
.game__operate-assist--btns span:nth-child(2)::after {
content: "START";
top: 2em;
left: 8.4em;
}
.game__operate-assist--audio {
position: absolute;
bottom: 0;
width: 5em;
height: 3em;
margin: 0 0 0.6em 0.6em;
background: red;
}
.game__operate-assist--audio::before {
--s: 0.5em;
--_g: conic-gradient(var(--wa-color) 25%,#0000 0) 0 0;
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
var(--_g)/calc(2*var(--s)) calc(var(--s)/9.5),
var(--_g)/calc(var(--s)/9.5) calc(2*var(--s)),
repeating-conic-gradient(#0000 0 25%,var(--wa-color) 0 50%) var(--s) 0 /calc(2*var(--s)) calc(2*var(--s)),
radial-gradient(50% 50%,var(--wa-bg) 98%,var(--wa-color)) 0 0/var(--s) var(--s);
}
@media screen and (min-width: 768px) {
#box {
width: 768px;
max-width: 768px;
}
.game__title {
margin: 0.8em 0.2em;
}
.game__operate-assist {
flex-direction: column;
justify-content: space-between;
align-items: center;
}
}
@media screen and (max-width: 768px) {
.tips {
display: none;
}
#box {
width: 100%;
max-width: fit-content;
}
.game__operate-ghost {
display: none;
}
#game__operate-direction .direction__item:active {
box-shadow: var(--wa-bg2) 0 3px 7px inset;
transform: translateY(2px);
}
}

42
docs/snake/index.html Normal file
View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>Wa: Snack Game</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<span class="tips">Please use the keyboard to control</span>
<div id="box">
<span class="game__title">
Wa Snack Game
<sup><em>2022</em></sup>
</span>
<div id="game">
<div id="game__screen">
<div id="game__screen-content"></div>
</div>
<div id="game__operate">
<div class="game__operate-ghost"><span></span><span></span><span></span><span></span></div>
<div class="game__operate-assist">
<span class="game__operate-assist--btns"><span></span><span></span></span>
<span class="game__operate-assist--audio"></span>
</div>
<div id="game__operate-direction">
<span id="game__operate-direction--up" class="direction__item"></span>
<span id="game__operate-direction--right" class="direction__item"></span>
<span id="game__operate-direction--down" class="direction__item"></span>
<span id="game__operate-direction--left" class="direction__item"></span>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript" src="./index.js"></script>
<script type="text/javascript" src="./wa_app.js"></script>
</html>

23
docs/snake/index.js Normal file
View File

@@ -0,0 +1,23 @@
(() => {
document.documentElement.addEventListener('touchstart', (event) => {
if (event.touches.length > 1) { event.preventDefault(); }
}, { passive: false });
let lastTouchEnd = 0;
document.documentElement.addEventListener('touchend', (event) => {
let now = Date.now();
if (now - lastTouchEnd <= 500) { event.preventDefault() }
lastTouchEnd = now;
}, { passive: false });
})()
const IS_MOBILE = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
const DIR_ID_PREFIX = 'game__operate-direction--';
const MOBILE_DIR_MAP = [
{ id: `${DIR_ID_PREFIX}up`, keyCode: 38 },
{ id: `${DIR_ID_PREFIX}down`, keyCode: 40 },
{ id: `${DIR_ID_PREFIX}left`, keyCode: 37 },
{ id: `${DIR_ID_PREFIX}right`, keyCode: 39 },
];

BIN
docs/snake/snake.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

137
docs/snake/wa_app.js Normal file
View File

@@ -0,0 +1,137 @@
(() => {
class WaApp {
constructor() {
this._inst = null;
this._wa_print_buf = "";
}
init(url) {
let app = this;
let importsObject = {
wa_js_env: new function () {
this.waPrintI32 = (i) => {
app._wa_print_buf += i
}
this.waPrintRune = (c) => {
let ch = String.fromCodePoint(c);
if (ch == '\n') {
console.log(app._wa_print_buf);
app._wa_print_buf = "";
}
else {
app._wa_print_buf += ch
}
}
this.waPuts = (prt, len) => {
let s = app.getString(prt, len);
app._wa_print_buf += s
}
this.rand = (m) => {
return parseInt(Math.random() * m)
}
this.newCanvas = (w, h) => {
let canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
canvas.id = 0; //!!!!!
const waContent = document.getElementById('game__screen-content');
waContent.appendChild(canvas);
function getPointOnCanvas(x, y) {
var bbox = canvas.getBoundingClientRect();
return {
x: parseInt((x - bbox.left) * (canvas.width / bbox.width)),
y: parseInt((y - bbox.top) * (canvas.height / bbox.height))
};
}
function onMouseDown(ev) {
let pt = getPointOnCanvas(ev.clientX, ev.clientY);
app._inst.exports['snake$canvas.OnMouseDown'](canvas.id, pt.x, pt.y);
}
function onMouseUp(ev) {
let pt = getPointOnCanvas(ev.clientX, ev.clientY);
app._inst.exports['snake$canvas.OnMouseUp'](canvas.id, pt.x, pt.y);
}
function onKeyDown(ev) {
app._inst.exports['snake$canvas.OnKeyDown'](canvas.id, ev.keyCode);
}
function onKeyUp(ev) {
app._inst.exports['snake$canvas.OnKeyUp'](canvas.id, ev.keyCode);
}
if (IS_MOBILE) {
MOBILE_DIR_MAP.forEach((dir) => {
const el = document.getElementById(dir.id);
el.addEventListener('touchstart', (ev) => onKeyDown({ keyCode: dir.keyCode }));
el.addEventListener('touchend', (ev) => onKeyUp({ keyCode: dir.keyCode }));
});
}
canvas.addEventListener('mousedown', onMouseDown, true);
canvas.addEventListener('mouseup', onMouseUp, true);
canvas.addEventListener('keydown', onKeyDown, true);
canvas.addEventListener('keyup', onKeyUp, true);
canvas.tabIndex = -1; //tabindex
canvas.focus();
this._ctx = canvas.getContext('2d');
this._canvas = canvas;
return canvas.id;
}
this.updateCanvas = (id, block, data) => {
let img = this._ctx.createImageData(this._canvas.width, this._canvas.height);
let buf_len = this._canvas.width * this._canvas.height * 4
let buf = app.memUint8Array(data, buf_len);
for (var i = 0; i < buf_len; i++) {
img.data[i] = buf[i];
}
this._ctx.putImageData(img, 0, 0);
}
}
}
WebAssembly.instantiateStreaming(fetch(url), importsObject).then(res => {
this._inst = res.instance;
this._inst.exports._start();
})
}
mem() {
return this._inst.exports.memory;
}
memView(addr, len) {
return new DataView(this._inst.exports.memory.buffer, addr, len);
}
memUint8Array(addr, len) {
return new Uint8Array(this.mem().buffer, addr, len)
}
getString(addr, len) {
return new TextDecoder("utf-8").decode(this.memView(addr, len));
}
setString(addr, len, s) {
const bytes = new TextEncoder("utf-8").encode(s);
if (len > bytes.length) { len = bytes.length; }
this.MemUint8Array(addr, len).set(bytes);
}
}
function gameLoop() {
window['waApp']._inst.exports['snake.Step']();
}
window['waApp'] = new WaApp();
window['waApp'].init("./snake.wasm")
const timer = setInterval(gameLoop, IS_MOBILE ? 150 : 100);
})()