airbnb / lottie-web

Render After Effects animations natively on Web, Android and iOS, and React Native. http://airbnb.io/lottie/
MIT License
29.85k stars 2.85k forks source link

JS Issue: testing Object type doesn't work in certain environments. #2951

Open DSalman opened 1 year ago

DSalman commented 1 year ago

This is not a bug per se, but rather a Javascript compatibility issue when using Lottie in a NodeJs environment. It may also apply to other situations as well. I traced some strange behavior to conditionals based on object type checks failing, e.g. "if (keyData.o.x.constructor === Array) ...", referring specifically to line 3472 of version 5.10.2. I'm far from a JS expert so I had to research why this might happen. It seems that a more robust method would be do something like:

if (Object.prototype.toString.call(someVar) === '[object Array]'){ // do something based on Array type } There's some in depth explanations here: https://stackoverflow.com/questions/332422/get-the-name-of-an-objects-type#332429 https://stackoverflow.com/questions/4775722/how-can-i-check-if-an-object-is-an-array

It looks like it falls under the issue of "will not work cross-frame and cross-window". Do you think the code should be updated using this method? I found multiple places where "constructor === Class" comparisons are used, but they don't seem to be problematic for me. I recon they could be depending on the features of the particular animation. Once I changed line 3472, my issue was cleared up.

bodymovin commented 1 year ago

Hi, I picked this validation because this code is part of the critical path of execution of the engine, and runs on every frame. From my benchmarks, this comparison in more efficient than invoking the toString method, which makes sense. I'm hesitant on changing and affecting performance in general.
It's an interesting case the one you shared, but I'm curious as to why node wouldn't identify the type as an array. Is it being created in another javascript context?

DSalman commented 1 year ago

Hi. Its quite possible there are multiple contexts, but I'm not sure how to evaluate if that's what's happening. Wouldn't the "keyData" object always be created and compared within the same context? Is there some specific test or check you can think of that might be relevant? I tried this within the context of "interpolateValue()": var myArray = []; // this succeeds if (myArray.constructor === Array) console.log("Array!"); // this fails if (keyData.o.x.constructor === Array) console.log("Array!"); Examining the structure types, they look the same (screenshot attached). I'm not sure where the keyData.o.x value is originally assigned/created, but maybe that would reveal something?

https://user-images.githubusercontent.com/1105016/230914090-718dcb40-7af3-4e98-ac48-030b293722cf.mov