pts icon indicating copy to clipboard operation
pts copied to clipboard

SVGCanvas animation doesn't work with if statement

Open taktran opened this issue 3 years ago • 3 comments

It seems like if you use an if statement within an SVGCanvas animation, it doesn't work as expected.

This function is a small reproduction of the problem:

export const createShapes = ({ space }) => {
  const state = {
    isPlaying: true
  };
  const form = space.getForm();

  const createPlayIcon = (x, y) =>
    Triangle.fromCenter(new Pt(x, y), 50).rotate2D(33);
  const createStopIcon = (x, y) => Rectangle.fromCenter(new Pt(x, y), 70);

  space.add({
    action: (type) => {
      if (type === "up") {
        state.isPlaying = !state.isPlaying;
      }
    }
  });

  // Works
  space.add(function () {
    // Required for SVGForm
    form.scope && form.scope(this);

    const polygon1 = state.isPlaying
      ? createPlayIcon(150, 150)
      : Rectangle.polygon(createStopIcon(100, 100));
    form.fill("#fff").polygon(polygon1);
  });

  // Doesn't work in SVGCanvas
  space.add(function () {
    // Required for SVGForm
    form.scope && form.scope(this);

    if (state.isPlaying) {
      form.fill("#fff").polygon(createPlayIcon(300, 150));
    } else {
      form.fill("#fff").rect(createStopIcon(250, 100));
    }
  });

  return state;
};

If you pass in a CanvasSpace for space, it works as expected, but when you pass in a SVGSpace, it only renders the play button, even if state.isPlaying changes.

You can see it running in https://codesandbox.io/s/ptsjs-svg-canvas-bug-m7f36

I'm guessing it's because of something funny going on with form.scope(this)?

taktran avatar Sep 23 '20 22:09 taktran

Hi @taktran , I tried a simplified version of your code and it seems to work like this:

Pts.namespace( this );
var space = new SVGSpace("#pt").setup({bgcolor: "#90f", resize: true });
var form = space.getForm();

const state = { isPlaying: true };

space.add({ 

  animate: function (time, ftime) {
    form.scope && form.scope(this);

    if (state.isPlaying) {
      form.fill("#fff").point(space.pointer, 50);
    } else {
      form.fill("#000").point(space.pointer, 50);
    }
  },

  action: function (type) {
    if (type === "up") {
      state.isPlaying = !state.isPlaying;
    }
  }

});

A couple of suggestions:

  • In your code, you're making 3 space.add(...) calls, which means there'll be 3 players created. Not sure if it's what you want to do -- consider using only one space.add(...) like the above.
  • Try using normal callback like action: function() {...} instead of arrow function action: () => {...}

The SVG implementation in Pts is in early-stage and missing a lot of features. Please continue to send feedbacks and file issues :) Thanks!

williamngan avatar Sep 26 '20 07:09 williamngan

Hi @williamngan , thanks for looking into it. Really ❤️ the API of pts.js btw 😄

I'm trying to figure out how to work with pts.js without using the global namespace. Is Pts.namespace built to work with anything other than window? Is this the code that sets up the namespace?

I think there is something that I'm not understanding about the difference between:

  1. Using the same form creation method (ie, .polygon)
const polygon1 = state.isPlaying
      ? createPlayIcon(150, 150)
      : Rectangle.polygon(createStopIcon(100, 100));
form.fill("#fff").polygon(polygon1);

// or
if (state.isPlaying) {
  form.fill("#fff").polygon(createPlayIcon(150, 150));
} else {
  form.fill("#fff").polygon(Rectangle.polygon(createStopIcon(100, 100)));
}
  1. Using different form creation methods (ie, .polygon and .rect)
if (state.isPlaying) {
  form.fill("#fff").polygon(createPlayIcon(300, 150));
} else {
  form.fill("#fff").rect(createStopIcon(250, 100));
}

An updated of the code example: https://codesandbox.io/s/ptsjs-svg-canvas-bug-2-or0wu?file=/src/create-shapes.js


consider using only one space.add(...)

Cool, it was mainly for demonstrative purposes, but it seems like I should restructure my code so that there are data transformation steps, and then have one player for the rendering step

Try using normal callback

Oh, I thought this was only relevant to the animate property. It doesn't seem to change anything with action - maybe because there is no use of this?

taktran avatar Sep 26 '20 09:09 taktran

Hi @taktran , I'm sorry that I totally missed your comment and late to respond.

I think there's a bug somewhere in the SVG scope that seems to clash with if/else statement. I'm going to mark this as a bug and investigate further. However, I am very busy these days, it might take me a while to get to it.

Re your question on "namespace", it's mostly a convenient method to "inject" Pts to a current scope like windows (or any functional scope). It's used for quick scripting. If you're using babel etc, you don't need it -- just use import statements instead.

Re your question on "form": The examples you give should be equivalent. "form" is just to visualize the "points" as rectangles, circles, etc. The bug in SVG might have confused you. Sorry. I would suggest using the canvas version as reference since it's way more stable.

Thank you for the bug report and detailed discussion. Cheers!

williamngan avatar Oct 15 '20 08:10 williamngan