Tween on a boolean property fails - three.js

I am using TWEEN to create a series of animations on THREE.
According to the documentation, creating a tween on a non numeric property should change the property at the end of the transition time.
I am trying to animate the visible property of a material, but it won't work.
Of course I am not trying to change gradually from non-visible to visible; I just want to do it at some moment, but integrated in the tween sequence.
It is not working, and after a quick look at the code, it seems difficult to make it work.
Is there something that I missing in the TWEEN documentation. Or any workaround ?
to
to ( props , duration , ease )
Queues a tween from the current values to the target properties. Set duration to 0 to jump to these value. Numeric properties will be tweened from their current value in the tween to the target value. Non-numeric properties will be set at the end of the specified duration.
tween.js documentation

You have to use .onComplete
new TWEEN.Tween( cube.material ).to( { opacity: 0 }, 1000 )
.onUpdate(function(){
// do something if u want
})
.onComplete(function(){
// change boolean
console.log('DONE');
})
.start();
animate();
Don't forget to set the transparent flag of the material

Related

gsap Flip.fit svg element not moving to expected coordinates because of { duration: x }

I am confused with gsap's Flip.fit moving to coordinates.
I have a game board with 182 tiles and 182 playing tiles.
The goal
When the user clicks the bag, a random playing tile is selected and is "supposed" to move over the tile on the board.
If you change
Flip.fit(PTILE[tileArray], TILE[tileArray], {duration: 1 , scale: true});
when changing { duration: 0, ... } the move works as expected, however no animation. When duration is above zero, the final location is very random.
codepen
I'm not sure how the duration affects the final position, however, I found a way to get the positions right. That is reset the transform of your PTILE before telling GSAP to do the Flip animation.
// reset transform value
gsap.set(PTILE[tileArray], { transform: "" });
// animate with new transform value
Flip.fit(PTILE[tileArray], TILE[tileArray], {
duration: 1,
scale: true
});
My reason is that PTITLE and TITLE are placed in different <g> tags which means their transform systems are inconsistent. Plus, Flip.fit() will act like gsap.to() with new TITLE position is the to object, GSAP will try to calculate the from object from your original transforms which are already set in the SVG as transform:matrix(). This process, somehow, is messing up. So what I did is give GSAP an exact transform value for the from object, which is empty.
Ok, I found out that Inkscape stores the SVG with inline transforms that threw the animation off. I tried saving in plain or optimised, but still had no luck.
So there are two solutions.
Use SVGOMG an online SVG cleaner.
Use Affinity Designer application which can export and flatten transforms.
The key to rule out other factors is to use relative coordinates and flatten transforms.
I have included a screenshot of Affinity exporting options.
Affinity Export screenshot

How to paint a line of decals on object with Three.js?

I have This Example , as you can see - the event that used for adding decals to the object is 'pointerup', like in the following function :
window.addEventListener( 'pointerup', function ( event ) {
if ( moved === false ) {
checkIntersection( event.clientX, event.clientY );
if ( intersection.intersects ) shoot();
}
} );
I wonder how can i add decals while the mouse/ pointer are pressed - so if i could do it - it will be like the action of drawing - which is what i want to achieve...
The problem is that i cant figure out which event and function should i use for repeatedly track each move and append it...
The problem is that i cant figure out which event and function should i use for repeatedly track each move and append it...
You can do this by combining pointerdown, pointerup and pointermove event listeners. Use the first and second one to manage a boolean variable e.g. drawing. On pointerdown, you set it to true. On pointerup, you set it to false. You then know when the interaction is in the drawing state.
In the pointermove event listener, you check for drawing. If set to true, you execute the actual drawing logic. The official three.js example webgl_materials_texture_canvas demonstrates this workflow. The idea of the example is to draw on a canvas which is used as a texture for a cube.

snapping brush to the next x value? [dc.js]

