ecomfe / zrender

A lightweight graphic library providing 2d draw for Apache ECharts
https://ecomfe.github.io/zrender-doc/public/
BSD 3-Clause "New" or "Revised" License
6.02k stars 1.56k forks source link

错误使用 prototype #366

Open cnlon opened 6 years ago

cnlon commented 6 years ago

zrender 版本:3.1.0 场景复现

使用 echarts-liquidfill 时报如下错误:

Uncaught TypeError: tracks[propName].push is not a function
    at Animator.when (Animator.js)
    at Sub._animateToShallow (Animatable.js)
    at Sub._animateToShallow (Animatable.js)
    at Sub.animateTo (Animatable.js)
    at animateOrSetProps (graphic.js)
    at Object.graphic.updateProps (graphic.js)
    at DataDiffer.eval [as _update] (liquidFillView.js)
    at DataDiffer.execute (DataDiffer.js)
    at ExtendedClass.render (liquidFillView.js)
    at ECharts.eval (echarts.js)

断点发现错误时 propName"constructor"

问题总结

由此发现问题应该是 src/graphic/Style.js L57 :

    Style.prototype = {

        constructor: Style,  // 此处

这种方式 constructor 为 enumerable,使用 for ... in 遍历的时候会遍历到。建议不要直接覆盖 prototype,而是逐个添加属性

涉及到的文件(搜索 constructor: )有: image

此外,zrender/core/util L183 中 inherits 工具方法也有此问题,不过可能不太好处理,不知 zrender 是否要兼容到 IE8,不兼容的话可以使用 Object.defineProperty,当然还不如尽早升级一下代码使用 ES6+ 并做降级处理

Ovilia commented 6 years ago

这个 for in 是哪里的?

Leiting2021 commented 6 years ago

你这个问题和Sytle中的constructor没有关系的,是因为在zrender添加和执行动画的顺序如下: 1.每一个Element会混入Animatable对象最为元素的动画管理类,其中animate方法就是想元素的animators数组中添加动画帧animate 2.animator添加后会返回animator实例,animator实例中的when方法添加动画片段,可以向一个帧中添加多个动画片段(when多次) 3.最后调用animator实例的start方法将每个动画片分解成Clip实例(我一直把clip翻译成小精灵) 4.animator调用animation实例(animation是管理整个zr的动画,她控制的是精灵库clips),这里分多钟情况,分别是:元素已经add到zr中,和元素未add到zr中 5.animation会一直轮训执行它的_update方法,去执行精灵库clips里的每个小精灵,这便是我们肉眼所看到的动画了。 因此,你上面的错误,看来是animator中执行when的时候,trancks没有初始化造成的。 上述分析仅供参考。

cnlon commented 6 years ago

抱歉,问题我没有描述的很清楚,下面我附上图(animation/Animator.js): image image

Leiting2021 commented 6 years ago

你理解的应该不对, 1.this._tracks只是保存动画片段(需要参与动画的属性键值对)一个对象,默认为空对象{},赋值以后的demo:{cx:[{time:500,value:500},{time:1500,value:1500}],cy:[...],...} 2.遍历的var propName in props的props是传入的需要参与的动画的属性,如:{cx:500,cy:500},在这边遍历,是为了把他们加入到_tracks的动画序列里 3.你上文给我回复的第二幅图片第2个箭头的地方,你说tracks一定存在constructor我给的回复是:这里只是想判断该参与动画的属性(如:cx:500)是否已存在动画序列,如果不存在,需要给该属性插入一个动画起点对象,并push到动画序列_tracks中去,就好比我们做动画的时候,是从起点开始的,不能第一帧就跳到变化后的地点开始。 不知道有没有讲清楚,有问题再联络。

cnlon commented 6 years ago

实在抱歉,新版本里已经修复了 image 感谢 @Ovilia Review,感谢 @leitinggit 解答