By adamlubek​
This recipe shows usage of scan operator for state management in simple game
​​​​
( StackBlitz )
// RxJS v6+import { fromEvent, interval } from 'rxjs';import { tap, scan, map, switchMap, takeWhile } from 'rxjs/operators';import {dot,updatedDot,setTimerText,resetDotSize,moveDot} from './dom-updater';​interface State {score: number;intrvl: number;}const makeInterval = (val: State) =>interval(val.intrvl).pipe(map(v => 5 - v),tap(setTimerText));const gameState: State = { score: 0, intrvl: 500 };const nextState = (acc: State) => ({score: (acc.score += 1),intrvl: acc.score % 3 === 0 ? (acc.intrvl -= 50) : acc.intrvl});const isNotGameOver = intervalValue => intervalValue >= 0;​const game$ = fromEvent(dot, 'mouseover').pipe(tap(moveDot),scan < Event,State > (nextState, gameState),tap(state => updatedDot(state.score)),switchMap(makeInterval),tap(resetDotSize),takeWhile(isNotGameOver));​game$.subscribe(n => {},e => {},() => setTimerText('ouch!'));
const random = () => Math.random() * 300;const elem = id => document.getElementById(id);const setElementText = (elem, text) => (elem.innerText = text.toString());const timer = elem('timer');const setDotSize = size => {dot.style.height = `${size}px`;dot.style.width = `${size}px`;};​export const dot = elem('dot');export const updatedDot = score => {if (score % 3 === 0) {dot.style.backgroundColor ='#' + ((Math.random() * 0xffffff) << 0).toString(16);}setElementText(dot, score);};export const setTimerText = text => setElementText(timer, text);export const moveDot = () => {setDotSize(5);dot.style.transform = `translate(${random()}px, ${random()}px)`;};export const resetDotSize = () => setDotSize(30);
<style>#dot {margin-top: 10px;height: 30px;width: 30px;background-color: lightgray;border-radius: 50%;transition: all 0.6s ease-in-out;text-align: center;color: white;}​#timer {position: absolute;top: 150px;left: 150px;opacity: 0.1;font-size: 60px;}</style>​<div id="timer"></div><div id="dot"></div>