Raphaeljs animation kills my browser - animation

I have this code where I have a made a character using 20 paths and put it into a set.
Now when I animate the set, the first transformation runs smoothly, the second animation stutters, the third animation doesn't happen as it should and the 4th animation kills my pc, the browser hangs and in the task manager I can see that it consumes up to 70% of CPU. How can I avoid this and free the resources so all the animations run smoothly.
*I have to execute 10 simple y-axis transformation animations on that character.
JS Fiddle
window.onload = function(){
var paper = Raphael(0,0,400,400);
var character = paper.set();
paper.setStart();
var attr = {fill:'red',stroke:'none'};
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var shape = paper.rect(100,100,10,20).attr(attr);
var character = paper.setFinish();
character.transform("t0,200")
//1st animation..
var chartrnsfrm = Raphael.animation({
transform:'...t0,-48'
},1000,"easeout",function(){
character.animate(chartrnsfrm1.delay(2000))
});
character.animate(chartrnsfrm.delay(2000));
//2nd animation..
var chartrnsfrm1 = Raphael.animation({
transform:'...t0,-48'
},1000,"easeout",function(){
character.animate(chartrnsfrm2.delay(2000))
});
//3rd animation..
var chartrnsfrm2 = Raphael.animation({
transform:'...t0,-48'
},1000,"easeout",function(){
character.animate(chartrnsfrm3.delay(2000))
});
//4th animation..
var chartrnsfrm3 = Raphael.animation({
transform:'...t0,-48'
},1000,"easeout");
}

You are expecting the callbacks from animation to work differently than they actually do.
The variable character holds a set of paths. Raphaƫl does not currently map those to groups that would animate all children at once. What is going on here is that each path is animated seperately. In effect, each such animation is triggering a callback when it is done. That way, for the first animation, chartrnsfrm1, the callback is triggered 20 times. You therefore schedule the second animation, chartrnsfrm2, for the whole character 20 times. By the time you reach chartrnsfrm3, your callback is being triggered 20^3 = 8000 times. Scheduling those animations is what kills your browser.
What I have done is remember the last shape, and only schedule the animation of the whole character if the element that the current callback is being called for (this) is equal to the lastShape.
var lastShape = shape;
//1st animation..
var chartrnsfrm = Raphael.animation({
transform:'...t0,-48'
},1000,"easeout",function(){
if (lastShape == this)
character.animate(chartrnsfrm1.delay(2000))
});
character.animate(chartrnsfrm.delay(2000));
You can see the whole updated fiddle here, it has the fourth animation enabled and all of them run smoothly.

Related

Three js - the best way to draw text and line

after gone through three.js docs, I found it is quite hard to draw line with stroke width, and text.
For the line with stroke width I have to draw an rectange and adjust it to look likes a thick line
For the text, there only two option, one is using font loader, which I found it very hard to handle the promise of font loader, and it become slower if I have so many text.
But if I use canvas to draw the text then use sprite to add that canvas, then I got my texts keep rotates whenever I rotate my camera
so it is 2018, is there any way to make life more easier with three js.
Thanks all
my code to create a thick curve line
function curveLine(start, mid, end, width) {
var curveLine = new THREE.Shape();
curveLine.moveTo(start.x, start.y + width);
curveLine.quadraticCurveTo(mid.x, mid.y + width, end.x, end.y + width);
curveLine.lineTo(end.x, end.y);
curveLine.quadraticCurveTo(mid.x, mid.y, start.x, start.y);
curveLine.lineTo(start.x, start.y);
return curveLine;
}
var line = curveLine({x:0,y:0}, {x:120,y:80}, {x:240,y:0}, 20);
var lineGeo = new THREE.ExtrudeGeometry(line, extrudeSettings);
var lineMesh = new THREE.Mesh(lineGeo, new THREE.MeshBasicMaterial({color: 'red'}));
scene.add(lineMesh);
And this my code to draw text, current using canvas to draws
function text2D(text, params) {
var canvas = document.createElement('canvas');
canvas.width = params.radius * 4;
canvas.height = params.radius * 2;
var context = canvas.getContext('2d');
context.font = '20px Verdana';
var metrics = context.measureText(text);
var textWidth = metrics.width;
context.fillStyle = params.color;
context.fillText(text, (canvas.width - textWidth) / 2, params.radius);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({map: texture});
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(canvas.width, canvas.height, 1);
return sprite;
}
and then whenever i rotate the camera, the text keep face the camera. is there any way to prevent that
Cheers all