I have a line chart and data in the form
[{
time: "2016-4-29"
total: 23242
},
{
time: "2016-5-16
total: 3322
}
...
]
I'm trying to filter on the x-axis with the brush, however, since I don't have every single date, if I brush in a small range, the filter handler seems to return an empty array for my filters
I've set up my line chart's x-axis like so:
.x(d3.time.scale().domain([minDate,maxDate]))
is there a way to make it so a user can only filter on dates that are in the dataset?
I would like the brush to snap to dates in the dataset.
it seems like whats happening is that you are able to brush between ticks..so it doesn't know what it selected.
I'm going to answer the easier question: How do I create a brush that will not allow nothing to be selected?
In other words, if the brush contains no data, do not allow it to take.
There are two parts to the solution. First, since any chart with a brush will remove the old filter and then add the new filter, we can set up the addFilterHandler to reject any filter that does not contain non-zero bins:
spendHistChart.addFilterHandler(function(filters, filter) {
var binsIn = spendHistChart.group().all().filter(function(kv) {
return filter.isFiltered(kv.key) && kv.value;
});
console.log('non-empty bins in range', binsIn.length);
return binsIn.length ? [filter] : [];
});
That's the straightforward part, and incidentally I think you could probably modify it to snap the brush to existing data. (I haven't tried it, though.)
The more tricky part is that this won't get rid of the brush, it just doesn't apply the filter. So the chart will end up in an inconsistent state.
We need to detect when the brush action has finished, and if there is no filter at that point, explicitly tell the chart to clear the filter:
spendHistChart.brush().on('brushend.no-empty', function() {
if(!spendHistChart.filters().length)
window.setTimeout(function() {
spendHistChart.filterAll().redraw();
}, 100);
});
We need a brief delay here, because if we respond to brushend synchronously, the chart may still be responding to it, causing bickering and dissatisfaction.
As a bonus, you get kind of a "nah-ah" animation because of the unintentional remove-brush animation.
demo fiddle

Multiple overlapping animations on the same element in svg.js

I want to start a animation on an element while a previous animation is still active. However, calling animate() on the element queues the new animation at the end of the current animation.
For example, consider an animation where an element is being moved to a new position. Now, I also want to make it fade out when it reaches a certain position. The following queues the “once” animation at the end of the move, rather than at 80%.
rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
rect.animate(200).opacity(0);
});
How do I make the element start fading out when it reaches 80% of the move? The API seems to be designed for chaining animations rather simultaneous overlapping animations.
What you are trying to do is a bit more complicated. Unforrtunately its not possible to "hack" into the current animation and add a new animation on the fly.
However what you can do is adding a new property which should be animated:
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.opacity(0);
});
As you will notice that has its own problems because the oopacity immediately jumps to 80%. So this is not an approach which works for you.
Next try: Use the during method:
var morh = SVG.morph(
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.during(function(pos, morphFn, easedPos) {
pos = (pos - 0.8) / 0.2
this.target().opacity(pos)
}
});
We just calculate the opacity ourselves

Control multiple cameras with the same controls

I have two different threejs scenes and each has its own camera. I can control each camera individually with a corresponding TrackballControls instance.
Is there a reliable way to 'lock' or 'bind' these controls together, so that manipulating one causes the same camera repositioning in the other? My current approach is to add change listeners to the controls and update both cameras to either's change, but this isn't very neat as, for one, both controls can be changing at once (due to dampening).
I believe it should work if you set the matrices of the second camera to the values of the first and disable automatic matrix-updates of both cameras:
camera2.matrix = camera1.matrix;
camera2.projectionMatrix = camera1.projectionMatrix;
camera1.matrixAutoUpdate = false;
camera2.matrixAutoUpdate = false;
But now you need to update the matrix manually in your renderloop:
camera1.updateMatrix();
That call will take the values for position, rotation and scale (that have been updated by the controls) and compose them into camera1.matrix, which per assignment before is also used as the matrix for the second camera.
However, this feels a bit hacky and can lead to all sorts of weird problems. I personally would probably prefer the more explicit approach you have already implemented.
Question is why are you even using two camera- and controls-instances? As long as the camera isn't added to the scene you can just render both scenes using the same camera.
Is it possible to use the Observer or Publisher design patterns to control these objects?
It seems that you are manipulating the cameras with a control. You might create an object that has the same control interface, but when you pass a command to the object, it repeats that same command to each of the subscribed or registered cameras.
/* psuedo code : es6 */
class MasterControl {
constructor(){
this.camera_bindings = [];
}
control_action1(){
for( var camera of this.camera_bindings ){
camera.control_action1();
}
}
control_action2( arg1, arg2 ){
for( var camera of this.camera_bindings ){
camera.control_action2( arg1, arg2 );
}
}
bindCamera( camera ){
if( this.camera_bindings.indexOf( camera ) === -1 ){
this.camera_bindings.push( camera );
}
}
}
var master = new MasterControl();
master.bindCamera( camera1 );
master.bindCamera( camera2 );
master.bindCamera( camera3 );
let STEP_X = -5;
let STEP_Y = 10;
//the following command will send the command to all three cameras
master.control_action2( STEP_X, STEP_Y );
This binding is self created rather than using native three.js features, but it is easy to implement and can get you functional quickly.
Note: I wrote my psuedocode in es6, because it is simpler and easy to communicate. You can write it in es5 or older, but you must change the class definition into a series of functional object definitions that create the master object and its functionality.

Resources