I'm new to THREE.js and with a very poor knowledge in physics - but I am trying to build a football game engine (viewed from top) and right now I'm struggling with the movement of the ball.
when trying to move the ball from side to side, the rotation is always facing one direction and I dont understand how to make this rotate in the direction its moving at.
Ive added a simple code showing this issue. your help is much appreciated.
/*
*
* SET UP MOTION PARAMS
*
*/
var degrees = 10;
var power = 1;
var angleRad = degrees * Math.PI / 120;
var velocityX = Math.cos(angleRad) * power;
var velocityY = Math.sin(angleRad) * power;
var velocityZ = 1;
var friction = 1;
var gravity = 0.2;
var bounciness = 0.9;
window.onload = function (params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
//set the scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
//set the camera
var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = 120;
//set the light
var light = new THREE.SpotLight(0xffffff, 1);
light.position.set(100, 1, 0);
light.castShadow = true;
light.position.set(0, 0, 100);
scene.add(light);
// set the renderer
var renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
/*
*
* ADD MESH TO SCENE
*
*/
// create and add the ball
var geometry = new THREE.SphereGeometry(5, 5, 5);
var material = new THREE.MeshLambertMaterial({ color: 'gray' });
var ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
scene.add(ball);
// create and add the field
var margin = 20;
var fieldRatio = 105 / 68;
var width = 90;
var height = width / fieldRatio;
var material = new THREE.MeshLambertMaterial({ color: 'green' });
var geometry = new THREE.BoxGeometry(width, height, 1);
var field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
* setting up rotation axis
*/
var rotation_matrix = null;
var setQuaternions = function () {
setMatrix();
ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
}
var setMatrix = function () {
rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
}
setQuaternions();
/*
*
* ANIMATION STEP
*
*/
var render = function (params) {
// add velocity to ball
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
//validate if ball is stop moving
if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
console.log("DONE!");
return;
}
// handle boucing effect
if (ball.position.z < 1) {
velocityZ *= -bounciness;
ball.position.z = 1
}
// Update the object's rotation & apply it
ball.matrix.multiply(rotation_matrix);
ball.rotation.setFromRotationMatrix(ball.matrix);
//reducing speed by friction
angleRad *= friction;
velocityX *= friction;
velocityY *= friction;
velocityZ *= friction;
//set up the matrix
setMatrix();
//validate ball is withing its borders otherwise go in the mirror direction
if (Math.abs(ball.position.x) > borders[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
}
if (Math.abs(ball.position.y) > borders[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
}
// reduce ball height with gravity
velocityZ -= gravity;
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>
This is actually a pretty advanced bit of physics to do in a super realistic way if you want to include friction and inertia, etc. But you can take some shortcuts to get a decent visual rolling effect...
If you take the vector in the movement direction of the ball, you can get a perpendicular vector.. by taking the .cross product of the movement vector, with the world up vector.
That vector is the axis that a ball would rotate around if it had complete friction with the ground. Once you have that axis, you can use .rotateOnWorldAxis ( axis : Vector3, angle : Float ) with the object..
then you have to figure out how much to rotate, based on the radius of the ball, and the distance travelled.. so it's the length (called magnitude in my code below) of the movement vector * (PI*2) / the circumference of the ball.
Let me know if this helps...
p.s - Your "angleRad" computation was dividing by 120 instead of 180.. i fixed that.
/*
*
* SET UP MOTION PARAMS
*
*/
var degrees = 35;
var power = 0.45;
var angleRad = degrees * Math.PI / 180;
var velocityX = Math.cos(angleRad) * power;
var velocityY = Math.sin(angleRad) * power;
var velocityZ = 1;
var friction = 1;
var gravity = 0.2;
var bounciness = 0.9;
var ballRadius = 5;
var ballCircumference = Math.PI * ballRadius * 2;
var ballVelocity = new THREE.Vector3();
var ballRotationAxis = new THREE.Vector3(0, 1, 0);
window.onload = function(params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
//set the scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
//set the camera
var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = 120;
//set the light
var light = new THREE.SpotLight(0xffffff, 1);
light.position.set(100, 1, 0);
light.castShadow = true;
light.position.set(0, 0, 35);
scene.add(light);
// set the renderer
var renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
/*
*
* ADD MESH TO SCENE
*
*/
// create and add the ball
var geometry = new THREE.SphereGeometry(ballRadius, 8, 8);
//make a checkerboard texture for the ball...
var canv = document.createElement('canvas')
canv.width = canv.height = 256;
var ctx = canv.getContext('2d')
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = 'black';
for (var y = 0; y < 16; y++)
for (var x = 0; x < 16; x++)
if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
var ballTex = new THREE.Texture(canv);
ballTex.needsUpdate = true;
var material = new THREE.MeshLambertMaterial({
map: ballTex
});
var ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
scene.add(ball);
// create and add the field
var margin = 20;
var fieldRatio = 105 / 68;
var width = 90;
var height = width / fieldRatio;
var material = new THREE.MeshLambertMaterial({
color: 'green'
});
var geometry = new THREE.BoxGeometry(width, height, 1);
var field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
* setting up rotation axis
*/
var rotation_matrix = null;
var setQuaternions = function() {
setMatrix();
ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
}
var setMatrix = function() {
rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
}
setQuaternions();
/*
*
* ANIMATION STEP
*
*/
var render = function(params) {
// add velocity to ball
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
//validate if ball is stop moving
if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
console.log("DONE!");
return;
}
// handle boucing effect
if (ball.position.z < 1) {
velocityZ *= -bounciness;
ball.position.z = 1
}
// Update the object's rotation & apply it
/*
ball.matrix.multiply(rotation_matrix); ball.rotation.setFromRotationMatrix(ball.matrix);
//set up the matrix
setMatrix();
*/
// Figure out the rotation based on the velocity and radius of the ball...
ballVelocity.set(velocityX, velocityY, velocityZ);
ballRotationAxis.set(0, 0, 1).cross(ballVelocity).normalize();
var velocityMag = ballVelocity.length();
var rotationAmount = velocityMag * (Math.PI * 2) / ballCircumference;
ball.rotateOnWorldAxis(ballRotationAxis, rotationAmount)
//reducing speed by friction
angleRad *= friction;
velocityX *= friction;
velocityY *= friction;
velocityZ *= friction;
//validate ball is withing its borders otherwise go in the mirror direction
if (Math.abs(ball.position.x) > borders[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
}
if (Math.abs(ball.position.y) > borders[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
}
// reduce ball height with gravity
velocityZ -= gravity;
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>
If I understand your situation correctly, then you'll want to apply a rotation to the ball, that is based around the "right axis" of the ball's local space.
THREE.js provides a number of helper methods to simplify this math, namely the makeRotationAxis() method on the THREE.Matrix4 class.
Conceptually and practically, a minor set of adjustments to your ball.rotation math should achieve what you are wanting. Please see the following code snippet to see how this can be done (or see this working jsFiddle):
/*
*
* SET UP MOTION PARAMS
*
*/
var rotationAngle = 0;
var degrees = 10;
var power = 1;
var angleRad = degrees * Math.PI / 120;
var velocityX = Math.cos(angleRad) * power;
var velocityY = Math.sin(angleRad) * power;
var velocityZ = 1;
var friction = 1;
var gravity = 0.2;
var bounciness = 0.9;
window.onload = function (params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
var borders = [40, 24] //indicate where the ball needs to move in mirror position
//set the scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
//set the camera
var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = 120;
//set the light
var light = new THREE.SpotLight(0xffffff, 1);
light.position.set(100, 1, 0);
light.castShadow = true;
light.position.set(0, 0, 100);
scene.add(light);
// set the renderer
var renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
/*
*
* ADD MESH TO SCENE
*
*/
// create and add the ball
var geometry = new THREE.SphereGeometry(5, 5, 5);
var material = new THREE.MeshLambertMaterial({ color: 'gray' });
var ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
scene.add(ball);
// create and add the field
var margin = 20;
var fieldRatio = 105 / 68;
var width = 90;
var height = width / fieldRatio;
var material = new THREE.MeshLambertMaterial({ color: 'green' });
var geometry = new THREE.BoxGeometry(width, height, 1);
var field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
* setting up rotation axis
*/
var rotation_matrix = null;
var setQuaternions = function () {
setMatrix();
ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
}
var setMatrix = function () {
rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
}
setQuaternions();
/*
*
* ANIMATION STEP
*
*/
var render = function (params) {
// add velocity to ball
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
//validate if ball is stop moving
if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
console.log("DONE!");
return;
}
// handle boucing effect
if (ball.position.z < 1) {
velocityZ *= -bounciness;
ball.position.z = 1
}
// Update the object's rotation & apply it
// ball.matrix.multiply(rotation_matrix);
// Compute the direction vector of the balls current forward direction of motion
var vectorDirection = new THREE.Vector3(velocityX, velocityY, velocityZ);
// Compute the vector about which the balls rotation is calculated. This is at a
// right angle to the vectorDirection, and so we use the cross product to
// calculate this
var axisOfRotation = new THREE.Vector3().crossVectors(vectorDirection, new THREE.Vector3(0,0,1) );
// Normalise the rotation axis to unit length
axisOfRotation.normalize();
// Build a rotation matrix around the rotation axis.
var rotation = new THREE.Matrix4();
rotation .makeRotationAxis(axisOfRotation, rotationAngle)
ball.rotation.setFromRotationMatrix(rotation );
// Decrement the rotation angle to achieve the rolling effect
rotationAngle -= 0.1;
// ball.rotation.setFromRotationMatrix(ball.matrix);
//reducing speed by friction
angleRad *= friction;
velocityX *= friction;
velocityY *= friction;
velocityZ *= friction;
//set up the matrix
setMatrix();
//validate ball is withing its borders otherwise go in the mirror direction
if (Math.abs(ball.position.x) > borders[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
}
if (Math.abs(ball.position.y) > borders[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
}
// reduce ball height with gravity
velocityZ -= gravity;
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
}
body {
padding: 0;
margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>
<head>
</head>
<body>
</body>
</html>
Related
I'm attempting to create a curved iframe. I know it is not possible to curve an iframe, although I have been researching and found a possible solution, which is, creating multiple iframes and later on syncing them.
I have got some of the code from this post: Three js curved (bend) CSS3DObject which served me really well, although I can't seem to curve the frames and position them correctly.
Here is the fiddle I have created:
https://jsfiddle.net/niltonsf/2cwq38tb/1/
var scene, camera, renderer, container;
init();
animate();
function init() {
scene = new THREE.Scene();
var createElements= function(url, divisions, parent) {
var w = 100.0 / divisions;
var x = 0;
for (var x = 0; x < 100; x += w) {
var el = document.createElement('iframe');
el.src = url;
var x2 = x + w;
el.style = "clip-path:polygon(" + x + "% 0%, " + x2 + "% 0%, " + x2 + "% 100%, " + x + "% 100%);";
var cssObject = new THREE.CSS3DObject(el);
/* if (x > 90){
cssObject.rotation.y = x * 0.4
console.log(x*-1)
cssObject.position.z = x * -1
} else {
cssObject.position.z = x * 1
}
*/
if (x >= 95 || x <= 1) {
/* cssObject.position.z = 0 */
} else if (x > 60) {
cssObject.position.z = (x * -1) * 1 + 10
} else {
cssObject.position.z = x * -1
}
parent.add(cssObject);
}
}
container = new THREE.Object3D();
createElements("https://example.com/",24,container);
scene.add(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 200;
camera.position.y = 30;
renderer = new THREE.CSS3DRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
container.rotation.y += .005;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
As you can see in the image, the red and aqua circles are referring to the ends of the plane bend, the frames from the red circle on (until the middle of all frames) should go backwards until the green circle. On the green circle on the frames should go positive back to the aqua circle, achieving something like:
I looked at this example with three js to draw particles with images and works perfectly but i want to change the image with a switch when click a button (calls this function):
const changeImg = function(num) {
switch (num)
{
case 0:
imgData ="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA....";
break;
case 1:
imgData = "data:image/png;base64,iVBORw0KGgoAAAAN..."
break;
}
img.src = imgData;
}
And works but when you click multiple times website becomes slow.
How can I update just the image without slowing down the website?
EDIT 1
I change the code like this:
var renderer, scene, camera, ww, wh, particles, mw, mh, mz, numState;
numState = 0;
mz = 6; // Matrerial size
ww = document.getElementById('map-container').offsetWidth,
wh = 450;
mw = ww * 2;
mh = wh * 2;
var centerVector = new THREE.Vector3(0, 0, 0);
var previousTime = 0
speed = 10
isMouseDown = false;
// Render
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("map"),
antialias: true
});
renderer.setSize(mw, mh);
renderer.setClearColor(0x12347C);
// Scence
scene = new THREE.Scene();
// Camera
camera = new THREE.OrthographicCamera( ww / - 2, ww / 2, wh / 2, wh / - 2, 1, 1000 );
camera.position.set(7, 0, 4);
camera.lookAt(centerVector);
scene.add(camera);
camera.zoom = 4;
camera.updateProjectionMatrix();
// Geometry
var geometry = new THREE.Geometry();
var material = new THREE.PointsMaterial({
size: mz,
color: 0xFFFFFF,
sizeAttenuation: false
});
// Particle
particles = new THREE.Points();
var getImageData = function(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, image.width, image.height);
}
var drawTheMap = function() {
geometry.dispose();
particles.material.dispose();
particles.geometry.dispose();
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
var vertex = new THREE.Vector3();
vertex.x = x - imagedata.width / 2;
vertex.y = -y + imagedata.height / 2;
vertex.z = -Math.random()*500;
vertex.speed = Math.random() / speed + 0.015;
geometry.vertices.push(vertex);
}
}
}
particles.material = material;
particles.geometry = geometry;
scene.add(particles);
requestAnimationFrame(render);
};
var init = function() {
imagedata = getImageData(image);
drawTheMap();
onResize();
window.addEventListener('mousemove', onMousemove, false);
window.addEventListener('mousedown', onMousedown, false);
window.addEventListener('mouseup', onMouseup, false);
window.addEventListener('resize', onResize, false);
};
var onResize = function(){
var mov1, mov2;
ww = document.getElementById('map-container').offsetWidth;
wh = 450;
if (window.innerWidth > 850) {
mw = ww * 2;
mh = wh * 2;
mz = 6;
mov1 = 2.2;
mov2 = 1.9;
particles.material.size = mz;
} else {
mw = ww;
mh = wh;
mz = 3;
mov1 = 2;
mov2 = 2;
particles.material.size = mz;
}
renderer.setSize(mw, mh);
camera.left = ww / - mov1;
camera.right = ww / 2;
camera.top = wh / mov2;
camera.bottom = wh / - 2;
camera.updateProjectionMatrix();
};
var onMouseup = function(){
isMouseDown = false;
}
var onMousedown = function(e){
isMouseDown = true;
lastMousePos = {x:e.clientX, y:e.clientY};
};
var onMousemove = function(e){
if(isMouseDown){
camera.position.x += (e.clientX-lastMousePos.x)/100;
camera.position.y -= (e.clientY-lastMousePos.y)/100;
camera.lookAt(centerVector);
lastMousePos = {x:e.clientX, y:e.clientY};
}
};
var render = function(a) {
requestAnimationFrame(render);
particles.geometry.verticesNeedUpdate = true;
if(!isMouseDown){
camera.position.x += (0-camera.position.x)*0.06;
camera.position.y += (0-camera.position.y)*0.06;
camera.lookAt(centerVector);
}
renderer.render(scene, camera);
};
var imgData;
var image;
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
const changeState = function(state, num) {
document.getElementById('dropbox-choose').innerHTML = state;
numState = num;
switch (numState)
{
case 0:
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
break;
case 1:
imgData = "data:image/png;base64,iVBORw0KGgoI..."
break;
}
image.src = imgData;
}
image = document.createElement("img");
image.onload = init;
image.src = imgData;
And the THREE.WebGLRenderer is only applied once but when I click to change the image, it does not update and also I still have the problem that the website slows down
it's my first time using three js and i don't know if i'm applying well what it says in the documentation
EDIT 2
var renderer, scene, camera, ww, wh, particles, mw, mh, mz, numState;
numState = 0;
mz = 6;
ww = document.getElementById('map-container').offsetWidth,
wh = 450;
mw = ww * 2;
mh = wh * 2;
var centerVector = new THREE.Vector3(0, 0, 0);
var previousTime = 0
speed = 10
isMouseDown = false;
// Render
renderer = new THREE.WebGLRenderer({
canvas: document.getElementById("map"),
antialias: true
});
renderer.setSize(mw, mh);
renderer.setClearColor(0x12347C);
// Scence
scene = new THREE.Scene();
// Camera
camera = new THREE.OrthographicCamera( ww / - 2, ww / 2, wh / 2, wh / - 2, 1, 1000 );
camera.position.set(7, 0, 4);
camera.lookAt(centerVector);
scene.add(camera);
camera.zoom = 4;
camera.updateProjectionMatrix();
// Geometry
//var geometry = new THREE.Geometry();
var material = new THREE.PointsMaterial({
size: mz,
color: 0xFFFFFF,
sizeAttenuation: false
});
// Particle
particles = new THREE.Points();
particles.material = material
scene.add(particles);
var getImageData = function(image) {
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0);
return ctx.getImageData(0, 0, image.width, image.height);
}
var drawTheMap = function() {
let vertices = particles.geometry; // this acts as a REFERENCE!
vertices.length = 0; // clears the vertices array
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
var vertex = new THREE.Vector3();
vertex.x = x - imagedata.width / 2;
vertex.y = -y + imagedata.height / 2;
vertex.z = -Math.random()*500;
vertex.speed = Math.random() / speed + 0.015;
vertices.vertices.push(vertex);
}
}
}
particles.geometry.verticesNeedUpdate = true; // Inform three.js of the update
requestAnimationFrame(render);
};
var init = function() {
imagedata = getImageData(image);
drawTheMap();
onResize();
window.addEventListener('mousemove', onMousemove, false);
window.addEventListener('mousedown', onMousedown, false);
window.addEventListener('mouseup', onMouseup, false);
window.addEventListener('resize', onResize, false);
};
var onResize = function(){
var mov1, mov2;
ww = document.getElementById('map-container').offsetWidth;
wh = 450;
if (window.innerWidth > 850) {
mw = ww * 2;
mh = wh * 2;
mz = 6;
mov1 = 2.2;
mov2 = 1.9;
particles.material.size = mz;
} else {
mw = ww;
mh = wh;
mz = 3;
mov1 = 2;
mov2 = 2;
particles.material.size = mz;
}
renderer.setSize(mw, mh);
camera.left = ww / - mov1;
camera.right = ww / 2;
camera.top = wh / mov2;
camera.bottom = wh / - 2;
camera.updateProjectionMatrix();
};
var onMouseup = function(){
isMouseDown = false;
}
var onMousedown = function(e){
isMouseDown = true;
lastMousePos = {x:e.clientX, y:e.clientY};
};
var onMousemove = function(e){
if(isMouseDown){
camera.position.x += (e.clientX-lastMousePos.x)/100;
camera.position.y -= (e.clientY-lastMousePos.y)/100;
camera.lookAt(centerVector);
lastMousePos = {x:e.clientX, y:e.clientY};
}
};
var render = function(a) {
requestAnimationFrame(render);
particles.geometry.verticesNeedUpdate = true;
if(!isMouseDown){
camera.position.x += (0-camera.position.x)*0.06;
camera.position.y += (0-camera.position.y)*0.06;
camera.lookAt(centerVector);
}
renderer.render(scene, camera);
};
var imgData;
var image;
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
const changeState = function(state, num) {
document.getElementById('dropbox-choose').innerHTML = state;
numState = num;
switch (numState)
{
case 0:
imgData ="data:image/png;base64,iVBORw0KGgoAAA...";
break;
case 1:
imgData = "data:image/png;base64,iVBORw0KGgoAAA..."
break;
}
image.src = imgData;
}
image = document.createElement("img");
image.onload = init;
image.src = imgData;
When I click to change the image, it does not update and also I still have the problem that the website slows down. I cahaged vertcies.push to vertices.vertices.push()
I know I mentioned disposal in a previous version of my answer, but let's instead consider re-using all of your objects.
particles - Add it to the scene immediately after creation.
material - Assign it to particles immediately; No need to re-assign it every time.
geometry - Don't create it globally, we'll let it work from within particles.
Now what we're going to do is replace the vertices and tell three.js that there are new vertices to upload to the GPU.
var drawTheMap = function() {
let vertices = particles.geometry; // this acts as a REFERENCE!
vertices.length = 0; // clears the vertices array
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
var vertex = new THREE.Vector3();
vertex.x = x - imagedata.width / 2;
vertex.y = -y + imagedata.height / 2;
vertex.z = -Math.random()*500;
vertex.speed = Math.random() / speed + 0.015;
vertices.push(vertex);
}
}
}
particles.geometry.verticesNeedUpdate = true; // Inform three.js of the update
requestAnimationFrame(render);
};
The important part here (other than replacing the contents of the vertices array) is setting particles.geometry.verticesNeedUpdate = true;. This is what triggers three.js to replace the vertices on the GPU. Everything else is re-used, not recreated, so it should run fairly smooth.
The solution is change THREE.geometry to THREE.BufferGeometry
var drawTheMap = function() {
particles.geometry = new THREE.BufferGeometry();
var positions = [];
for (var y = 0, y2 = imagedata.height; y < y2; y += 2) {
for (var x = 0, x2 = imagedata.width; x < x2; x += 2) {
if (imagedata.data[(x * 4 + y * 4 * imagedata.width)] < 128) {
positions.push(x - imagedata.width / 2);
positions.push(-y + imagedata.height / 2);
positions.push(-Math.random()*500);
particles.geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
}
}
}
particles.geometry.verticesNeedUpdate = true;
requestAnimationFrame(render);
};
I'm new to THREE.js and with a very poor knowledge in physics but still I want to make a football manager game (played from top view) and I need to know that the kick of the ball is realistic as possible.
I was able to make the ball move and rotate in the correct direction while changing the position of the movement when the ball hits its boundaries.
now I need to deal with a issue of the curve of the ball and how do I make it so the ball with move in an arc to the top and to the sides (X / Y) depending of the angle of the foot hitting the ball
lets just say, I need to know how to handle two scenarios:
1) when kick start from the near bottom axis of the ball
2) when kick start from the near right axis of the ball
your help is highly appropriated. Thank you!
**
- I've added a code showing what i have so far
- I've added an image illustrating my goal (or this person scoring a goal)
/*
*
* SET UP MOTION PARAMS
*
*/
var boundries = [40, 24] //indicate where the ball needs to move in mirror position
var completeFieldDistance = boundries[0] * 2;
var fullPower = 1.8; //the power needed to move the ball the enitre field in one kick
var power = null; //will be set when the kick set in depending on the distance
var isKickStop = false; //indicate the renderer weather to stop the kick
var velocityX = null;
var velocityY = null;
//*** this is where i need help! ***
//how can I make the ball move in the Z axis with a nice curv up depending on a given angle
var curv = 15;
var peak = curv;
var velocityZ = 0;
var friction = 0.98;
var gravity = 0.5;
var bounciness = 0.8;
var minVelocity = 0.035; //for when it need to stop the kick rendering
var ballRadius = 3;
var ballCircumference = Math.PI * ballRadius * 2;
var ballVelocity = new THREE.Vector3();
var ballRotationAxis = new THREE.Vector3(0, 1, 0);
//world meshes
var ball = {};
var field = {};
/*
*
* THE KICK HANDLERS
*
*/
function onKick(angleDeg, distance) {
isKickStop = true;
peak = curv;
power = (distance / completeFieldDistance) * fullPower;
velocityX = Math.cos(angleDeg) * power;
velocityY = Math.sin(angleDeg) * power;
velocityZ = peak / (distance / 2);
requestAnimationFrame(function (params) {
isKickStop = false;
animateKick();
})
}
//** THIS IS WHERE I NEED HELP - how do I make the ball move
// render the movements of the ball
var animateKick = function (params) {
if (isKickStop) { return; }
ball.position.x += velocityX;
ball.position.z += velocityZ;
ball.position.y += velocityY;
if (Math.abs(velocityX) < minVelocity && Math.abs(velocityY) < minVelocity) {
ball.position.z = ball.bottom;
isKickStop = true;
console.log("DONE!");
return;
}
if (ball.position.z >= peak) {
ball.position.z = peak;
velocityZ *= -1;
}
if (ball.position.z < ball.bottom) {
peak *= gravity;
velocityZ *= -1;
ball.position.z = ball.bottom;
}
// Figure out the rotation based on the velocity and radius of the ball...
ballVelocity.set(velocityX, velocityY, 0);
ballRotationAxis.set(0, 0, 1).cross(ballVelocity).normalize();
var velocityMag = ballVelocity.length();
var rotationAmount = velocityMag * (Math.PI * 2) / ballCircumference;
ball.rotateOnWorldAxis(ballRotationAxis, rotationAmount);
//reduce velocity due to friction
velocityX *= friction;
velocityY *= friction;
//making sure ball is not outside of its boundries
if (Math.abs(ball.position.x) > boundries[0]) {
velocityX *= -1;
ball.position.x = (ball.position.x < 0) ? boundries[0] * -1 : boundries[0];
}
if (Math.abs(ball.position.y) > boundries[1]) {
velocityY *= -1;
ball.position.y = (ball.position.y < 0) ? boundries[1] * -1 : boundries[1];
}
}
window.onload = (function (params) {
/*
*
* SET UP THE WORLD
*
*/
//set up the ratio
var gWidth = window.innerWidth;
var gHeight = window.innerHeight;
var ratio = gWidth / gHeight;
//set the scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
//set the camera
var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = 120;
//set the light
var light = new THREE.SpotLight(0xffffff, 1);
light.castShadow = true;
light.position.set(0, 0, 35);
scene.add(light);
// set the renderer
var renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
/*
*
* ADD MESH TO SCENE
*
*/
// create and add the ball
var geometry = new THREE.SphereGeometry(ballRadius, 8, 8);
//make a checkerboard texture for the ball...
var canv = document.createElement('canvas')
canv.width = canv.height = 256;
var ctx = canv.getContext('2d')
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = 'black';
for (var y = 0; y < 16; y++)
for (var x = 0; x < 16; x++)
if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
var ballTex = new THREE.Texture(canv);
ballTex.needsUpdate = true;
var material = new THREE.MeshLambertMaterial({
map: ballTex
});
ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
ball.bottom = ballRadius / 2;
scene.add(ball);
// create and add the field
var margin = 20;
var fieldRatio = 105 / 68;
var width = 90;
var height = width / fieldRatio;
var material = new THREE.MeshLambertMaterial({ color: 'green' });
var geometry = new THREE.BoxGeometry(width, height, 1);
field = new THREE.Mesh(geometry, material);
field.receiveShadow = true;
field.position.z = -1;
scene.add(field);
/*
*
* HANDLING EVENTS
*
*/
var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
domEvents.addEventListener(field, 'click', function (e) {
//set points 1 and 2
var p1 = { x: e.intersect.point.x, y: e.intersect.point.y };
var p2 = { x: ball.position.x, y: ball.position.y };
var angleDeg = Math.atan2(p1.y - p2.y, p1.x - p2.x);
var a = p1.x - p2.x;
var b = p1.y - p2.y;
var distance = Math.sqrt(a * a + b * b);
window.onKick(angleDeg, distance);
}, false);
/*
*
* ANIMATION STEP
*
*/
var render = function (params) {
//render kick if it is on the go
if(!isKickStop){
animateKick();
}
//render the page
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
})()
body {
padding: 0;
margin: 0;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://www.klika.co.il/scripts/three.events.js"></script>
</head>
<body>
</body>
</html>
I build a model to mock this, the model accept several parameters, initial velocity and angular velocity, there are three force on the ball, gravity, air resistance force and Magnus force.
v0_x = 0; //initial velocity
v0_y = 4;
v0_z = 1;
w_x = 0 * Math.PI; // initial angular velocity
w_y = 2 * Math.PI;
w_z = 0 * Math.PI;
m = 2; //weight
rho = 1.2; // air density
g = 9.8; // gravity
f = 10; //frequency of the rotation of the ball
cl = 1.23; //horizontal tension coefficient
cd = 0.5; //air resistance coefficient
D = 0.22; // diameter of the ball
A = Math.PI * Math.pow((0.5 * D), 2); //cross-sectional area of the ball
t_step = 1 / 60;
b = (1 / 2) * cd * rho * A; //for convenience
c = cl * rho * Math.pow(D, 3) * f; // for convenience
vt_x = v0_x
vt_y = v0_y
vt_z = v0_z
animateKick = function() {
if (ball.position.y < 0) {
return;
}
tmp_1 = c * Math.pow(Math.pow(vt_x, 2) + Math.pow(vt_z, 2) + Math.pow(vt_y, 2), 2)
tmp_2 = (Math.sqrt(Math.pow(w_z * vt_y - w_y * vt_z, 2) + Math.pow(w_y * vt_x - w_x * vt_y, 2) + Math.pow(w_x * vt_z - w_z * vt_x, 2)))
tmp = tmp_1 / tmp_2
Fl_x = tmp * (w_z * vt_y - w_y * vt_z)
Fl_z = tmp * (w_y * vt_x - w_x * vt_y)
Fl_y = tmp * (w_x * vt_z - w_z * vt_y)
//Motion differential equation
a_x = -(b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_x + (Fl_x / m)
a_z = -(b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_z + (Fl_z / m)
a_y = -g - (b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_y + (Fl_y / m)
//use formula : s_t = s_0 + v_0 * t to update the position
ball.position.x = ball.position.x + vt_x * t_step
ball.position.z = ball.position.z + vt_z * t_step
ball.position.y = ball.position.y + vt_y * t_step
//use formula : v_t = a * t to update the velocity
vt_x = vt_x + a_x * t_step
vt_z = vt_z + a_z * t_step
vt_y = vt_y + a_y * t_step
}
window.onload = (function() {
gWidth = window.innerWidth;
gHeight = window.innerHeight;
ratio = gWidth / gHeight;
scene = new THREE.Scene();
scene.background = new THREE.Color(0xeaeaea);
camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
camera.position.z = -15;
light = new THREE.SpotLight(0xffffff, 1);
light.castShadow = true;
light.position.set(0, 5, -10);
scene.add(light);
renderer = new THREE.WebGLRenderer();
//properties for casting shadow
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setSize(gWidth, gHeight);
document.body.appendChild(renderer.domElement);
geometry = new THREE.SphereGeometry(D, 8, 8);
//make a checkerboard texture for the ball...
canv = document.createElement('canvas')
canv.width = canv.height = 256;
ctx = canv.getContext('2d')
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = 'black';
for (y = 0; y < 16; y++)
for (x = 0; x < 16; x++)
if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
ballTex = new THREE.Texture(canv);
ballTex.needsUpdate = true;
material = new THREE.MeshLambertMaterial({
map: ballTex
});
ball = new THREE.Mesh(geometry, material);
ball.castShadow = true;
ball.receiveShadow = false;
ball.bottom = D / 2;
scene.add(ball);
camera.lookAt(ball.position);
plane_geometry = new THREE.PlaneGeometry(20, 100, 32);
plane_material = new THREE.MeshBasicMaterial({
color: 'green',
side: THREE.DoubleSide
});
ground_plane = new THREE.Mesh(plane_geometry, plane_material);
ground_plane.rotation.x = 0.5 * Math.PI
ground_plane.position.y = -1
ground_plane.position.z = 20
scene.add(ground_plane);
render = function(params) {
animateKick();
renderer.render(scene, camera);
requestAnimationFrame(render);
};
render();
})
body {
padding: 0;
margin: 0;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<script src="https://www.klika.co.il/scripts/three.events.js"></script>
</head>
<body>
</body>
</html>
I am creating particles and positioning them randomly with three.js:
for( var i = 0; i < particleCount; i++ ){
var pX = Math.random() * 100-50;
var pY =Math.random() * 100-50;
var pZ = Math.random() * 100-50;
particle = new THREE.Vector3(pX,pY,pZ);
particle.velocity = new THREE.Vector3(Math.random(), Math.random(), pZ);
particles.vertices.push(particle);
}
Then on my requestAnimationFrame update function I am moving the particles:
for (var i = 0; i < particleCount; i++) {
var particle = particles.vertices[i];
particle.y += particle.velocity.y*speed;
particle.x += particle.velocity.x*speed;
}
How can I introduce some limits to the movement ? i.e when the particle reaches the edge of the screen I want to "bounce" them back.
It's better to have direction and velocity for each particle. Direction is always a normalized THREE.Vector3().
Then the code for your particles will be like this:
var particles = [];
var particleCount = 100;
var sizeX = 300;
var sizeY = 200;
var sizeZ = 100;
for (var i = 0; i < particleCount; i++) {
var pX = Math.random() * sizeX - sizeX / 2;
var pY = Math.random() * sizeY - sizeY / 2;
var pZ = Math.random() * sizeZ - sizeZ / 2;
particle = new THREE.Vector3(pX, pY, pZ);
particle.direction = new THREE.Vector3(Math.random() - .5, Math.random() - .5, 0).normalize(); // a normalized vector with random values for x,y
particle.velocity = Math.random() * 50; // speed is 50 units per second
particles.push(particle);
}
Supposing, you use THREE.Points():
var geometry = new THREE.Geometry();
geometry.vertices = particles;
var points = new THREE.Points(geometry, new THREE.PointsMaterial({
size: 5,
color: "red"
}));
scene.add(points);
To set the proper speed (our 50 units per second) we'll need THREE.Clock() and its .getDelta() method:
var clock = new THREE.Clock();
var shift = new THREE.Vector3(); //we will re-use it in the animation loop
var delta = 0; // we will re-use it in the animation loop
And in the animation loop we will do this:
delta = clock.getDelta(); // get period between frames (in seconds)
particles.forEach(function(p) {
if (p.x > sizeX / 2 || p.x < -sizeX / 2) { // it's also can be like if (Math.abs(p.x > sizeX / 2))
p.direction.x = -p.direction.x;
}
if (p.y > sizeY / 2 || p.y < -sizeY / 2) {
p.direction.y = -p.direction.y;
}
if (p.z > sizeZ / 2 || p.z < -sizeZ / 2) {
p.direction.z = -p.direction.z;
}
p.add(shift.copy(p.direction).multiplyScalar(p.velocity * delta)); // here we re-use the `shift` vector
})
points.geometry.verticesNeedUpdate = true; // important, if you won't set it to true you won't get your particles moving
So that's it.
jsfiddle example
PS If you want to use BufferGeometry, then you can refer to this very good SO answer
1.I want to be able to animated shapes at the same time using canvas, but each to one side.
2.Then when the mouse was placed on each circle appears around it with a text.My canvas knowledge isn't amazing, Here is an image to display what i want.
anyone shed some light on how to do it? Here is a fiddle of what I've managed
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var canvas01 = document.getElementById("canvas01");
var ctx01 = canvas01.getContext("2d");
canvas.width = 600;
canvas.height = 600;
canvas01.width = 600;
canvas01.height = 600;
var centerX = canvas01.width / 2;
var centerY = canvas01.height / 2;
var cw = canvas.width;
var ch = canvas.height;
var nextTime = 0;
var duration = 2000;
var start = Date.now();
var end = start + duration;
var endingPct = 100;
var endingPct1 = 510;
var pct = 0;
var pct1 = 0;
var i = 0;
var increment = duration;
var angle = 0;
var background = new Image();
var img = new Image();
img.src = "http://uupload.ir/files/2fhw_adur-d-01.jpg";
//http://uupload.ir/files/2fhw_adur-d-01.jpg
background.src = "http://uupload.ir/files/9a2q_adur-d-00.jpg";
//http://uupload.ir/files/9a2q_adur-d-00.jpg
Math.inOutQuart = function(n) {
n *= 2;
if (n < 1)
return 0.5 * n * n * n * n;
return -0.5 * ((n -= 2) * n * n * n - 2);
};
background.onload = function() {
ctx.drawImage(background, 0, 0);
};
function animate() {
var now = Date.now();
var p = (now - start) / duration;
val = Math.inOutQuart(p);
pct = 101 * val;
draw(pct);
if (pct >= (endingPct )) {
start = Date.now();
return animate1();
}
if (pct < (endingPct )) {
requestAnimationFrame(animate);
}
}
function animate1() {
var now1 = Date.now();
var p1 = (now1 - start) / duration;
val = Math.inOutQuart(p1);
pct1 = centerY + 211 * val;
SmallCircle(pct1);
if (pct1 < (endingPct1 )) {
requestAnimationFrame(animate1);
}
}
function draw(pct) {
var endRadians = -Math.PI / 2 + Math.PI * 2 * pct / 100;
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, 180, -Math.PI / 2, endRadians);
ctx.lineTo(canvas.width / 2, canvas.height / 2);
ctx.fillStyle = 'white';
ctx.fill();
ctx.save();
ctx.clip();
ctx.drawImage(img, 0, 0);
ctx.restore();
}
animate();
function SmallCircle(pctt) {
ctx01.clearRect(0, 0, canvas01.width, canvas01.height);
ctx01.beginPath();
ctx01.arc(centerX, pctt, 7, 0, 2 * Math.PI, false);
ctx01.closePath();
ctx01.fillStyle = 'green';
ctx01.fill();
}
You can use transformations to draw your small circles extending at a radius from the logo center.
Here is example code and a Demo:
The smallCircle function let you specify these settings:
X & Y of the logo center: cx,cy,
The current radius which the small circle is from the logo center: pctt,
The angle of the smallCircle vs the logo center: angle,
The text to draw: text,
The smallCircle fill color: circlecolor,
The arc-circle stroke color: arccolor (if you don't want the arc-circle to appear you can specify transparent as the arccolor),
The text color: textcolor (if you don't want the text to appear you can specify transparent as the textcolor),
var canvas=document.getElementById("canvas01");
var ctx01=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
var cx=canvas.width/2;
var cy=canvas.height/2;
var PI2=Math.PI*2;
var smallCount=8;
var pctt=0;
var chars=['A','B','C','D','E','F','G','H'];
var circleFill='green';
var arcStroke='lawngreen';
var textFill='white';
ctx01.textAlign='center';
ctx01.textBaseline='middle';
animate(performance.now());
function animate(time){
ctx01.clearRect(0, 0, canvas01.width, canvas01.height);
for(var i=0;i<smallCount;i++){
smallCircle(
cx,cy,pctt,PI2/smallCount*i,
chars[i],circleFill,'transparent','transparent');
}
pctt+=1;
if(pctt<100){
requestAnimationFrame(animate);
}else{
for(var i=0;i<smallCount;i++){
smallCircle(
cx,cy,pctt,PI2/smallCount*i,
chars[i],circleFill,arcStroke,textFill);
}
}
}
function hilightCircle(n){}
function smallCircle(cx,cy,pctt,angle,text,circlecolor,arccolor,textcolor){
// move to center canvas
ctx01.translate(cw/2,ch/2);
// rotate by the specified angle
ctx01.rotate(angle);
// move to the center of the circle
ctx01.translate(pctt,0);
// draw the filled small circle
ctx01.beginPath();
ctx01.arc(0,0,7,0,PI2);
ctx01.closePath();
ctx01.fillStyle = circlecolor;
ctx01.fill();
// stroke the outside circle
ctx01.beginPath();
ctx01.arc(0,0,7+5,0,PI2);
ctx01.closePath();
ctx01.strokeStyle=arccolor;
ctx01.stroke();
// unrotate so the text is upright
ctx01.rotate(-angle);
// draw the text
ctx01.fillStyle=textcolor;
ctx01.fillText(text,0,0);
// reset all transforms to default
ctx01.setTransform(1,0,0,1,0,0);
}
body{ background-color:gray; }
canvas{border:1px solid red; margin:0 auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>After animation, click the mouse.</h4>
<canvas id="canvas01" width=300 height=300></canvas>