Three.js: scene snapshot excluding a particular object

I have a three.js scene. There are some objects including a watermark object. I need to take a scene snapshot but it should not include the watermark object. But at the same time a user should not see the scene without watermark on his screen so he could not take a screenshot.
Is it possible and how?
Thank you!
HERE is a fiddle that shows how to take a screenshot.
HERE is a version that hides the mesh before the screenshot is taken.
Original function.
function takeScreenshot() {
var w = window.open('', '');
w.document.title = "Screenshot";
var img = new Image();
img.src = renderer.domElement.toDataURL();
w.document.body.appendChild(img);
}
Altered function to hide mesh.
function takeScreenshot() {
var w = window.open('', '');
w.document.title = "Screenshot";
var img = new Image();
mesh.visible = false;
renderer.render(scene, camera);
img.src = renderer.domElement.toDataURL();
mesh.visible = true;
w.document.body.appendChild(img);
}
I just set the mesh to visible = false, render the scene to take the screenshot, then set mesh.visible back to true.

Attach text above 3D Object

I'm wondering if it is possible to attach a text above a 3D Object?
If so, how would I do it?
So far I'm doing the following below to load a mesh with its material and lastly adding it to a THREE.Object3D(); and adding it to the scene. Works great without any problems.
Next step is I want to show a nice text above its this object that is always fixed and can be seen from every angle.
loader.load('assets/' + enemyUrl, function (geometry, materials) {
material = new THREE.MeshFaceMaterial( materials );
model = new THREE.SkinnedMesh( geometry, material );
var mats = model.material.materials;
for (var i = 0,length = mats.length; i < length; i++) {
var m = mats[i];
m.skinning = true;
}
ensureLoop(geometry.animations[0]);
function ensureLoop( tmp ) {
for ( var i = 0; i < tmp.hierarchy.length; i ++ ) {
var bone = tmp.hierarchy[ i ];
var first = bone.keys[ 0 ];
var last = bone.keys[ bone.keys.length - 1 ];
last.pos = first.pos;
last.rot = first.rot;
last.scl = first.scl;
}
}
model.scale.set(2.5,2.5,2.5);
// TODO: Randomize where to put it in the world
yawObject.position.y = spawnPosition.y;
yawObject.position.x = spawnPosition.x;
yawObject.position.z = spawnPosition.z;
yawObject.add(model);
scene.add(yawObject);
});
Something like this:
This is what my game looks like now:
sure its possible. you can create a canvas with text on it, which you use as a texture on a plane that always looks at the camera, you could also do it with a single particle, but im not quite sure how it would work since particle materials have a size parameter. something like:
var name = 'Rovdjuret';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
ctx.font="20px Georgia";
ctx.fillText(name,10,50);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true; //just to make sure it's all up to date.
var label = new THREE.Mesh(new THREE.PlaneGeometry, new THREE.MeshBasicMaterial({map:texture}));
and inside the render/animation loop you make all the labels look at the camera:
label.lookAt(camera.position);

Adding images on to canvas when Onclick on the canvas

