veltman / flubber

Tools for smoother shape animations.
MIT License
6.61k stars 167 forks source link

Flubber performs differently when loaded via webpack/babel. #93

Closed nthitz closed 6 years ago

nthitz commented 6 years ago

I'll preface this with the admission it could be a bug in webpack/transpilation process but I'm not sure.

I was attempting to use flubber in an app created with create-react-app. I noticed some of my animations had ugly transitions when using flubber. I attempted to reproduce using a bare minimum example but was unable. After a bit of banging my head, it seemed flubber didn't work correctly for me when loaded via an es6 import (alongside webpack/babel) but did work when loaded via a normal script tag.

Here's a link to my demo: https://nthitz.github.io/flubbertest/index.html the top (broken) animation is using flubber loaded through es6 imports. The bottom animation looks more what I desire but I'm having to load flubber via a script tag.

I copied the basic demo from here https://veltman.github.io/flubber/demos/basic-svg.html and added my own paths that were causing problems (Rings). My HTML is here https://github.com/nthitz/flubbertest/blob/master/public/index.html and it has two identical SVGs with different #ids.

My JS is here https://github.com/nthitz/flubbertest/blob/master/src/index.js basically I just call the same flubber demo once using the flubber loaded via ES6 and once with the flubber loaded via the script tag. End up with vastly different behavior. This demo doesn't actually use React, just using CRA for the quick webpack/babel setup.

Any idea if this is a flubber issue or something happening with the trasnspilation? Thanks!

veltman commented 6 years ago

This does seem like a transpilation issue of some kind - I can reproduce the issue with CRA but if I copy the flubber repo into src/ instead, and import it directly, e.g.:

import { interpolate } from "./flubber/index.js"

I can get the desired effect. I'm not sure where exactly the transpiler is going awry, I'll try to look into it more later.

veltman commented 6 years ago

I think there are two related issues at play here:

1) The particular path strings you included get very different results when measuring them with the browser's native SVG .getPointAtLength vs. with the svg-path-properties library. If I force Flubber to use the browser's measurement, it looks fine. svg-path-properties is approximating the curves in such a way that the calculated points are far enough off the mark to produce this weird result with this particular shape.

2) Flubber includes the following block:

  if (typeof module !== "undefined" && module.exports) {
    return svgPathProperties(d);
  } else {
    var svg = window.document.createElementNS("http://www.w3.org/2000/svg", "svg"),
      path = window.document.createElementNS("http://www.w3.org/2000/svg", "path");

    path.setAttributeNS(null, "d", d);

    return path;
  }

the purpose of which is to use the browser's native SVG methods when running in a browser, and otherwise to use the svg-path-properties library to measure the path string's length and points along it when running in Node.

Something about how Webpack wraps modules into its bundle is causing Flubber to think it's not in a browser and execute that block instead of skipping it, presumably module.exports is getting shimmed in somehow.

The ideal solution for this would be for me to produce a different build for the browser field in package.json that doesn't use svg-path-properties at all, and then maybe Webpack would rely on that instead. But in the meantime, it seems like the fix here would be to update the Webpack config in such a way that it actually bundles the ES6 modules directly rather than using the rolled up flubber.min.js.

veltman commented 6 years ago

Closed with 525ce54, which always checks for browser API first before falling back to svg-path-properties.

nthitz commented 6 years ago

Thanks for the fix!