mirror of
https://gitee.com/wa-lang/wa.git
synced 2025-12-06 09:18:53 +08:00
chore: up snake demo
This commit is contained in:
BIN
docs/snake/RocherColorGX.woff2
Normal file
BIN
docs/snake/RocherColorGX.woff2
Normal file
Binary file not shown.
414
docs/snake/index.css
Normal file
414
docs/snake/index.css
Normal 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
42
docs/snake/index.html
Normal 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
23
docs/snake/index.js
Normal 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
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
137
docs/snake/wa_app.js
Normal 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);
|
||||
})()
|
||||
Reference in New Issue
Block a user