I caught a bug when trying to deploy my site and the three.js canvas would not load (for some reason worked on dev but that's neither here nor there)
The problem is that I am trying to animate text that was loaded using fontLoader.load(). For example, I add text to the scene using the following:
fontLoader.load(
'node_modules/three/examples/fonts/droid/droid_serif_regular.typeface.json',
(droidFont) => {
const textGeometry = new TextGeometry('Scroll to Start', {
size: 5,
height: 1,
font: droidFont,
bevelSize: 5,
bevelThickness: 2,
});
const introTexture = new THREE.TextureLoader().load('suntexture.png');
const textMaterial = new THREE.MeshBasicMaterial({map: introTexture, transparent:true, opacity: .5});
cosnt introText = new THREE.Mesh(textGeometry, textMaterial);
introText.position.set(-5, 37, -140);
scene.add(introText);
}
);
Then, I want it to gently oscillate on the screen so as to not appear static, to do this I would include something like this in my animation function (called at the end of main.js):
function introAnimate() {
introText.position.y += (Math.sin(clock.getElapsedTime())/62.8);
introText.rotation.y += (Math.cos(clock.getElapsedTime())/700);
}
The problem with this is that the console (on dev/preview) says that introText is not defined, I'm assuming because it was declared in a function. I tried to fix this by first declaring them as var or const (didn't work), then adding globalThis. or window. (ie window.introText). But the problem persists.
To be honest, I am surprised the npm run dev version ran correctly in the first place given this reference error.
I have seen some versions of text animation using three.js flow, but I am interested in triggering certain animations on scroll, and rotating/changing other properties that I don't think flow can do. Any suggestions on how to address this would be much appreciated.
I think your problem is that you're running introAnimate() before your font finishes loading.
fontLoader.load() runs asynchronously, meaning it starts loading, and the rest of your code (introAnimate() for instance) keeps running until your font finally loads at which point it calls the callback.
Locally on in your dev environment the font might load instantly, but in production the font takes relatively longer time to download.
And until then, if introAnimate() tries to run, it'll fail because the callback to declare the window.introText was not called yet.
The solution would be to declare window.introText = null before calling fontLoader.load(), overwrite it once it's initialized, and in your introAnimate() check if introText is not null before doing anything:
function introAnimate() {
if (introText === null) return;
introText.position.y += (Math.sin(clock.getElapsedTime())/62.8);
introText.rotation.y += (Math.cos(clock.getElapsedTime())/700);
}
Related
I've been having a persistent problem with my image display elements (cubes) not showing the assigned JPG/PNG on their surfaces, usually on the first load of my ThreeJS world from a browser with an empty browser cache. At first, I assumed that this was some kind of a CDN distribution problem with my server, because after you reload the world (host page) once or twice, everything displays fine. This led me to believe, incorrectly, that it was CDN issue.
However, my internet went out for a few days and then I discovered something strange. Please remember that at the time this happened I was working off a local copy of my resources so loading and display of images should have been unaffected by the outage. Instead, it was way worse. Instead of about half or so of the images beeing missing, pretty much all of the JPG/PNG based picture displays (ThreeJS cubes) were blank. (See the screenshot below).
Now my intuition on the problem has completely reversed. It seems that loading the images remotely from my server actually helped reduce the problem. My current theory is that the longer load times on the elements that are loaded before the JPG/PNG surfaces are loaded, a list of videos hosted on my server, changes the timing of that code thus delaying the execution of my animation loop until some of those JPG/PNG surfaces are "ready" (i.e. - the loader callback function has succeeded).
The problem can't be the CDN because there is no CDN involved when running in the local context. When I look in the browser debugger (Chrome DevTools) and I see a bunch of "Texture marked for update but image is undefined" errors coming from ThreeJS during the SetTexutre2D() method call (see screenshot with the call stack)..
I looked at these other posts on the this forum and Stack Overflow:
https://discourse.threejs.org/t/why-texture-marked-for-update-but-image-is-incomplete/353/2
Three.js r72 - Texture marked for update but image is undefined?
But I looked at my code that loads these images, and I am not setting needsUpdate to TRUE anywhere in it:
/**
* Given a URL to an image, build a ThreeJS texture from it.
*
* #param {String} srcUrl - A URL to an image.
* #param {Boolean} bIsRepeated - Whether or not the texture should be repeated.
* #param {Object} theTexture - A ThreeJS texture object.
*
* #return {MeshBasicMaterial} - Returns a ThreeJS MeshBasicMaterial object
* built from the image at the given URL.
*/
function createMaterialFromImage(srcUrl, bIsRepeated=false) {
const errPrefix = `(createMaterialFromImage) `;
if (misc_shared_lib.isEmptySafeString(srcUrl))
throw new Error(errPrefix + `The srcUrl parameter is empty.`);
// Make sure an attempt to load a GIF file is not made with
// this function.
if (srcUrl.toLowerCase().endsWith('.gif'))
throw new Error(errPrefix + `The srcUrl parameter is a GIF file: ${srcUrl}.`);
if (typeof bIsRepeated !== 'boolean')
throw new Error(errPrefix + `The value in the bIsRepeated parameter is not boolean.`);
const threeJsMaterial = new THREE.MeshBasicMaterial();
if (bVerbose) {
console.info(`${errPrefix}Loading image: ${srcUrl}.`);
}
const loader = new THREE.TextureLoader().load(
// resource URL
srcUrl,
// This function fires when the resource is loaded.
function ( theTexture ) {
// If the image is to be repeated, set the wrap
// properties to THREE.RepeatWrapping, otherwise
// use the default wrapping which is THREE.ClampToEdgeWrapping.
theTexture.wrapS = bIsRepeated ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
theTexture.wrapT = bIsRepeated ? THREE.RepeatWrapping : THREE.ClampToEdgeWrapping;
// Assign the texture value to the material map when the texture is loaded.
threeJsMaterial.map = theTexture;
if (bVerbose)
console.info(`${errPrefix}Resource LOADED: ${srcUrl}.`);
},
// This function will be called as the download of an
// image progresses.
function ( xhr ) {
if (bVerbose) {
const pctLoaded = xhr.loaded / xhr.total * 100;
console.info(`${errPrefix}${pctLoaded}}% loaded. Resource: ${srcUrl}.`);
}
},
// This function will be called in the event of an error.
function ( xhr ) {
console.error( `${errPrefix} Download failed for resource: ${srcUrl}.`);
}
);
// Return the threeJsMaterial we created the desired image.
return threeJsMaterial;
}
Why am i getting this error? Should I not add the element to the scene until the async texture loader callback function has already fired?
Also, is this actually what is causing my problem with the blank cube surfaces in my ThreeJS world, or is it something else?
BROWSER DEBUGGER SCREEN SHOT 1:
BROWSER DEBUGGER SCREEN SHOT 2 (Call Stack):
I have gone through the docs and sample only to be lost in outdated docs. Apparently there is no samples for the latest version of createjs.
I need to smooth scroll a horizontal spritesheet. So that the middle of images are shown before the new image is entirely in the "window". So the place in the page doesnt move only what is displayed from the single column horizontal spritesheet is different. And we do not switch between images we scroll up and down.
I am at my wits end with this.
this.sprite = new createjs.BitmapAnimation(spriteSheet);
that line gives an error
not a constructor
this.sprite = createjs.BitmapAnimation(spriteSheet);
gives an error
not a function
here is the sprite sheet
https://github.com/nydehi/asp.net-mvc-example-invoicing-app/blob/master/screenshots/1.png
i am doing this now and no error but nothing is displayed.
<script type="text/javascript" src="createjs-2014.12.12.min.js"></script>
<script>
function init() {
var data = {
images: ["1.png"],
frames: {width:15, height:20},
animations: {
stand:0,
run:[1,5],
jump:[6,8,"run"]
}
};
var spriteSheet = new createjs.SpriteSheet(data);
var animation = new createjs.Sprite(spriteSheet, "run");
var stage = new createjs.Stage("demoCanvas");
stage.addChild(animation);
animation.gotoAndPlay("run"); //walking from left to right
stage.update();
}
</script>
You're probably using a more recent version of CreateJS (around version 0.8.2) which no longer has a BitmapAnimation class on it.
Older versions (0.6.0) had it, but it was most likely replaced by the SpriteSheet class.
Check here for the most recent documentation.
The earlier comments about BitmapAnimation being deprecated long ago are right. Your updated code sample looks fine otherwise.
I think you are just missing something to update the stage when contents change. Your sample just has the one update, but because your image is loaded using a string path, it is not ready yet when you call stage.update().
Any time contents change, you need to tell the stage to refresh. Typically, this is manually controlled when contents change, or constantly using the Ticker.
createjs.Ticker.on("tick", function(event) {
// Other updates
// Update the stage
stage.update(event);
});
// Or a handy shortcut if you don't need to do anything else on tick:
createjs.Ticker.on("tick", stage);
While it is easy enough to get firstPaint times from dev tools, is there a way to get the timing from JS?
Yes, this is part of the paint timing API.
You probably want the timing for first-contentful-paint, which you can get using:
const paintTimings = performance.getEntriesByType('paint');
const fmp = paintTimings.find(({ name }) => name === "first-contentful-paint");
enter code here
console.log(`First contentful paint at ${fmp.startTime}ms`);
Recently new browser APIs like PerformanceObserver and PerformancePaintTiming have made it easier to retrieve First Contentful Paint (FCP) by Javascript.
I made a JavaScript library called Perfume.js which works with few lines of code
const perfume = new Perfume({
firstContentfulPaint: true
});
// ⚡️ Perfume.js: First Contentful Paint 2029.00 ms
I realize you asked about First Paint (FP) but would suggest using First Contentful Paint (FCP) instead.
The primary difference between the two metrics is FP marks the point
when the browser renders anything that is visually different from what
was on the screen prior to navigation. By contrast, FCP is the point
when the browser renders the first bit of content from the DOM, which
may be text, an image, SVG, or even a canvas element.
if(typeof(PerformanceObserver)!=='undefined'){ //if browser is supporting
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.entryType);
console.log(entry.startTime);
console.log(entry.duration);
}
});
observer.observe({entryTypes: ['paint']});
}
this will help you just paste this code in starting of your js app before everything else.
i am making a game based on draw images and clear it every some part of second.
i started with:
var peng = new Image();
and then:
peng.onload = function() {
context.drawImage(peng, pengXPosition, pengYPosition, pengWidth, pengHight);
};
and in the loop:
var i=0;
function pengMoveRight(){ i++;if(i==1){peng.src = 'images/1.png';}else if(i==2)
{peng.src = 'images/2.png';} else if(i==3){peng.src = 'images/3.png';}else if(i==4){
peng.src = 'images/4.png';}else if(i==5){peng.src = 'images/5.png';}else if(i==6){
peng.src = 'images/6.png';i-=6;}}
when i run it it works well on IE but on chrome and mozilla it`s too slow and the character is about to disappear .. i used setinterval(); once and window.requestAnimationFrame(); once and both of them cause the same problem.
what should i do to make it smooth move?
here is the full script http://mark-tec.com/custom%20game/
Instead of changing the source, try to create several Image objects instead. That way, the drawImage call can always use a pre-loaded image.
You need to preload all the images or use the sprite method (all images packed into a single sprite) in order to avoid the initial delay caused by the image loading only when it's needed.
However, after that initial problem, your example should run fine once all the images are cached.
Since downloading the current version of chrome (Version 31.0.1650.57) I've been completely unable to draw images in a HTML5 Canvas with my code; there are no errors and it's finding the resources, they just aren't drawing. I'd really appreciate some help on this!
var grass_img = new Image();
grass_img.src = 'grass.gif';
grass_img.onload = draw_here(grass_img, (center_x + base_x + xpos), (center_y + base_y + ypos),1);
Which appears here and there on several different images and calls:
function draw_here(image, x, y, scale){
draw_canv.drawImage(image, x, y, image.width * scale, image.height * scale);
}
X and Y are correct, as is the scale; there are no coding errors picked up by the debugger and the program worked perfectly until the latest version of chrome came out. Downgrading chrome is also not an option.
The problem is in this line:
grass_img.onload = draw_here( /* ... */ );
You seem to think that you assign the function draw_here as an onload-handler. But that's not what really happens in this line. What you really do is execute draw_here immediately and assign the return-value of that function (which is undefined) to grass_img.onload.
Try this instead:
grass_img.onload = function() {
draw_here(grass_img, (center_x + base_x + xpos), (center_y + base_y + ypos),1);
}
This creates an anonymous function which is assigned to the onload-handler. When that anonymous function is called (which will happen when the load-event is triggered) it calls your draw-handler with your arguments.
I solved the problem. It seems that the latest version of Chrome does not like it if you declare your image in every frame iteration. (Something it seems to have allowed previously, but I really shouldn't have been doing.) If you declare your images and their sources at the top of your js, outside of any functions, and only draw them in functions then the problem is solved, eg:
var image = new Image();
image.src='image.jpg';
someFunction() {
canvas.drawImage(image,0,0);
}
Note: .src should always be called AFTER .onload. Otherwise lots of hard-to-track-down buggies (like this one) appear. Especially if your image loads before onload can be parsed. Your currently accepted solution is possibly functional due to a race condition with function overhead timing.