-

   rss_rss_hh_full

 - e-mail

 

 -

 LiveInternet.ru:
: 17.03.2011
:
:
: 1

:


[]

, 28 2017 . 14:24 +
Poccomaxa_zt 14:24

  • Tutorial


. - , Angular React, , JavaScript. . , ( API), ( ) .

, , , ?

, , :



, , . , ! , , , : , . Observables () , . - Observable, , , .

. HTML5 JavaScript RxJS, , .

Github, -. , . , Twitter.




    • (direction$)

    • BehaviorSubject
    • (score$)
  • (snake$)






, , 1970- . . .

, . , , , . . , , . ! , . , . ?

, :



, . , ? , . , , , . , , , .



, canvas (), API- JavaScript. , , , , . , canvas.

, egghead Keith Peters.

index.html , JavaScript.



  
  


  




(script), (body), . , body canvas. , JavaScript. , , , .

export const COLS = 30;
export const ROWS = 30;
export const GAP_SIZE = 1;
export const CELL_SIZE = 10;
export const CANVAS_WIDTH = COLS * (CELL_SIZE + GAP_SIZE);
export const CANVAS_HEIGHT = ROWS * (CELL_SIZE + GAP_SIZE);

export function createCanvasElement() {
  const canvas = document.createElement('canvas');
  canvas.width = CANVAS_WIDTH;
  canvas.height = CANVAS_HEIGHT;
  return canvas;
}


, canvas body :

let canvas = createCanvasElement();
let ctx = canvas.getContext('2d');
document.body.appendChild(canvas);


, CanvasRenderingContext2D, getContext ('2d') canvas. 2D- , , , , , .

! .



, :

  • ( )
  • ( )


, . , , , , , , . , , . . , , , , , .

, .

, . . , keydown$, , .

. , . , , , , 1. snakeLength$.

, , , , , . . . .

. , .

. : , , . ? , , , , , , 5 200 ms. , , , ticks$. .

, : . . . , , , . , . , .

, , . , :

  • keydown$: (KeyboardEvent)
  • snakeLength$: (Number)
  • ticks$: , (Number)


, , , , .

, .



. , . , , (observable) . fromEvent():

let keydown$ = Observable.fromEvent(document, 'keydown');


, KeyboardEvent , . , keydown . , , , . , :

export interface Point2D {
  x: number;
  y: number;
}

export interface Directions {
  [key: number]: Point2D;
}

export const DIRECTIONS: Directions = {
  37: { x: -1, y: 0 }, // Left Arrow
  39: { x: 1, y: 0 },  // Right Arrow
  38: { x: 0, y: -1 }, // Up Arrow
  40: { x: 0, y: 1 }   // Down Arrow
};


KeyboardEvent, , keyCode. , .

Point2D, x y . 1, -1 0, , . .

(direction$)

, keydown, , , , KeyboardEvent, . map() .

let direction$ = keydown$
  .map((event: KeyboardEvent) => DIRECTIONS[event.keyCode])


, , , , , . , , . keyCode, , undefined. , , filter(), .

let direction$ = keydown$
  .map((event: KeyboardEvent) => DIRECTIONS[event.keyCode])
  .filter(direction => !!direction)


, . , . , - . ?

, , , . . , , , ?

. , , . , next () :

export function nextDirection(previous, next) {
  let isOpposite = (previous: Point2D, next: Point2D) => {
    return next.x === previous.x * -1 || next.y === previous.y * -1;
  };

  if (isOpposite(previous, next)) {
    return previous;
  }

  return next;
}


, Observable () , - . ! , ?

, Observables. RxJS , scan().

scan() Array.reduce(), , , . scan() . , , .

direction$ :

let direction$ = keydown$
  .map((event: KeyboardEvent) => DIRECTIONS[event.keyCode])
  .filter(direction => !!direction)
  .scan(nextDirection)
  .startWith(INITIAL_DIRECTION)
  .distinctUntilChanged();


, startWith(), , Observable (keydown$). Observable , .

, , . , . , distinctUntilChanged(). . , distinctUntilChanged() , .

direction$ , . , , , , Observable, , , .





, . ? , . , , , , , . , . , , .

. snake$, , , , . snake$, . , ticks$, . , snake$ , ticks$, x . , , snake$ - . , , .

, . , apples$ snake$. , , , , , - . apples$ , , .

BehaviorSubject

