spritejs/spritejs

View on GitHub
benchmark/birds_fly.spritejs-1.16.1.html

Summary

Maintainability
Test Coverage
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
  <title>Birds Flying</title>
  <style>
    *{      
      -webkit-touch-callout: auto; /* prevent callout to copy image, etc when tap to hold */      
      -webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */      
      -webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */       
      -webkit-user-select:none;
    }  
    html,body {
      width: 100%;
      height: 100%;
      padding: 0;
      margin: 0;
      max-width: 500px;
    }
    #paper {
      position: relative;
      overflow: hidden;
      width: 100%;
      padding-top: 100%;
      height: 0px;
    }
  </style>
</head>
<body>
  <div>fps: <span id="fps">--</span> | sprites: <span id="spriteCount">1</span></div>
  <div id="paper"></div>
  
  <script src="https://s2.ssl.qhres2.com/!87edaa34/animator-0.3.1.min.js"></script>
  <script src="https://s0.ssl.qhres2.com/!cfaa644c/spritejs.min.js"></script>
  
  <script>
  const birdsJsonUrl = 'https://s5.ssl.qhres2.com/static/5f6911b7b91c88da.json';
  const birdsRes = 'https://p.ssl.qhimg.com/d/inn/c886d09f/birds.png'

  ;(async function () {
    const paper = spritejs.Paper2D('#paper');
    paper.setResolution(800, 800); // 设置 Paper 的实际分辨率
  
    const cacheMap = new Map();
    window.cacheMap = cacheMap;

    class Bird extends spritejs.Sprite {
      constructor() {
        super('bird1.png');
      }

      // 共用缓存,提高性能
      get cache() {
        const key = JSON.stringify(this.textures) + this.attr('scale');
        if(cacheMap.has(key)) {
          return cacheMap.get(key);
        }
        return null;
      }

      set cache(context) {
        if(context == null) {
          // cacheMap.clear()
          return;
        }
        const key = JSON.stringify(this.textures) + this.attr('scale');

        if(!cacheMap.has(key)) {
          cacheMap.set(key, context);
        }
      }
    }

    await paper.preload(
      [birdsRes, birdsJsonUrl] // 预加载资源,支持雪碧图
    );
  
    const bglayer = paper.layer('bg'), // 背景层
      // 前景层
      // 不代理事件,提升性能
      fglayer = paper.layer('fg', {
        handleEvent: false,
        evaluateFPS: true,
        renderMode: 'repaintAll',
      });
  
    const axisZero = [400, 400];
    const circle = new spritejs.Sprite();
  
    circle.attr({
      anchor: [0.5, 0.5],
      size: [800, 800],
      pos: axisZero,
      bgcolor: '#139',
      opacity: 0.5,
      borderRadius: 400,
    });
  
    bglayer.appendChild(circle);
  
    function pointAdd(p1, p2 = [0, 0]) {
      return [p1[0] + p2[0], p1[1] + p2[1]].map(Math.round);
    }
  
    function pointSub(p1, p2 = [0, 0]) {
      return [p1[0] - p2[0], p1[1] - p2[1]].map(Math.round);
    }
  
    function sleep(time) {
      return new Promise(resolve => setTimeout(resolve, time));
    }
  
    async function randomAnimate(bird) {
      const birdPoint = bird.attr('pos');
      const randomArc = Math.random() * 2 * Math.PI;
      const randomPoint = pointAdd([350 * Math.cos(randomArc),
        350 * Math.sin(randomArc)], axisZero);
  
      const dist = pointSub(randomPoint, birdPoint);
      const distance = Math.round(Math.sqrt(dist[0] * dist[0] + dist[1] * dist[1]));
      const flip = dist[0] < 0 ? -1 : 1;
      const duration = 5 * distance + 100;

      bird.attr('scale', [flip, 1]); // scale 放在外面,触发缓存

      const anim = new Animator(duration, ((p) => {
        const pos = pointAdd(birdPoint, [p * dist[0], p * dist[1]]);
        if(Number.isNaN(pos[0]) || Number.isNaN(pos[1])) {
          console.log('p', duration, p);
        }
        bird.attr({
          pos,
        });
      }));

      await anim.animate();
      await sleep(500);
    }

    let birdCount = 0;
    async function addBird() {
      spriteCount.innerHTML = ++birdCount;
      const bird = new Bird();

      bird.attr({
        anchor: [0.5, 0.5],
        pos: axisZero,
        transform: {
          rotate: 0,
        },
        size: [86, 60],
      });
      window.bird = bird;

      fglayer.appendChild(bird);
  
      let idx = 0;
      setInterval(() => {
        bird.textures = [`bird${++idx % 3 + 1}.png`];
      }, 100);
      window.bird = bird;
      // noprotect
      do {
        await randomAnimate(bird);
      } while(1);
    }

    addBird();

    circle.on('click', (evt) => {
      addBird();
    });
    const timer = setInterval(() => {
      if(birdCount < 500) addBird();
      else clearInterval(timer);
    }, 16);
    // 显示 fps ,注意,因为本框架采用的是非定时渲染,即只有 sprite 有更新时才渲染
    // 所以所有精灵不运动的时候 fps 也会降下来
    setInterval(() => {
      fps.innerHTML = fglayer.fps;
    }, 1000);
    window.fglayer = fglayer;
    window.paper = paper;
  }());
  </script>
</body>
</html>