Currently I have setup a canvas, and I'm trying to add some images onto it.
So only when i Onclick on the canvas, a small images will then appear on the position that i clicked on the canvas.
Can anybody able to help me out?
The first thing to keep in mind is that you'll need to capture the coordinates with respect to the canvas, not the entire page, which is exactly what you'll get from the onclick event. This is fairly easy to translate, with code such as this:
var onMouseClick = function(evt){
var x, y;
var point = findOffset(e.target);
x = e.pageX - point.x;
y = e.pageY - point.y;
return {x,y};
}
var findOffset = function(element) {
var left = 0;
var top = 0;
if (element.offsetParent) {
do {
top += element.offsetTop;
left += element.offsetLeft;
} while(element = element.offsetParent);
return {
x : left,
y : top
};
}
};
Where your onMouseClick callback returns an object with the x,y position that the click occurred in your canvas itself.
Next, you'll want to insert your image at this point. How you do this is going to depend somewhat on where your image source, is but it will basically look like:
var canvasCtx = document.getElementById("myCanvasElement").getContext('2d');
var img = document.getElementById("id-of-an-img-tag-in-your-DOM");
canvasCtx.drawImage(img,x,y);
If you want to load your image from somewhere besides the DOM, you can construct the image object yourself like so:
var img = new Image();
img.onload = function(){
canvasCtx.drawImage(img,x,y);
};
img.src = "url/to/your/image.jpg";
This will cause your image to be loaded from a URL on mouse-click, then once the image finishes loading, adds it to your canvas.

The sprite animation in my simple easeljs script is not showing up

I am trying my hand at easeljs and animating a spritesheet. This is the first time I am working with sprites and as such am not knowledgeable about them.
My simple easeljs code to show this specific animation is;
var stage;
function init() {
// create a new stage and point it at our canvas:
stage = new createjs.Stage(document.getElementById("demoCanvas"));
var data = {
images: ["http://i.imgur.com/g5WtL7v.png"],
frames: {width:256, height:256},
animations: {
run:[0,4]
}
};
var spriteSheet = new createjs.SpriteSheet(data);
var animation = new createjs.BitmapAnimation(spriteSheet);
animation.gotoAndPlay("run");
}
But the sprite doesn't shows up on the canvas at all. WHat am I doing wrong?
Additional question;
defining frames in easeljs can be done by
frames: [ // x, y, width, height, imageIndex, regX, regY
While I do understand width and height, what are x, y, imageIndex and regX, regY. The documentaion explains how I can use these parameters but for someone who is working with sprites for the 1st time in my life, I just dont know what these terms mean.
EDIT: I have also tried changing the code as such;
var stage;
function init() {
// create a new stage and point it at our canvas:
stage = new createjs.Stage("demoCanvas");
var data = {
images: ["http://i.imgur.com/g5WtL7v.png"],
frames: {width:256, height:256, count:8},
animations: {
run:[0,4, true]
}
};
var ss = new createjs.SpriteSheet(data);
var animation = new createjs.BitmapAnimation(ss);
animation.x = 100;
animation.y = 100;
stage.addChild(animation);
createjs.Ticker.setFPS(60);
createjs.Ticker.addEventListener("tick", stage);
}
But I am still seeing a blank canvas...
You are missing a frame-count in the frames-object:
frames: {width:...,height:...,count:4} // or whatever number of frames your sprite holds
And just in case: The width and height is the width and height of 1 frame, not the entire image.
See more information and examples here: http://www.createjs.com/Docs/EaselJS/classes/SpriteSheet.html
ok i got things to work by combining both the codes that I have listed above;
var stage;
function init() {
// create a new stage and point it at our canvas:
stage = new createjs.Stage("demoCanvas");
var data = {
images: ["http://i.imgur.com/g5WtL7v.png"],
frames: {width:256, height:256, count:8},
animations: {
run:[0,4, true]
}
};
var ss = new createjs.SpriteSheet(data);
var animation = new createjs.BitmapAnimation(ss);
animation.x = 100;
animation.y = 100;
stage.addChild(animation);
animation.gotoAndPlay("run");
createjs.Ticker.setFPS(10);
createjs.Ticker.addEventListener("tick", stage);
}
Now I have some questions;
If I need animation.gotoAndPlay("run"); to animate the sprite how come the code at https://github.com/CreateJS/EaselJS/blob/master/examples/SpriteSheet_simple.html doesn't needs it?
whats the difference between new createjs.Sprite(ss, "run"); and new createjs.BitmapAnimation(spriteSheet); . I am unable to find any documentation of the former.

Resources