PROBLEM
I am trying to get an image on a canvas to move from left to right smoothly. It's not too bad on Chrome/Safari (still stutters a little), but there is noticeable stutter in Firefox on multiple machines (tried on Windows and Mac). I'm a bit stumped as to how to solve this.
WHAT I TRIED
I am using requestAnimationFrame instead of setTimeout. I am using clearRect instead of setting the canvas width, though I am clearing the entire canvas instead of the minimum bounding box as I wanted to test it in the worst case scenario. I did close extra tabs. I even disabled Firebug. I am using drawImage instead of the image data functions. Since I'm not doing anything else but clearRect and drawImage, I avoided using an offscreen canvas.
EXAMPLE 1: http://jsfiddle.net/QkvYs/
This one has a set framerate where it ensures that the position is correct regardless of how often the game loop runs. It displays the number of frames skipped. This example is closer to what I'm aiming for. Note that it looks choppy even when no frames are being skipped. I have also played around with the framerate with no success, though I'd like to aim for about 30 fps (var frameRate = 33;).
EXAMPLE 2: http://jsfiddle.net/R8sEV/
Here is a simple one where all it does is move the image. This stutters for me on multiple machines.
//loop
function loop() {
//request anim frame
requestAnimationFrame(loop);
//set canvas once able
if (!canvas) {
var temp = document.getElementById("testCanvas");
if (temp) {
canvas = temp;
ctx = canvas.getContext('2d');
}
}
//update and render if able
if (canvas) {
//increase x
x += 5;
//start from beginning
if (x > canvas.width) {
x = 0;
}
//clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
//image
ctx.drawImage(image, x, 200);
}
}
//start
loop();
WHAT I LOOKED AT
I realize that this question has been asked before and I did look before asking. However, the answers given have unfortunately not helped.
https://gamedev.stackexchange.com/questions/37298/slow-firefox-javascript-canvas-performance
HTML5 Canvas Animation Has Occasional Jitter / Hesitation / Stutter
Canvas animation stutters in FireFox but is perfect in Chrome
Is there a solution for HTML5 canvas animation stutter?
Thanks for the help! I appreciate it.
For instance use time delta in your position calculations. This will ensure that object is moved by certain value in given time regardless of FPS and delay between frames.
Edited your fiddle:
http://jsfiddle.net/R8sEV/2/
wrong approach:
x += 5
good approach:
x += speed * delta / 1000
where delta is time in [ms] which passed from last frame - and speed is measured in [pixels/second]
You are getting the element every frame?!
That can cause a lot of lag.
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var x = 0;
function loop() {
requestAnimationFrame(loop);
x += 5;
ctx.drawImage(IMAGE, x, 0);
}
loop();
Related
I am doing a little scratch/reveal game based on the AlphaMaskFilter example:
http://createjs.com/demos/easeljs/AlphaMaskReveal.html
I want to detect that the the mask is completely gone, or use a threshold (90% scratched for example).
I read the doc on AlphaMaskFilter, shape and graphics objects and im not really sure how to achieve this.
Im not even sure i Have acess to the pixel information and check the alpha channel to detect it, but even so, I wonder if I will performance issue.
any help is welcome, thanks.
**** EDIT **** ADD TO THE ACCEPTED ANSWER ****
So, I was able to have the pct of transparency using the AlphaMapFilter (thanks Lanny).
The AlphaMapFilter offer you a mapping to the alpha channel of all the pixels.
Here is a little sample code that worked for me:
// mShapeToScratch is a createjs Shape. like in the http://createjs.com/demos/easeljs/AlphaMaskReveal.html example
var alphaMaskFilter = new createjs.AlphaMapFilter(mShapeToScratch.cacheCanvas);
var canvas = alphaMaskFilter.alphaMap;
var ctx = canvas.getContext("2d");
var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var alphaData = imgData.data;
var pixelTotal = rect.h*rect.w;
var transparentPixel = 0;
// rect.h is the height of the image and rect.w is the width not shown in the example
for (var y = 0; y < rect.h; ++y)
{
for (var x=0; x < rect.w; ++x)
{
var pixelIdx = (y*rect.w + x);
if(alphaData[pixelIdx] > 128) // transparent will be 255.
{
transparentPixel++;
}
}
console.log("transparent % = " + transparentPixel/pixelTotal);
This example checks all the pixels, but it's pretty easy to check one every X pixels to speeds up checks as Lanny suggested.
The alpha mask uses canvas composite operation, and not pixel access, so without some completely custom approach, there isn't a great way to do this.
Iterating pixels (check out AlphaMapFilter as an example) would work - but could be fairly slow. Maybe checking every 4th, 10th, or 25th pixel would speed it up.
Cheers.
I'm creating a drawing program witch should also use semi-transparent brushes. When I use a transparent brush I end up with some transparent strokes, witch are the lasts until I release the mouse. If I then draw a new stroke again my old strokes get full opacity, even if I don't come across them. The program works getting mouse coordinates, waiting for position changed, and then draws (and strokes) a line which goes from the first point to the second. I have seen that some tutorial suggests to store in memory (array) all the path and draw it again on every mouse release, but I'm not sure due to memory consumption. The program is written in QML + javascript, but canvas works in the same way as does in HTML5.
Thank you in advance to everybody.
The following is the context call:
function pencilBehaviour() {
if (canvas.isPressed){
var ctx = canvas.getContext('2d')
if ((canvas.bufferX != -1) || (canvas.bufferY != -1)){
ctx.globalCompositeOperation = "source-atop"
ctx.moveTo(canvas.bufferX, canvas.bufferY)
ctx.lineTo(canvas.px, canvas.py)
ctx.globalAlpha = 0.4
ctx.lineCap = "round"
ctx.lineJoin = "round"
ctx.strokeStyle = "white"
ctx.lineWidth = 3
ctx.stroke()
console.log("pencil invoking canvas")
//Buffers are needed to draw a line from buffer to current position
canvas.bufferX = canvas.px
canvas.bufferY = canvas.py
}
else{
//Buffers are needed to draw a line from buffer to current position
canvas.bufferX = canvas.px
canvas.bufferY = canvas.py
}
}
}
Hard to know without code, but here is a guess...
Make sure all your new strokes begin with context.beginPath() so the context is not "remembering" your previous strokes.
I suppose this doesn't work because canvas is drawing a bitmap of a vector (and a bitmap is not a path).
Even if it did work, the bitmap is likely always has a rectangular permitter.
Is there any way to leverage something like isPointInPath when using drawImage?
example:
The top canvas is drawn using drawImage and isPointInPath does not work.
The bottom canvas is drawn using arc and isPointInPath works.
a link to my proof
** EDIT **
I draw a circle on one canvas, and use isPointInPath to see if the mouse pointer is inside the circle (bottom canvas in my example).
I also "copy" the bottom canvas to the top canvas using drawImage. Notice that isPointInPath will not work on the top canvas (most likely due to reasons I mentioned above). Is there a work-around I can use for this that will work for ANY kind of path (or bitmap)?
A canvas context has this hidden thing called the current path. ctx.beginPath, ctx.lineTo etc create this path.
When you call ctx.stroke() or ctx.fill() the canvas strokes or fills that path.
Even after it is stroked or filled, the path is still present in the context.
This path is the only thing that isPointInPath tests.
If you want to test if something is in an image you have drawn or a rectangle that was drawn with ctx.fillRect(), that is not possible using built in methods.
Typically you'd want to use a is-point-in-rectangle function that you write yourself (or get from someone else).
If you're looking for how to do pixel-perfect (instead of just the image rectangle) hit detection for an image there are various methods of doing that discussed here: Pixel perfect 2D mouse picking with Canvas
You could try reimplementing ctx.drawImage() to always draw a box behind the image itself, like so (JSFiddle example):
ctx.customDrawImage = function(image, x, y){
this.drawImage(image, x, y);
this.rect(x, y, image.width, image.height);
}
var img1 = new Image();
img1.onload = function(){
var x = y = 0;
ctx.drawImage(img1, x, y);
console.log(ctx.isPointInPath(x + 1, y + 1));
x = 1.25 * img1.width;
ctx.customDrawImage(img1, x, y);
console.log(ctx.isPointInPath(x + 1, y + 1));
Note: you might get side effects like the rectangle appearing over the image, or bleeding through from behind if you are not careful.
To me, isPointInPath failed after canvas was moved. So, I used:
mouseClientX -= gCanvasElement.offsetLeft;
mouseclientY -= gCanvasElement.offsetTop;
I had some more challenges, because my canvas element could be rescaled. So first when I draw the figures, in my case arc, I save them in an array together with a name and draw them:
if (this.coInit == false)
{
let co = new TempCO ();
co.name= sensor.Name;
co.path = new Path2D();
co.path.arc(c.X, c.Y, this.radius, 0, 2 * Math.PI);
this.coWithPath.push(co);
}
let coWP = this.coWithPath.find(c=>c.name == sensor.Name);
this.ctx.fillStyle = color;
this.ctx.fill(coWP.path);
Then in the mouse event, I loop over the items and check if the click event is in a path. But I also need to rescale the mouse coordinates according to the resized canvas:
getCursorPosition(event) {
const rect = this.ctx.canvas.getBoundingClientRect();
const x = ((event.clientX - rect.left ) / rect.width) * this.canvasWidth;
const y = ((event.clientY - rect.top) / rect.height) * this.canvasHeight;
this.coWithPath.forEach(c=>{
if (this.ctx.isPointInPath(c.path, x, y))
{
console.log("arc is hit", c);
//Switch light
}
});
}
So I get the current size of the canvas and rescale the point to the original size. Now it works!
This is how the TempCO looks like:
export class TempCO
{
path : Path2D;
name : string;
}
I have loaded some images through XML and attached into dynamically created MovieClips named mc0,mc1,mc2...etc.
_loader.removeEventListener(ProgressEvent.PROGRESS, onLoadingAction);
count++;
var img:Bitmap = Bitmap(e.target.content);
img.cacheAsBitmap = true;
img.smoothing = true;
img.alpha = 0;
TweenLite.to(MovieClip(my_mc.getChildByName("mc"+count)).addChild(img),1, {alpha:1,ease:Quint.easeIn});
and within ENTER_FRAME handler
for (i=0; i < mc.numChildren; i++)
{
my_mc.getChildAt(i).x -= Math.round((mouseX-stage.stageWidth/2)*.006);
}
Everthing works fine. But it is shaking so that it was not looking good.
How do I achieve smooth movement?
One solution I've used is to round the (x,y) position to the closest integer. No matter that you've added smoothing to your bitmap and cached it, rounding could make it feel less choppy and way smoother.
Another thing you need to be careful is the dimensions of the images. Images that have an odd dimension won't be smoothed the same way as images with even dimensions. Check how to workaround this in my blog post Flash Smoothing Issue.
Since Flash has a variable frame rate (in the sense that it will drop frames), one shouldn't depend on the entering of a frame as a unit of action. Rather, it would be wiser to calculate the elapsed time explicitly.
For instance, in the enter frame handler:
var currentTime:Number = (new Date()).time;
for (i=0; i < mc.numChildren; i++)
{
my_mc.getChildAt(i).x -= speed * (currentTime - lastTime); // speed is in px/ms
}
lastTime = currentTime;
where you have the variable lastTime declared somewhere in a persistent scope:
var lastTime:Number = (new Date()).time;
I don't know if this addresses what you are calling "shaking", but it's at least something to consider.
I want to draw a 3D ball or sphere in HTML 5.0 canvas. I want to understand the Algorithm about how to draw a 3D sphere. Who can share this with me?
You will need to model a sphere, and have it be varying colors so that as it rotates you can see that it is not only a sphere, but being rendered.
Otherwise, a sphere in space, with not point of reference around it looks like a circle, if it is all one solid color.
To start with you will want to try drawing a circle with rectangles, as that is the main primitive you have.
Once you understand how to do that, or create a new primitive, such as a triangle, using the Path method, and create a circle, then you are ready to move it to 3D.
3D is just a trick, as you will take your model, probably generated by an equation, and then flatten it, as you determine which parts will be seen, and then display it.
But, you will want to change the color of the triangles based on how far they are from a source of light, as well as based on the angle of that part to the light source.
This is where you can start to do optimizations, as, if you do this pixel by pixel then you are raytracing. If you have larger blocks, and a point source of light, and the object is rotating but not moving around then you can recalculate how the color changes for each triangle, then it is just a matter of changing colors to simulate rotating.
The algorithm will depend on what simplifications you want to make, so as you gain experience come back and ask, showing what you have done so far.
Here is an example of doing it, and below I copied the 3D sphere part, but please look at the entire article.
function Sphere3D(radius) {
this.point = new Array();
this.color = "rgb(100,0,255)"
this.radius = (typeof(radius) == "undefined") ? 20.0 : radius;
this.radius = (typeof(radius) != "number") ? 20.0 : radius;
this.numberOfVertexes = 0;
// Loop from 0 to 360 degrees with a pitch of 10 degrees ...
for(alpha = 0; alpha <= 6.28; alpha += 0.17) {
p = this.point[this.numberOfVertexes] = new Point3D();
p.x = Math.cos(alpha) * this.radius;
p.y = 0;
p.z = Math.sin(alpha) * this.radius;
this.numberOfVertexes++;
}
// Loop from 0 to 90 degrees with a pitch of 10 degrees ...
// (direction = 1)
// Loop from 0 to 90 degrees with a pitch of 10 degrees ...
// (direction = -1)
for(var direction = 1; direction >= -1; direction -= 2) {
for(var beta = 0.17; beta < 1.445; beta += 0.17) {
var radius = Math.cos(beta) * this.radius;
var fixedY = Math.sin(beta) * this.radius * direction;
for(var alpha = 0; alpha < 6.28; alpha += 0.17) {
p = this.point[this.numberOfVertexes] = new Point3D();
p.x = Math.cos(alpha) * radius;
p.y = fixedY;
p.z = Math.sin(alpha) * radius;
this.numberOfVertexes++;
}
}
}
}
u can try with three.js library , which abstracts a lot of code from core webgl programming. Include three.js library in your html from three.js lib.
u can use canvas renderer for safari browser , webgl works for chrome
please find the JS FIDDLE FOR SPHERE
var camera, scene, material, mesh, geometry, renderer
function drawSphere() {
init();
animate();
}
function init() {
// camera
scene = new THREE.Scene()
camera = new THREE.PerspectiveCamera(50, window.innerWidth / innerHeight, 1, 1000);
camera.position.z = 300;
scene.add(camera);
// sphere object
var radius = 50,
segments = 10,
rings = 10;
geometry = new THREE.SphereGeometry(radius, segments, rings);
material = new THREE.MeshNormalMaterial({
color: 0x002288
});
mesh = new THREE.Mesh(geometry, material);
//scene
;
scene.add(mesh);
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
mesh.rotation.x += .01;
mesh.rotation.y += .02;
renderer.render(scene, camera);
}
// fn callin
drawSphere();
Update: This code is quite old and limited. There are libraries for doing 3D spheres now: http://techslides.com/d3-globe-with-canvas-webgl-and-three-js/
Over ten years ago I wrote a Java applet to render a textured sphere by actually doing the math to work out where the surface of the sphere was in the scene (not using triangles).
I've rewritten it in JavaScript for canvas and I've got a demo rendering the earth as a sphere:
(source: haslers.info)
I get around 22 fps on my machine. Which is about as fast as the Java version it was based on renders at, if not a little faster!
Now it's a long time since I wrote the Java code - and it was quite obtuse - so I don't really remember exactly how it works, I've just ported it JavaScript. However this is from a slow version of the code and I'm not sure if the faster version was due to optimisations in the Java methods I used to manipulate pixels or from speedups in the math it does to work out which pixel to render from the texture. I was also corresponding at the time with someone who had a similar applet that was much faster than mine but again I don't know if any of the speed improvements they had would be possible in JavaScript as it may have relied on Java libraries. (I never saw their code so I don't know how they did it.)
So it may be possible to improve on the speed. But this works well as a proof of concept.
I'll have a go at converting my faster version some time to see if I can get any speed improvements into the JavaScript version.
Well, an image of a sphere will always have a circular shape on your screen, so the only thing that matters is the shading. This will be determined by where you place your light source.
As for algorithms, ray tracing is the simplest, but also the slowest by far — so you probably wouldn't want to use it to do anything very complicated in a <CANVAS> (especially given the lack of graphics acceleration available in that environment), but it might be fast enough if you just wanted to do a single sphere.