I'm creating an animation with 5 objects using createjs and tween.
The first object is a car that cross horizontally the screen and the other 4 objects are 4 buildings that I want to show once at a time every time the car pass over the object (that is initially hidden).
This is part of the code where i set the position and the alpha to 0.
Truck is the object crossing the screen blocks are the building I want to show one at time.
truck = new createjs.Shape();
truck = new createjs.Bitmap(loader.getResult("truck"));
truck.x=-truck.image.width;
truck.y=0;
createjs.Tween.get(truck, { loop: true })
.wait(250)
.to({ x: w }, 5600, createjs.Ease.getPowInOut(0.5));
block1 = new createjs.Bitmap(loader.getResult("block3"));
block1.x=0;
block1.y=truck.image.height-block1.image.height;
block1.alpha=0.0;
createjs.Tween.get(block1, { override: true })
.wait(250)
.to({ alpha: 1 }, 600, createjs.Ease.getPowInOut(0.5));
block2 = new createjs.Bitmap(loader.getResult("block2"));
block2.x=0;
block2.y=truck.image.height-block2.image.height-block1.image.height;
block2.alpha=0.0;
createjs.Tween.get(block2, { override: true })
.wait(500)
.to({ alpha: 1 }, 1600, createjs.Ease.getPowInOut(0.5));
block3 = new createjs.Bitmap(loader.getResult("block1"));
block3.x=block1.image.width;
block3.y=truck.image.height-block3.image.height;;
block3.alpha=0.0;
block4 = new createjs.Bitmap(loader.getResult("block4"));
block4.x=block1.image.width+block3.image.width;
block4.y=truck.image.height-block4.image.height;;
block4.alpha=0.0;
block5 = new createjs.Bitmap(loader.getResult("block5"));
block5.x=block1.image.width+block3.image.width+block4.image.width;
block5.y=truck.image.height-block5.image.height;;
block5.alpha=0.0;
Use this
var index = truck.parent.getChildIndex(truck);
in order to get the index of the truck object. Then, you will need to set the index of the objects you want to show above, using this:
block.parent.setChildIndex(block, index + 1);
Alternatively, you can remove and then re-add an object in order to get it to be above (highest index) of all the other objects on the canvas:
var parent = block.parent;
parent.removeChild(block);
parent.addChild(block);
Related
So essentially, I have a function that draws a colored rectangle at every globe coordinate point via coordinate and color arrays. (Frequency means new rectangle every x coordinates)
//Given an array of coordinates, respective colors, and level of detail,
//Draws heatmap on the globe
function DrawMapGivenArrays(CoordinateArray, Colors, frequency)
{
var instances = [];
for(var i = 0; i < CoordinateArray.length; i++)
{
var Cartesian1 = new Cesium.Cartesian3.fromDegrees(CoordinateArray[i].lon,
CoordinateArray[i].lat);
var Cartesian2 = new Cesium.Cartesian3.fromDegrees(CoordinateArray[i].lon+frequency,
CoordinateArray[i].lat-frequency);
var CartesianArray = Array();
CartesianArray.push(Cartesian1);
CartesianArray.push(Cartesian2);
var newPrim = new Cesium.GeometryInstance
({
geometry : new Cesium.RectangleGeometry
({
rectangle : Cesium.Rectangle.fromCartesianArray(CartesianArray),
vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
}),
attributes :
{
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Colors[i])
},
id: "Rectangle" + i,
});
instances.push(newPrim);
numberOfRectangles++;
}
var primitive = new Cesium.Primitive
({
releaseGeometryInstances : false,
geometryInstances : instances,
appearance : new Cesium.PerInstanceColorAppearance(),
});
scene.primitives._primitives[1] = primitive;
}
That works fine.. After I draw the map, I'm using this small function below to individually edit the color of one rectangle. (I call this for every rectangle to change all of them).
//Changes the color of a rectangle primitive given its unique id and a color value
function setPrimitiveRectangle(id, color)
{
var CesiumColor = Cesium.ColorGeometryInstanceAttribute.toValue(color);
scene.primitives._primitives[1].getGeometryInstanceAttributes(id).color = CesiumColor; //this line is 10x slower for every instance after it runs the 1st time
//".getGeometryInstanceAttributes(id)" This specific phrase runs 10x slower after 1st instance
}
That works as well. But, for some reason, it has issues.
For example, when I re-color all of the rectangles the first time, it runs very fast. However, every time I re-fun that function again after the first time, it's 10x slower. I narrowed it down to the phrase that was giving me problems( ".getGeometryInstanceAttributes(id)" ).
I tried to circumvent calling the get function by modifying color values directly with this: (Where i is iterating over every rectangle).
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[0] = 0;
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[1] = 0;
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[2] = 0;
viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[3] = 0;
Once I do this, I can check in chrome and see that the values located at those areas changed, however, the colors of the rectangles do not update.
I don't understand why ".getGeometryInstanceAttributes(id)" runs 10x slower after it's called the first time, and why I cannot directly modify viewer.scene.primitives._primitives[1].geometryInstances[i].attributes.color.value[0].
Thanks
Let's say I have a shape at position.x = 0 and I want to smoothly animate it in the render loop to position.x = 2.435. How would I go about that?
You can use the THREE AnimationMixer. The function below sets up the animation. Example jsFiddle.
const createMoveAnimation = ({ mesh, startPosition, endPosition }) => {
mesh.userData.mixer = new AnimationMixer(mesh);
let track = new VectorKeyframeTrack(
'.position',
[0, 1],
[
startPosition.x,
startPosition.y,
startPosition.z,
endPosition.x,
endPosition.y,
endPosition.z,
]
);
const animationClip = new AnimationClip(null, 5, [track]);
const animationAction = mesh.userData.mixer.clipAction(animationClip);
animationAction.setLoop(LoopOnce);
animationAction.play();
mesh.userData.clock = new Clock();
this.animationsObjects.push(mesh);
};
Set your target position as a variable (outside the render loop):
var targetPositionX = 2.435;
Then in your render loop, create an if statement that checks if the object's X position is less than the targetPositionX. If it is , it will add an increment (which you can change based on how fast you want it to move) to the object's X position. When the object's X position becomes greater or equal to the targetPositionX, it will stop moving.
Something like this:
if (object.position.x <= targetPositionX) {
object.position.x += 0.001; // You decide on the increment, higher value will mean the objects moves faster
}
Here is the full code for the render loop:
function loop(){
// render the scene
renderer.render(scene, camera);
// Check the object's X position
if (object.position.x <= targetPositionX) {
object.position.x += 0.001; // You decide on the increment, higher value will mean the objects moves faster
}
// call the loop function again
requestAnimationFrame(loop);
}
Side note
For more detailed/complex animations you may want to look into Tween.js for Three.js which makes animation easier and also allows you to add easing functions to your animation.
You can find it here:
https://github.com/sole/tween.js
I would recommend reading into it if you are getting into Three.js.
I'm very new to Javascript, and what I'm about to ask is probably very rudimentary, but I'm stuck on a project, and I'd like some help.
Basically I'm doing a small Javascript project, where I want there to be some enemies. In the beginning there will be 0 enemies, and then as time goes on, there might be created more enemies. I was thinking that whenever an enemy is created, I should create it as an object, and then store it in an array containing all the active enemies in game. Then, later on, I could remove enemies from the array, that needed to be removed.
This is the function that creates my enemy objects:
function enemy(name, strength, rarity, estTime, success, remTime, Id){
this.name = name;
this.strength = strength;
this.rarity = rarity;
this.estTime = estTime;
this.success = success;
this.remTime = remTime;
this.Id = Id;
}
So, I could then create some enemies like this:
var enemy1 = new enemy("Bob", 1, "Common", 100, 1, 30, 1)
var enemy2 = new enemy("Cow", 22, "Rare", 50, 10, 40, 2)
var enemy3 = new enemy("Pig", 333, "Epic", 25, 10, 50, 3)
Then I could create an array of enemies, and put my 3 enemies into that array:
var enemies = [];
enemies = [enemy1, enemy2, enemy3];
All fine and dandy when I'm doing this manually, but the problem emerges when I want to try to get the code to automatically create some more enemies.
Say I wanted to create an enemy everytime a button was pushed by the user. The name enemy would get some name, strength, rarity whatever, and the next Id in line (in this case 1, 2 and 3 are being used, so the next would be 4).
I was thinking I could do it something like this, but this doesn't work:
enemy[enemies.length + 1] = new enemy("Dog", 444, "Epic", 13, 100, 60, 4);
enemies.push(enemy + [enemies.length + 1]);
I was hoping that this would create an object called "enemy4" with with name, id and whatever I just typed in, and then add that object to the array of enemies.
But this obviously doesn't work. I hope you guys understand the problem, and any help is greatly appreciated. I realize that I'm probably just approaching this all wrong, and that there probably exists a much simpler way to do this.
Thanks!
EDIT: Yep, answer was very simple, got it now. Thanks!
var enemies = [
new enemy(/*params here*/),
new enemy(/*params here*/),
new enemy(/*params here*/),
new enemy(/*params here*/),
new enemy(/*params here*/)
];
Then later:
enemies.push(
new enemy(/*params here*/)
);
I'm trying to change the length of a cylinder or the extrudedHeight of a circle when it has been added to the primitives and is shown in the cesium widget/viewer. For example this cylinder:
var length = 100;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(
Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
var primitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
widget.scene.primitives.add(primitive);
Because it's added to the primitives array it will be shown in the widget, but after 2 seconds for example I get a notification that the length should be halved (that means set to 50). Is there any way to do this? Simply changing it in cylinderGeometry doesn't seem to do the job.
I kind of have it working by creating a new cylinder with the new height, adding it and removing the old one. This however tends to flicker the cylinder (it's gone for a fraction of a second) before the new one is shown. I fixed this problem by removing the old instance after a set time after the new one is added. This whole solution isn't very elegant and doesn't work very well on devices with a small amount of computing power, hence my search for a better solution.
I don't care if this is achieved using cylinders or extruded circles. If you need any more information don't hesitate to ask in the comments below the question.
EDIT
I implemented the second solution Matthew suggested but after a while of it running perfectly the cylinders stop changing height (which didn't occur when I used my solution. The callback in the interval does get called. Here is some code showing what my new solution is (not working):
primitives.add(prim);
window.nodeValuesInterval = setInterval(function () {
if (prim._state == Cesium.PrimitiveState.COMPLETE) {
clearInterval(window.nodeValuesInterval);
clearTimeout(window.nodeValuesTimeout);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}
}, cylindervalueinterval);
window.nodeValuesTimeout = setTimeout(function () {
clearInterval(window.nodeValuesInterval);
primitives.remove(primitiveObjects.value);
primitiveObjects.value = prim;
}, cylindervaluedelay);
Cesium's geometry is currently optimized for static data. Some attributes, such as visibility, color, and material can be changed on the fly, but items that actually modify the geometry (like cylinder height) require you to remove the primitive and recompute the geometry. The flickering your seeing is the result of asynchronous primitive creation being on by default. There are two ways to do want you want.
Disable asynchronous primitive create by passing [options.asynchronous: false to the Primitive constructor. This means that when you add a new primitive, Cesium will not render until it is ready. For one or two objects, you won't notice anything. For lots of objects it will lock up the browser until everything is ready. This does guarantee that you can remove old/add new primitives without any flicker.
The second option is to add your new primitive (without removing the old one) and then every frame, check the _state property of your new Primitive (I thought this was part of the public API but apparently it's not). When the _state is equal to Cesium.PrimitiveState.COMPLETE you can safely remove the old primitive and your guaranteed the new one will render (hence no flicker).
I think we have a bug/feature request to expose the state variable publicly or otherwise notify when the Primitive is ready; but using _state should be fine for the forseeable future. I'll update this issue if we add an official way sometime soon.
Hope that helps.
EDIT: Since more help was requested; here's a complete example. You can copy and paste the below code into Sandcastle using this link.
Basically it uses the scene.preRender event instead of a timeout (preRender is almost always the better answer here). Also, if you receive a new update before the old one is finished processing, it's important to remove that one before computing the new one. Let me know if you are still having problems.
require(['Cesium'], function(Cesium) {
"use strict";
var widget = new Cesium.CesiumWidget('cesiumContainer');
var ellipsoid = widget.scene.globe.ellipsoid;
var lon = 0;
var lat = 0;
var cylinderradius = 30000;
var length = 10000000;
var cylinderslices = 32;
var newPrimitive;
var currentPrimitive;
//This function creates a new cylinder that is half the length of the old one.
function decreaseLength() {
//If there's a pending primitive already, remove it.
if(Cesium.defined(newPrimitive)){
widget.scene.primitives.remove(newPrimitive);
}
length /= 2;
var cylinderGeometry = new Cesium.CylinderGeometry({
length : length,
topRadius : cylinderradius,
bottomRadius : cylinderradius,
slices: cylinderslices,
vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
});
var cylinder = new Cesium.GeometryInstance({
geometry: cylinderGeometry,
modelMatrix: Cesium.Matrix4.multiplyByTranslation(Cesium.Transforms.eastNorthUpToFixedFrame(ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(lon, lat))),
new Cesium.Cartesian3(0.0, 0.0, length * 0.5)),
attributes: {
color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED)
},
id: "Cylinder1"
});
newPrimitive = new Cesium.Primitive({
geometryInstances : cylinder ,
appearance : new Cesium.PerInstanceColorAppearance({
closed : false,
translucent: true,
flat: false,
faceForward: true
}),
allow3DOnly: true,
vertexCacheOptimize: true,
allowPicking: true,
releaseGeometryInstances: false
});
//We add the new cylinder immediately, but don't remove the old one yet.
widget.scene.primitives.add(newPrimitive);
}
//Create the initial cylinder.
decreaseLength();
//Subscribe to the preRender event so we can check the primitive every frame.
widget.scene.preRender.addEventListener(function(scene, time) {
//Remove the old cylinder once the new one is ready.
if(Cesium.defined(newPrimitive) && newPrimitive._state === Cesium.PrimitiveState.COMPLETE){
if(Cesium.defined(currentPrimitive)){
widget.scene.primitives.remove(currentPrimitive);
}
currentPrimitive = newPrimitive;
newPrimitive = undefined;
}
});
Sandcastle.addToolbarButton('Decrease Length', decreaseLength);
Sandcastle.finishedLoading();
});
In raphael, if I want to render the following shape:
I have to do something like:
var paper = Raphael("notepad", 320, 200);
var rect = paper.rect(...);
var line1 = paper.path(...);
var line2 = paper.path(...);
which create three elements: rect, line1, line2.
BUT, I would like to treat the rendered shape as one object in other js code insteand of three. In Raphael, how can I create this shape which returns me just one object not three?
You want to create a set.
Creates array-like object to keep and operate couple of elements at once. Warning: it doesn't create any elements for itself in the page.
var st = paper.set();
st.push(
paper.circle(10, 10, 5),
paper.circle(30, 10, 5)
);
st.attr({fill: "red"});
Your code would look something like this:
var paper = Raphael("notepad", 320, 200),
st = paper.set();
st.push(
paper.rect(...),
paper.path(...),
paper.path(...)
);
// use st elsewhere
Edit
How can I access individual element in the set then?
You can grab references to the objects before you add them to the set:
var paper = Raphael("notepad", 320, 200),
st = paper.set(),
rect1 = paper.rect(...),
path1 = paper.path(...),
path2 = paper.path(...);
st.push(rect1, path1, path2);
I'm not 100% sure, but since the docs say that a set is "array-like," then you should also be able to access elements in the set using array index notation:
var i, elt;
for (i=0; i<st.length; i++)
{
elt = st[i];
// do stuff with elt
}