Newer
Older
reroad-test / 2020-fuga / aframe-master / tests / components / animation.test.js
@fuga sakurai fuga sakurai on 4 Nov 2020 20 KB a-フレームを追加した
/* global assert, setup, suite, test, THREE */
var entityFactory = require('../helpers').entityFactory;
var components = require('index').components;
var registerComponent = require('index').registerComponent;

suite('animation', function () {
  var component;
  var el;

  setup(function (done) {
    this.done = false;
    el = entityFactory();
    el.setAttribute('animation', '');
    el.addEventListener('componentinitialized', function handler (evt) {
      if (evt.detail.name !== 'animation' || this.done) { return; }
      component = el.components.animation;
      this.done = true;
      el.removeEventListener('componentinitialized', handler);
      done();
    });
  });

  suite('basic animation', () => {
    test('sets from value', function () {
      el.setAttribute('animation', {property: 'light.intensity', from: 0.5, to: 1});
      component.tick(0, 20);
      assert.equal(el.getAttribute('light').intensity, 0.5);
    });

    test('sets between value', function () {
      el.setAttribute('animation', {property: 'light.intensity', from: 0.5, to: 1.0, dur: 500});
      component.tick(0, 100);
      component.tick(0, 100);
      assert.ok(el.getAttribute('light').intensity > 0.5);
      assert.ok(el.getAttribute('light').intensity < 1.0);
    });

    test('sets to value', function () {
      el.setAttribute('animation', {property: 'light.intensity', from: 0.5, to: 1.0, dur: 500});
      component.tick(0, 1);
      component.tick(0, 500);
      assert.equal(el.getAttribute('light').intensity, 1.0);
    });

    test('can infer from value', function () {
      el.setAttribute('light', 'intensity', 0.75);
      el.setAttribute('animation', {property: 'light.intensity', to: 1});
      assert.equal(component.config.targets.aframeProperty, 0.75);
    });

    test('handles non-truthy from value (i.e., 0)', function () {
      el.setAttribute('text', {value: 'supermedium'});
      el.setAttribute('animation', {
        property: 'components.text.material.uniforms.opacity.value',
        from: 0,
        to: 1,
        dur: 1000
      });
      component.tick(0, 1);
      assert.equal(el.components.text.material.uniforms.opacity.value, 0);
    });

    test('handles non-truthy to value (i.e., 0)', function () {
      el.setAttribute('animation', {
        property: 'object3D.scale.y',
        from: 1,
        to: 0,
        dur: 1000
      });
      component.tick(0, 1);
      assert.equal(el.object3D.scale.y, 1);
      component.tick(0, 1000);
      assert.equal(el.object3D.scale.y, 0);
    });
  });

  suite('direct component value animation', () => {
    test('can animate component value or member directly', function () {
      el.setAttribute('material', 'opacity', 0);
      el.setAttribute('animation', {
        property: 'components.material.material.opacity',
        dur: 1000,
        from: 0,
        to: 1
      });
      component.tick(0, 1);
      assert.equal(el.components.material.material.opacity, 0);
      component.tick(0, 500);
      assert.ok(el.components.material.material.opacity > 0);
      assert.ok(el.components.material.material.opacity < 1.0);
      component.tick(0, 500);
      assert.equal(el.components.material.material.opacity, 1.0);
    });

    test('can infer from value', function () {
      el.setAttribute('material', 'opacity', 0.75);
      el.setAttribute('animation', {
        property: 'components.material.material.opacity',
        dur: 1000,
        to: 1
      });
      assert.equal(component.config.targets.aframeProperty, 0.75);
    });
  });

  suite('direct object3D value animation', () => {
    test('can animate object3D value directly', function () {
      el.setAttribute('animation', {
        property: 'object3D.position.x',
        dur: 1000,
        from: 0,
        to: 10
      });
      component.tick(0, 1);
      assert.equal(el.object3D.position.x, 0);
      component.tick(0, 500);
      assert.ok(el.object3D.position.x > 0);
      assert.ok(el.object3D.position.x < 10);
      component.tick(0, 500);
      assert.equal(el.object3D.position.x, 10);
    });

    test('can infer from value', function () {
      el.object3D.position.z = 0.75;
      el.setAttribute('animation', {
        property: 'object3D.position.z',
        dur: 1000,
        to: 1
      });
      assert.equal(component.config.targets.aframeProperty, 0.75);
    });

    test('uses degrees for rotation', function () {
      el.setAttribute('animation', {
        property: 'object3D.rotation.x',
        dur: 1000,
        from: 0,
        to: 360
      });
      component.tick(0, 1);
      assert.equal(el.object3D.position.x, 0);
      component.tick(0, 500);
      assert.equal(THREE.Math.degToRad(component.config.targets.aframeProperty),
                   el.object3D.rotation.x);
    });
  });

  suite('color animation', () => {
    test('can animate color object directly', function () {
      el.setAttribute('material', '');
      el.setAttribute('animation', {
        property: 'components.material.material.color',
        dur: 1000,
        from: 'blue',
        to: 'red',
        type: 'color'
      });
      component.tick(0, 1);
      assert.equal(el.components.material.material.color.b, 1);
      assert.equal(el.components.material.material.color.r, 0);
      component.tick(0, 500);
      assert.ok(el.components.material.material.color.b > 0);
      assert.ok(el.components.material.material.color.b < 1);
      assert.ok(el.components.material.material.color.r > 0);
      assert.ok(el.components.material.material.color.r < 1);
      component.tick(0, 500);
      assert.equal(el.components.material.material.color.b, 0);
      assert.equal(el.components.material.material.color.r, 1);
    });

    test('can infer from value', function () {
      el.setAttribute('material', '');
      el.components.material.material.color.r = 0.1;
      el.components.material.material.color.g = 0.2;
      el.components.material.material.color.b = 0.3;
      el.setAttribute('animation', {
        property: 'components.material.material.color',
        dur: 1000,
        to: '#FFF',
        type: 'color'
      });
      assert.equal(component.config.targets[0].r, 0.1);
      assert.equal(component.config.targets[0].g, 0.2);
      assert.equal(component.config.targets[0].b, 0.3);
    });
  });

  suite('vec3 animation', () => {
    setup(function () {
      components.dummy = undefined;
    });

    test('can animate vec3', function () {
      el.setAttribute('animation', {
        property: 'position',
        dur: 1000,
        from: '1 1 1',
        to: '0 0 0'
      });
      component.tick(0, 1);
      assert.equal(el.object3D.position.x, 1);
      assert.equal(el.object3D.position.y, 1);
      assert.equal(el.object3D.position.z, 1);
      component.tick(0, 500);
      assert.ok(el.object3D.position.x > 0);
      assert.ok(el.object3D.position.x < 1);
      component.tick(0, 500);
      assert.equal(el.object3D.position.x, 0);
      assert.equal(el.object3D.position.y, 0);
      assert.equal(el.object3D.position.z, 0);
    });

    test('can infer from value', function () {
      el.object3D.position.set(5, 5, 5);
      el.setAttribute('animation', {
        property: 'position',
        dur: 1000,
        to: '10 10 10'
      });
      assert.equal(component.config.targets[0].x, 5);
      assert.equal(component.config.targets[0].y, 5);
      assert.equal(component.config.targets[0].z, 5);
    });

    test('uses degrees for rotation', function () {
      el.setAttribute('animation', {
        property: 'rotation',
        dur: 1000,
        from: '0 0 0',
        to: '30 60 90'
      });
      component.tick(0, 1);
      component.tick(0, 1000);
      assert.equal(el.object3D.rotation.x, THREE.Math.degToRad(30));
      assert.equal(el.object3D.rotation.y, THREE.Math.degToRad(60));
      assert.equal(el.object3D.rotation.z, THREE.Math.degToRad(90));
    });

    test('can animate vec3 single-property custom component', function () {
      registerComponent('dummy', {
        schema: {type: 'vec3'}
      });
      el.setAttribute('dummy');
      el.setAttribute('animation', {
        property: 'dummy',
        dur: 1000,
        from: '1 1 1',
        to: '0 0 0'
      });
      component.tick(0, 1);
      assert.equal(el.components.dummy.data.x, 1);
      assert.equal(el.components.dummy.data.y, 1);
      assert.equal(el.components.dummy.data.z, 1);
      component.tick(0, 500);
      assert.ok(el.components.dummy.data.x > 0);
      assert.ok(el.components.dummy.data.x < 1);
      component.tick(0, 500);
      assert.equal(el.components.dummy.data.x, 0);
      assert.equal(el.components.dummy.data.y, 0);
      assert.equal(el.components.dummy.data.z, 0);
    });

    test('can animate vec3 property of custom component', function () {
      registerComponent('dummy', {
        schema: {vector: {type: 'vec3'}}
      });
      el.setAttribute('dummy', '');
      el.setAttribute('animation', {
        property: 'dummy.vector',
        dur: 1000,
        from: '1 1 1',
        to: '0 0 0'
      });
      component.tick(0, 1);
      assert.equal(el.components.dummy.data.vector.x, 1);
      assert.equal(el.components.dummy.data.vector.y, 1);
      assert.equal(el.components.dummy.data.vector.z, 1);
      component.tick(0, 500);
      assert.ok(el.components.dummy.data.vector.x > 0);
      assert.ok(el.components.dummy.data.vector.x < 1);
      component.tick(0, 500);
      assert.equal(el.components.dummy.data.vector.x, 0);
      assert.equal(el.components.dummy.data.vector.y, 0);
      assert.equal(el.components.dummy.data.vector.z, 0);
    });
  });

  suite('boolean animation', () => {
    test('can toggle from false to true via strings', function () {
      el.setAttribute('animation', {property: 'visible', from: 'false', to: 'true', dur: 1000});
      component.tick(0, 1);
      assert.equal(el.object3D.visible, false);
      component.tick(0, 500);
      assert.equal(el.object3D.visible, false);
      component.tick(0, 500);
      assert.equal(el.object3D.visible, true);
    });

    test('can toggle from false to true via bools', function () {
      el.setAttribute('animation', {property: 'visible', from: false, to: true, dur: 1000});
      component.tick(0, 1);
      assert.equal(el.object3D.visible, false);
      component.tick(0, 500);
      assert.equal(el.object3D.visible, false);
      component.tick(0, 500);
      assert.equal(el.object3D.visible, true);
    });
  });

  suite('dir (direction)', () => {
    test('can reverse', function () {
      el.setAttribute('animation', {
        property: 'light.intensity',
        from: 0.5,
        to: 1,
        dir: 'reverse',
        dur: 1000
      });
      component.tick(0, 1);
      assert.equal(el.getAttribute('light').intensity, 1.0);
      component.tick(0, 500);
      assert.ok(el.getAttribute('light').intensity < 1.0);
      assert.ok(el.getAttribute('light').intensity > 0.5);
      component.tick(0, 500);
      assert.equal(el.getAttribute('light').intensity, 0.5);
    });

    test('can alternate', function () {
      el.setAttribute('animation', {
        property: 'object3D.rotation.x',
        from: 0,
        to: 360,
        dir: 'alternate',
        dur: 1000,
        loop: true
      });

      component.tick(0, 1);
      assert.equal(el.object3D.rotation.x, 0);

      // Now going up.
      component.tick(0, 1000);
      assert.equal(el.object3D.rotation.x, Math.PI * 2);

      // Now going down.
      component.tick(0, 500);
      assert.ok(el.object3D.rotation.x > 0);
      assert.ok(el.object3D.rotation.x < Math.PI * 2);
    });
  });

  suite('loop', () => {
    test('can loop', function () {
      el.setAttribute('animation', {
        property: 'light.intensity',
        from: 0,
        to: 1,
        loop: true,
        dur: 1000
      });
      component.tick(0, 1);
      assert.equal(el.getAttribute('light').intensity, 0);
      component.tick(0, 1000);
      assert.equal(el.getAttribute('light').intensity, 1.0);
      component.tick(0, 1);
      assert.equal(Math.round(el.getAttribute('light').intensity), 0);
      component.tick(0, 1000);
      assert.equal(el.getAttribute('light').intensity, 1.0);
    });
  });

  test('can set easing', function () {
    el.setAttribute('animation', {
      property: 'light.intensity',
      from: 0,
      to: 1,
      easing: 'easeInOutCubic'
    });
    assert.equal(component.config.easing, 'easeInOutCubic');
  });

  test('can set round', function () {
    el.setAttribute('animation', {
      property: 'light.intensity',
      from: 0,
      to: 1,
      round: true
    });
    assert.equal(component.config.round, true);
  });

  test('can set elasticity', function () {
    el.setAttribute('animation', {
      property: 'light.intensity',
      from: 0,
      to: 1,
      elasticity: 2
    });
    assert.equal(component.config.elasticity, 2);
  });

  suite('startAnimation', function () {
    test('plays by default', function () {
      el.setAttribute('animation', {property: 'position'});
      assert.ok(component.animationIsPlaying);
    });

    test('plays on delay', function (done) {
      el.setAttribute('animation', {property: 'position', delay: 100});
      assert.notOk(component.animationIsPlaying);
      setTimeout(() => {
        assert.ok(component.animationIsPlaying);
        done();
      }, 100);
    });

    test('does not play if startEvents', function () {
      el.setAttribute('animation', {property: 'position', startEvents: 'foo'});
      assert.notOk(component.animationIsPlaying);
    });

    test('does not play if not autoplay', function () {
      el.setAttribute('animation', {property: 'position', autoplay: false});
      assert.notOk(component.animationIsPlaying);
    });
  });

  suite('event listeners', () => {
    test('plays on startEvents', function (done) {
      el.setAttribute('animation', {property: 'position', startEvents: ['foo', 'far']});
      assert.notOk(component.animationIsPlaying);
      el.addEventListener('foo', function handler () {
        assert.ok(component.animationIsPlaying);
        el.removeEventListener('foo', handler);
        done();
      });
      el.emit('foo');
    });

    test('restarts animation on startEvents', function (done) {
      el.setAttribute('animation', {
        property: 'object3D.scale.z',
        from: 1,
        to: 2,
        startEvents: ['foo', 'foo2']
      });

      el.addEventListener('foo', function handler () {
        assert.ok(component.animationIsPlaying);
        assert.equal(el.object3D.scale.z, 1);
        component.tick(0, 1);
        component.tick(0, 550);
        assert.ok(el.object3D.scale.z > 1);
        el.emit('foo2');
        el.removeEventListener('foo', handler);
      });

      el.addEventListener('foo2', function handler2 () {
        component.tick(0, 1);
        assert.ok(component.animationIsPlaying);
        assert.equal(el.object3D.scale.z, 1);
        component.tick(0, 550);
        assert.ok(el.object3D.scale.z > 1);
        el.removeEventListener('foo2', handler2);
        done();
      });

      component.tick(0, 1);
      component.tick(0, 500);
      el.emit('foo');
    });

    test('pauses on pauseEvents', function (done) {
      el.setAttribute('animation', {property: 'position', pauseEvents: 'bar, boo'});
      assert.ok(component.animationIsPlaying);
      el.addEventListener('bar', function handler () {
        setTimeout(() => {
          assert.notOk(component.animationIsPlaying);
          el.removeEventListener('bar', handler);
          done();
        });
      });
      el.emit('bar');
    });

    test('resumes on resumeEvents', function (done) {
      el.setAttribute('text', {opacity: 0, value: 'supermedium'});
      el.setAttribute('animation', {
        property: 'components.text.material.uniforms.opacity.value',
        from: 0,
        to: 1,
        dur: 1000,
        pauseEvents: 'bar',
        resumeEvents: 'qux'
      });
      el.addEventListener('bar', function handler () {
        assert.notOk(component.animationIsPlaying);
        el.removeEventListener('bar', handler);
        el.emit('qux');
      });
      el.addEventListener('qux', function handler2 () {
        assert.ok(component.animationIsPlaying);
        assert.ok(el.components.text.material.uniforms.opacity.value > 0, 'More than 0');
        assert.ok(el.components.text.material.uniforms.opacity.value < 1, 'Less than 1');
        component.tick(0, 500);
        assert.equal(el.components.text.material.uniforms.opacity.value, 1);
        el.removeEventListener('qux', handler2);
        done();
      });

      assert.ok(component.animationIsPlaying, 'Should be playing');
      component.tick(0, 1);
      component.tick(0, 500);
      assert.ok(el.components.text.material.uniforms.opacity.value > 0, 'More than 0');
      assert.ok(el.components.text.material.uniforms.opacity.value < 1, 'Less than 1');
      el.emit('bar');
    });
  });

  suite('event emissions', function () {
    test('emits animationbegin event', function (done) {
      el.addEventListener('animationbegin', evt => { done(); });
      el.setAttribute('animation', {property: 'position', to: '2 2 2'});
    });

    test('emits animationcomplete event', function (done) {
      el.addEventListener('animationbegin', evt => {
        el.addEventListener('animationcomplete', evt => { done(); });
        component.tick(1, 1);
        component.tick(100000, 99999);
      });
      el.setAttribute('animation', {property: 'position', to: '2 2 2'});
    });

    test('emits animationcomplete event twice', function (done) {
      var calledOnce = false;
      el.addEventListener('animationbegin', evt => {
        component.tick(1, 1);
        component.tick(100000, 99999);
      });

      el.addEventListener('animationcomplete', evt => {
        if (calledOnce) {
          done();
        } else {
          calledOnce = true;
          component.el.emit('startAnimation');
        }
      });

      el.setAttribute('animation', {
        property: 'position',
        to: '2 2 2',
        startEvents: 'startAnimation'
      });
      component.el.emit('startAnimation');
    });
  });

  suite('tick', function () {
    test('only calls animejs animation.tick if playing', function () {
      el.setAttribute('animation', 'property', 'position');
      let animationTickSpy = this.sinon.spy(component.animation, 'tick');
      component.animationIsPlaying = false;
      component.tick(0, 10);
      assert.notOk(animationTickSpy.called);
      component.animationIsPlaying = true;
      component.tick(0, 10);
      assert.ok(animationTickSpy.called);
      assert.equal(animationTickSpy.getCalls()[0].args[0], 10);
    });
  });

  suite('remove', function () {
    test('stops animation', function () {
      el.setAttribute('animation', {property: 'position'});
      assert.ok(component.animationIsPlaying);
      el.removeAttribute('animation');
      assert.notOk(component.animationIsPlaying);
    });

    test('removes event listeners', function (done) {
      el.setAttribute('animation', {property: 'position', startEvents: 'foo'});
      el.removeAttribute('animation');
      el.emit('foo');
      setTimeout(() => {
        assert.notOk(component.animationIsPlaying);
        done();
      }, 10);
    });
  });

  suite('stopRelatedAnimations', function () {
    test('stops related animations', function (done) {
      el.setAttribute('animation__mouseenter', {
        property: 'position',
        startEvents: 'mouseenter',
        dur: 10000
      });

      el.setAttribute('animation__mouseleave', {
        property: 'position',
        startEvents: 'mouseleave',
        dur: 10000
      });

      let mouseenterComponent = el.components['animation__mouseenter'];
      let mouseleaveComponent = el.components['animation__mouseleave'];
      assert.notOk(mouseenterComponent.animationIsPlaying);
      assert.notOk(mouseleaveComponent.animationIsPlaying);

      el.emit('mouseenter');
      setTimeout(() => {
        assert.ok(mouseenterComponent.animationIsPlaying);
        assert.notOk(mouseleaveComponent.animationIsPlaying);
        el.emit('mouseleave');
        setTimeout(() => {
          assert.notOk(mouseenterComponent.animationIsPlaying);
          assert.ok(mouseleaveComponent.animationIsPlaying);
          done();
        }, 10);
      }, 10);
    });
  });

  test('exposes anime.js', () => {
    assert.ok(window.AFRAME.ANIME);
  });
});