-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support complex tweens #260
Comments
I would be very happy to provide an implementation and docs/examples for either variant if you like it. |
I like option 1 better. The only thing it changes is allowing "nested" or "recursive" properties to be tweened. I would also prefer that "tween groups" actually be groups of TWEEN.Tween objects, if/when they are implemented. Synchronous tweens would then be possible with some kind of "to" and "startAll" method of the tween group. Option 2 is potentially faster than option 1, because option 1 requires checking the recursive properties in the onUpdate method, or at least checking if each property is an object. This will be a very minor performance hit for tweens without nested properties. Please implement option 1 when you have the time, and make a pull request. Try to change as little of the code as possible and keep the "hot path" efficient. |
@mikebolt I think the first option has a nicer interface, but implementation-wise I would prefer the group-variant, because
I think it could be possible to have both variants supported with the same codebase (the first option would then be a syntactical sugar variant for the second). This is how I thought:
That way there would be the simple use-case (i.e. all tween-settings identical) supported with a simple syntax and the user would have the option to manually create a group if more configuration is required. |
OK, I can see your reasoning. Let's draft an API for tween groups. Once we I implemented a version of tween groups a while back, but it was never
|
So, just what I had in mind in more detail: The basic Idea for a group is to make a set of tweens controllable together. first, the tween-group // creating a group
var group = new TWEEN.Group({
tween1: new TWEEN.Tween(object1),
tween2: new TWEEN.Tween(object2)
});
// children are stored by key in a children-object of the group so they could be
// accessed independently
group.children === {tween1: ..., tween2: ...};
// maybe we should also add methods add/remove/contains to modify
// the list of child-tweens? What do you think?
// groups have the same methods and behavior as regular tweens.
// The `to`-method is special though, as it will accept values for all child-tweens
// and pass them on to the childs based on the top-level key.
group.to({
tween1: {value: 1, otherValue: 10},
tween2: {value: 2, otherValue: 20}
}, 500);
// would internally call
tween1.to({value: 1, otherValue: 10}, 500);
tween2.to({value: 2, otherValue: 20}, 500);
// other methods will set the same values / call the corrensponding methods
// on all child-tweens
group.easing(TWEEN.Easing.Quadratic.In);
group.delay(100);
group.start();
// we might consider here the possibility to pass objects to these methods in
// order to control child-tweens independently
group.easing({
tween1: TWEEN.Easing.Quadratic.In,
tween2: TWEEN.Easing.Quadratic.InOut
});
group.delay({tween1: 0, tween2: 500});
// or to set independent durations
group.to({...}, { tween1: 1000, tween2: 2000 }); this is still not a complete proposal, but I think it's enough to get an Idea and to collect opinions. |
Thank you. I agree that we should collect more opinions. Here's mine: I agree that groups should make groups of tweens controllable together, and that they should have all the methods that single tweens have. However, I think that allowing grouped tweens to have keys would cause serious confusion. I think that tween groups should behave like a set of tweens, not a map of tweens. If grouped tweens must have names or keys, then it seems to me like there is no reason to put them in a group. You could have just made multiple separate tweens yourself. Yeah, you can control them together, but there's another problem... One common use case is to make a bunch of tweens algorithmically, in some kind of loop:
Now suppose I want to group the tweens together, so that I can set the easing with one call, and start them all at once. With your proposal, I have to invent a key for each one, then remember those keys, and it would look something like this:
In general, I think that if you want to have named tweens, you can do that with JavaScript, and if you want to control tweens separately, then you can do that by making separate tweens and controlling them separately. The only purpose of the groups should be to blindly perform actions on an entire set of tweens simultaneously. Here's how I would like the above code to be written:
Or, alternatively:
|
Hi @usefulthink |
@dalisoft please, please keep discussions to the scope of tween.js. If people want to use your project, they will. Talking about something else just distracts us from the issue we are trying to discuss. |
@usefulthink I like the idea that Tween.Group creates tweens internally! I also like @mikebolt idea + observations. Also, I am thinking that this could totally live on its own project, somewhere like |
@sole, sorry |
@sole Thanks! Yes, I would fully agree with that Also considering @mikebolt's take on it, I think it should be possible without too much problems to simply join both use-cases (groups as arrays vs. groups as objects). I will probably just sketch out such a module. |
@usefulthink @sole |
@usefulthink did you think more about this? :) thanks! |
I just recalled this thread when I was thinking about how to solve tweening for a very specific usecase: When working with three.js, I basically just want to be able to do things like const tween = new TWEEN.Tween(object.material)
.to(someOtherMaterial).start(); or (and this is in essence what inspired the initial Idea): const tween = new TWEEN.Tween(object)
.to({position: somePosition, quaternion: andARotation}); Now, this obviously doesn't work as of now. But: in all these cases (including the ones I mentioned in the comments before) this could be solved if there were some configurable part of this library that allows it to handle any data-type, not just numbers. Let's call them If we now make tween.js aware of the interpolators to use, this could look something like this: const tween = new TWEEN.Tween(object)
.to({position: somePosition, quaternion: andARotation});
.interpolators({position: TWEEN.ThreeVector3, quaternion: TWEEN.ThreeQuaternion})
.start(); Those interpolators could be supplied via external libraries for any datatype and library. The Idea just came up and I didn't write a PoC or something like that, but I believe they would look a bit like this (I just thought about this for half an hour): class ThreeVector3 {
constructor(target, to) {
// instance is created for every property interpolated by this interpolator
// when a tween is started.
// target: the value-instance that will be written to (also starting-value)
// to: the specified end-value for the tween
}
update(value) {
// value: the current progress-value ([0..1]) after applied easing-calculation
// updates the target-property with the interpolated value
}
}; Bonus features:
What do you think? (I'm really open for better suggestions regarding the naming, structure and so on, just wanted to get the Idea out). |
I like it. The code currently does interpolation. However, currently interpolation is only performed on numeric properties. We basically have a function like this:
Currently start and end must be numeric values, and the result is a numeric value. We achieve object-to-object tweening by applying this to all of an object's numeric properties. If we changed this to an "anything to anything" function, as you propose, then we don't need to apply the interpolation to each property, necessarily. Would the interpolator return an interpolated object, or would it be responsible for modifying the tweened object? This may be hard to do without breaking backwards-compatibility. You are welcome to try, but maybe we should save this for the next version. |
I don't know about interpolation of objects, but i tried some string interpolation. You can look on My tweening engine based on tween.js, not best as Object tweening code lines: L384-L447 I hope you find this useful, I thinked about make PR, but all of my PR isn't accepted due of Sorry for bad english |
I think it's better if the interpolator only gets access to the value of a single property of the tweened object and it's specified target value. If this is a primitive value, it has to return a value for the library to update the tweened object. But in case of animated Vectors (or any other object- or array-type) this is not strictly neccessary because the Vector-properties will most likely be updated in place to prevent unneccessary object-allocations. I would probably implement something like this: // replacing Tween.js#L340-L355
const interpolator = this.getInterpolator(propertyName);
const interpolated = interpolator.update(value);
if (typeof interpolated !== 'undefined') {
_object[property] = interpolated;
} But yes, I will maybe find some time for a proof-of-concept implementation over the weekend or the holidays. Let's discuss this further when that is done. API-wise it should be possible to get this done without breaking compatibility, although I still have to find a solution for the |
May I ask what happened to the original idea of nesting properties? A simple recursive function could do it. Edit: it seems there is a PR waiting, #366 |
@miltoncandelero Yep! Looking to get that merged soon! |
Actually #520 is more updated. |
Yes, since we needed the feature I asked Malows to make it happen and we are using tween.js directly from his fork now. If the feature gets merged we can go back to depending on this one |
Alright, the nested properties PR is merged! I think there were also some other ideas above, like const interpolator = this.getInterpolator(propertyName);
const interpolated = interpolator.update(value);
if (typeof interpolated !== 'undefined') {
_object[property] = interpolated;
} Please feel free to open new issues for the specific ideas if still desired. |
I did use tween.js a lot in combination with three.js, where there are quite often tweens that need to manipulate properties of different objects at the same time.
What I did mostly was to handle this using a custom onUpdate-function like this:
But it would be nice if that could be made a bit easier.
There are two possible solutions I thought of:
1) allow tweens with complex objects
2) something like tweenGroups (i.e. tweens that always start and end together)
The group would expose an interface identical to the regular tweens and forward most calls to the individual tweens.
what do you think?
The text was updated successfully, but these errors were encountered: