Snake 0.9: crown control via scroll-delta turns (firmware-derived)

This commit is contained in:
2026-09-13 22:33:54 +01:00
parent 18a6ef7d72
commit aaa292ac49
5 changed files with 116 additions and 10 deletions
+2 -2
View File
@@ -2,8 +2,8 @@
"package": "com.ilias.snake",
"name": "SNAKE",
"icon": "/common/icon.png",
"versionName": "0.8",
"versionCode": 8,
"versionName": "0.9",
"versionCode": 9,
"minAPILevel": 1,
"deviceTypeList": [
"watch"
+45 -3
View File
@@ -1,6 +1,7 @@
<template>
<div class="page" onswipe="onSwipe">
<stack class="dial">
<div class="page">
<scroll id="scroller" class="page" scroll-y="true" direction="vertical" onscroll="onCrownScroll">
<stack class="dial">
<image class="board" src="/common/tex/board.png" onclick="onTap"></image>
<image class="food" src="/common/tex/apple.png" show="{{ hasFood }}" onclick="onTap" style="left: {{ food.left }}; top: {{ food.top }};"></image>
<image class="seg" src="{{ seg.src }}" for="{{ seg in snake }}" onclick="onTap" style="left: {{ seg.left }}; top: {{ seg.top }};"></image>
@@ -32,7 +33,8 @@
<text class="modeButton" onclick="selectMode('wrap')">WRAP</text>
</div>
</div>
</stack>
</stack>
</scroll>
</div>
</template>
<script>
@@ -43,6 +45,17 @@ import best from '../../common/best.js'
const CELL = snake.ARENA.cell
const GRID = snake.ARENA
const OFFSET = snake.ARENA.offset
// Crown rotation arrives as scroll deltas on the focused scroll container
// (see docs/CROWN.md). This many px of accumulated scroll = one 90° turn.
const CROWN_STEP_PX = 40
const CW_ORDER = [{ x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 0, y: -1 }]
function rotate90(d, clockwise) {
let i = 0
for (let k = 0; k < 4; k++) {
if (CW_ORDER[k].x === d.x && CW_ORDER[k].y === d.y) { i = k; break }
}
return clockwise ? CW_ORDER[(i + 1) % 4] : CW_ORDER[(i + 3) % 4]
}
export default {
private: {
@@ -157,6 +170,31 @@ export default {
if (snake.turn(last, v) === last) return
this.queue.push(v)
},
// Crown rotation: firmware routes it into this container's scroll offset
// (docs/CROWN.md). Accumulate deltas, one 90° turn per CROWN_STEP_PX, then
// re-center so the board never visibly moves.
onCrownScroll(e) {
if (this.destroyed) return
const top = e && typeof e.scrollTop === 'number' ? e.scrollTop : 0
const center = 466
const d = top - (this.crownAnchor == null ? center : this.crownAnchor)
this.crownAnchor = top
if (d === 0) return
// keep the dial pinned: snap back to center immediately
try { this.$element('scroller').scrollTo({ top: center }) } catch (err) {}
this.crownAnchor = center
if (this.over || this.paused || !this.visible) return
this.crownAcc = (this.crownAcc || 0) + d
while (Math.abs(this.crownAcc) >= CROWN_STEP_PX) {
const clockwise = this.crownAcc > 0
this.crownAcc += clockwise ? -CROWN_STEP_PX : CROWN_STEP_PX
const base = this.queue.length ? this.queue[this.queue.length - 1] : this.dir
if (this.queue.length >= 2) return
const v = rotate90(base, clockwise)
if (v.x === base.x && v.y === base.y) return
this.queue.push(v)
}
},
onTap() {
if (this.destroyed || !this.visible || this.over) return
this.paused = !this.paused
@@ -190,6 +228,10 @@ export default {
background-color: #000000;
align-items: center;
justify-content: center;
/* scrollable strip: dial centered in a 1398px-tall content so the crown
always has travel to scroll against; page re-centers after each event */
padding-top: 466px;
padding-bottom: 466px;
}
.dial {
width: 466px; height: 466px;