CreateJS / EaselJS

The Easel Javascript library provides a full, hierarchical display list, a core interaction model, and helper classes to make working with the HTML5 Canvas element much easier.
http://createjs.com/
MIT License
8.11k stars 1.97k forks source link

removeChild not working #1038

Open Jaswant99 opened 4 years ago

Jaswant99 commented 4 years ago

Issue Details

if a Tween is added to some timeline on some target. tween = cjs.Tween.get(target); timeline.addTween(tween.wait(1));

if target is an instance of createjs.DisplayObject then in below mentioned _resolveState function(MovieClip.js) _addManagedChild(MovieClip.js) adds that target as a child to owner of the timeline.

Consider this.removeChild(target) is called and target is removed from parent but child is still there because on next tick target again gets added as child in _resolveState function and it looks like removeChild didn't work.

p._resolveState = function() {
        var tl = this.timeline;
        this.currentFrame = tl.position;

        for (var n in this._managed) { this._managed[n] = 1; }

        var tweens = tl.tweens;
        for (var i=0, l=tweens.length; i<l; i++) {
            var tween = tweens[i],  target = tween.target;
            if (target === this || tween.passive) { continue; } // TODO: this assumes the actions tween from Animate has `this` as the target. There's likely a better approach.
            var offset = tween._stepPosition;

            if (target instanceof createjs.DisplayObject) {
                // motion tween.
                this._addManagedChild(target, offset);
            } else {
                // state tween.
                this._setState(target.state, offset);
            }
        }

        var kids = this.children;
        for (i=kids.length-1; i>=0; i--) {
            var id = kids[i].id;
            if (this._managed[id] === 1) {
                this.removeChildAt(i);
                delete(this._managed[id]);
            }
        }
    };

p._addManagedChild = function(child, offset) {
        if (child._off) { return; }
        this.addChildAt(child,0);

        if (child instanceof MovieClip) {
            child._synchOffset = offset;
            // TODO: this does not precisely match Adobe Flash/Animate, which loses track of the clip if it is renamed or removed from the timeline, which causes it to reset.
            // TODO: should also reset when MovieClip loops, though that will be a bit tricky to detect.
            if (child.mode === MovieClip.INDEPENDENT && child.autoReset && (!this._managed[child.id])) { child._reset(); }
        }
        this._managed[child.id] = 2;
    };

Is this ok or is there any other solution or any other approach to solve this problem?

https://jsfiddle.net/jaswant0709/mocqfj3s/17/

danzen commented 3 years ago

This was mentioned just recently in https://github.com/CreateJS/EaselJS/issues/1048#issuecomment-670836338

So... it sounds like it is not the right behavior to add the clip back again once it is removed.

Your proposed solution would be to set the _off property to true when removeChild() is used and set the _off property to false when the addChild() is used. I have made a test version of CreateJS here: https://zimjs.org/cdn/1.3.3/createjs_doc_off.js

The three changes in it can be found by searching: https://github.com/CreateJS/EaselJS/issues/1038

If you could test it and see if it works, that would be great. If not, then I will set up tests here, etc. Just going out for a swim! (hahaha - funny, the issue is from December and I am hoping for a test by the time I get back from swimming eight months later - I am so used to fixing bugs with developers as soon as they report them.)