, , BehaviorSubject. RxJS Subjects () . , Subject Subjects. , Subject , Observer () Observable (). Observables , Observers Observables .

BehaviorSubject Subject, , . , Observer BehaviorSubject, , . , , Observers .

BehaviorSubject SNAKE_LENGTH:

// SNAKE_LENGTH specifies the initial length of our snake
let length$ = new BehaviorSubject(SNAKE_LENGTH);


snakeLength$:

let snakeLength$ = length$
  .scan((step, snakeLength) => snakeLength + step)
  .share();


, snakeLength$ length$, BehaviorSubject. , , Subject, next(), snakeLength$. , scan() . , , share(), ?

, snakeLength$ snake$, . Observable. , length$ cold Observable ( ).

hot and cold Observables ( ), Cold vs Hot Observables.

, share(), Observable, . Subject . , Subject Observable . Subject, , Observable. () .

! , , , score$.

(score$)

, . snakeLength$, score$, scan():

let score$ = snakeLength$
  .startWith(0)
  .scan((score, _) => score + POINTS_PER_APPLE);


snakeLength$ , , length$, , , , POINTS_PER_APPLE, . , startWith(0) scan(), .

, :



, , BehaviorSubject snakeLength$ score$. , share() , , , .

. , . ?

(snake$)

snake$. , - , . , interval(x), x . .

let ticks$ = Observable.interval(SPEED);


snake$ . , , , , . scan() . , , , , . direction$ snakeLength$?

. , snake$, . , .

, RxJS withLatestFrom(). , , , . , , . , withLatestFrom() .

, snake$:

let snake$ = ticks$
  .withLatestFrom(direction$, snakeLength$, (_, direction, snakeLength) => [direction, snakeLength])
  .scan(move, generateSnake())
  .share();


ticks$, , , direction$, snakeLength$. , , , , .

, ( ) withLatestFrom, , . , , .

move(), . , GitHub.

, :



direction$? , withLatestFrom() , , Observable (), .



, , . , .

, direction$, snakeLength$, score$ snake$. , . , . .

, . -, , . , , . . ?

, scan() . , , , , . , . distinctUntilChanged() .

let apples$ = snake$
  .scan(eat, generateApples())
  .distinctUntilChanged()
  .share();


! , , apples$ , , . , , snake$, snakeLength$, , .



, ? . eat():

export function eat(apples: Array, snake) {
  let head = snake[0];

  for (let i = 0; i < apples.length; i++) {
    if (checkCollision(apples[i], head)) {
      apples.splice(i, 1);
      // length$.next(POINTS_PER_APPLE);
      return [...apples, getRandomPosition(snake)];
    }
  }

  return apples;
}


length$.next(POINTS_PER_APPLE) . , ( ES2015). ES2015 , . , , .

, applesEaten$. apples$ , , - , length$.next(). do(), .

. - () , apples$. , , . , RxJS , skip().

, applesEaten$ , . .

let appleEaten$ = apples$
  .skip(1)
  .do(() => length$.next(POINTS_PER_APPLE))
  .subscribe();




, , , scene$. combineLatest. withLatestFrom, . -, :

let scene$ = Observable.combineLatest(snake$, apples$, score$, (snake, apples, score) => ({ snake, apples, score }));


, , , Observables () . , , . , .





, - . , 60 .

, , ticks$, . :

// Interval expects the period to be in milliseconds which is why we devide FPS by 1000
Observable.interval(1000 / FPS)


, JavaScript . , . , . , , . , . .

, requestAnimationFrame, . Observable? , , interval(), Scheduler (). , Scheduler , - .

RxJS , , , animationFrame. window.requestAnimationFrame.

! , Observable game$:

// Note the last parameter
const game$ = Observable.interval(1000 / FPS, animationFrame)


16 , 60 FPS.



game$ scene$. , ? , , , 60 . game$ , , , scene$. ? , withLatestFrom.

// Note the last parameter
const game$ = Observable.interval(1000 / FPS, animationFrame)
  .withLatestFrom(scene$, (_, scene) => scene)
  .takeWhile(scene => !isGameOver(scene))
  .subscribe({
    next: (scene) => renderScene(ctx, scene),
    complete: () => renderGameOver(ctx)
  });


, takeWhile() . , Observable. game$ , isGameOver() true.

:





, , . , , , .

!



James Henry Brecht Billiet .
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/338910/

:  

: [1] []
 

:
: 

: ( )

:

  URL