I am trying to enable smooth pinch to zoom on mobile devices on my fabric.js canvas. I have a solution that technically works, the problem is that it is very stuttery, the zoom happens in distinct stages, almost frame by frame, so it is not a good experience. It also seems to get worse as time goes on. Is there any way I can make it stutter less and scale more smoothly? I am testing on a good smartphone.
Here is the code:
canvas.on('touch:gesture',function(event) {
if (event.e.touches && event.e.touches.length == 2) {
if (event.self.state == "start") {
zoomStartScale = self.canvas.getZoom();
}
// Calculate delta from start scale
var delta = zoomStartScale * event.self.scale;
// Zoom to pinch point
self.canvas.setZoom(delta);
}
})
Related
I need a Phaser 3 sprite to fall and bounce vertically according to gravity. I also want the body to decrease speed horizontally when the player releases the controller.
But it seems that gravity and friction don't work well together...?
As soon as I add damping and drag, gravity gets screwed up completely. Either the sprite falls very very slowly, or gravity is just removed completely.
How do I combine horizontal drag with vertical gravity?
Physics settings
this.body.setBounce(1, 1)
this.body.allowGravity = true // only works without drag/damping
this.body.allowDrag = true
this.body.useDamping = true
this.body.setDrag(0.88, 0.95) // x drag and y drag
Controls
if (this.cursors.left.isDown) {
this.body.setVelocityX(-300)
}
else if (this.cursors.right.isDown) {
this.body.setVelocityX(300)
}
if (this.cursors.up.isDown) {
this.body.setVelocityY(-300)
}
It seems like your drag numbers might just be too small. At 0.95 and 60fps I think you're going to come to a complete stop in .5 seconds. Try 0.99 or 0.999 and work your way down from there.
I need to build a kind of map in canvas, which must be able to hold more than 10.000 elements and thus has quiet big dimensions in some cases (> 8000px width, >4000 px height). Also I need to pan and zoom the map.
After some fiddeling around with existing libraries (Paper.js) and possible other solutions (Leaflet Map) I eventually wrote an own library from scratch, because the main requirement is, that is should be really really fast (loading, mouseovers, ...) and none of the libraries I tried could offer all of the aspects.
The structure is as follows:
I have one map object with an associated Control object, which registers events and has resize methods etc.
A map is divided in mutliple even sized tiles (1024px x 1024px - customizable) because using the map with only one canvas at a size over 8000px width made it incredibly slow
Each tile is associated with a canvas
The elements (just circles) are added to one or multiple tiles (If it's on the edge) - more specifically to the tiles' canvas.
The tiles are placed within an container div which has the dimensions of the map area (when not zoomed out)
The container div is placed within a viewport div to enable the map being displayed as a "widget"
Zooming scales every tile/canvas and the container. For sake of performance I sacrificed smooth zoom and implemented a customizable amount of zoom steps, which still feels okay.
Panning set's the topand left style of the container.
Events used are window.resize, mousewheel, DOMMouseScrol, mousedown, mouseup, mousemove, touchstart,touchend,touchmove and Hammertime pinch
This alltogether runs satisfying on Desktop Browsers, and iPhones (tested with SE, 6S) but on every Android device I tested it (Samsung S4, One Plus One and another 1 year old device, and android studio emulator) it runs extremly slow. Drawing of the Map is fine in speed, but zooming and panning is near to impossible.
The code is too comprehensive to post it here, so I'm asking you if there are any known problems with canvas on android, that could explain this problem, or maybe some issues with the way I built the structure that could produce issues with android. I'm really clueless here, since it works on desktop and iPhone.
The real problem you're hitting is you're overloading the GPU. Loading that much data all and once then moving it around is going to put a toll on the GPU and likely force the browser into software rendering mode, which is a big performance hit.
Instead, I'd suggest changing your approach. Rather than having various large canvases, you should have one canvas that is, at most, the size of the users screen. Then, utilize methods of the canvas API such as scale and translate to render what you need. For an added bonus, avoid trying to render things which are off screen.
It may seem like having to redraw the scene every time you move around would be slow but it's not. The reality is that either you specify exactly what needs to be drawn or the browser has to attempt to draw all of it again when you shift it around. Here's a brief example of how you can render and move large images.
var ctx = document.querySelector('canvas').getContext('2d');
var img = new Image();
img.src = 'https://placeimg.com/1000/1000/nature';
img.onload = start;
function start() {
var xDirection = -1;
var yDirection = -1;
var xPosition = 0;
var yPosition = 0;
var prev = Date.now();
(function render() {
var now = Date.now();
var delta = (now - prev) / 1000;
xPosition += xDirection * delta * 20;
yPosition += yDirection * delta * 40;
if (xPosition > 0) {
xPosition = 0;
xDirection *= -1;
} else if (xPosition < -320) {
xPosition = -320;
xDirection *= -1;
}
if (yPosition > 0) {
yPosition = 0;
yDirection *= -1;
} else if (yPosition < -240) {
yPosition = -240;
yDirection *= -1;
}
prev = now;
ctx.save();
ctx.translate(xPosition, yPosition);
ctx.drawImage(img, 0, 0);
ctx.restore();
requestAnimationFrame(render);
})();
}
body {
background: #111;
}
canvas {
background: #FFF;
}
<canvas width="320" height="240"></canvas>
I'm working on a 2d canvas-based app using EaselJS where the user can move indefinitely on the xy-plane by dragging the background. Think google maps, except the background is a repeating tile.
The code for the movement is very simple and looks something like this:
// container - the createjs.Container being panned
// background - a createjs.Shape child of container, on which the
// background is drawn
background.onPress = function(evt) {
var x = evt.stageX, y = evt.stageY;
evt.onMouseMove = function(evt) {
// the canvas is positioned in the center of the window, so the apparent
// movement works by changing the registration point of the container in
// the opposite direction.
container.regX -= evt.stageX - x;
container.regY -= evt.stageY - y;
x = evt.stageX;
y = evt.stageY;
stage.update();
};
evt.onMouseUp = function(evt) {
// Here the background would be redrawn based on the new container coords.
// However the issue occurs even before the mouse is released.
background.redraw();
stage.update();
};
};
All works as expected until reaching 32678px (2^15) on either axis. What occurs is different in different browsers, but the point where it first happens is the same.
In Firefox, it will suddenly shift a large chunk of pixels (~100) rather than 1. It will then happen again at 65538 (2^16+2), perhaps more after that, but I haven't witnessed it. After the trouble points, the drag will continue smoothly, as expected, but remaining shifted.
In Chrome, the effect is more dramatic. The drawing breaks and results in repeated ~100px wide "stripes" of the background across the page at 32768, and does not correct itself on redraw.
Oddly, the numbers in EaselJS don't reflect the issue. The only change in the container's transform matrix is the tx or ty incrementing by 1. No other matrices change. It seems as though EaselJS is getting all the numbers right.
Can anyone shed any light this issue?
Edit:
I worked around this problem by redrawing parts of the container using a calculated regX/regY, rather than attempting to translate very large regX/regY coords on the canvas.
This question may be related
What is the maximum size for an HTML canvas?
From what I gather browsers use short int to store canvas sizes with 32,767 being the maximum possible value.
Links possibly related to your issue,
Mozilla - HTML5 Canvas slow/out of memory
https://stackoverflow.com/a/12535564/149636
I've been working on a simulation that's pretty bloody huge, which meant it became necesary to zoom in and out.
I found a good working script but when the simulation got bigger I found it had a problem...
The more you zoom in, the smaller the steps became and the further you zoom out, the bigger.
This is a problem because I need the steps to be equal no matter what zoom level you are.
The script I'm using now is:
var total = 75,
delta = e.detail ? e.detail * -120 : e.wheelDelta; //e is mouse event
this.zoomValue += (delta > 0) ? 1 : -1;
this.zoomRatio = 1 + (this.zoomValue / total) * 2;
e.preventDefault();
You can see it in action here: http://clanpvp.com/sol (if it's not working, I'm either fiddling with it or you are using Internet Explorer)
I use the zoomRatio later to calculate the positions of various objects.
Who knows of a better way to zoom in?
The anwser is very simple actualy... yet I needed somebody to show me before I saw it myself.
The solution is to have it zoom exponentialy, so
this.zoomRatio = Math.exp(this.zoomValue);
Does the trick, you can do something like
this.zoomRatio = Math.exp(this.zoomValue / 10);
To make it scroll in smaller 'steps'.
There doesn't seem to be a good way to base the <canvas> viewport on the location of the mouse pointer and being able to move around freely. Basically, like every other first-person game on the market.
There is no way to capture the mouse
inside a <canvas> element.
There is no way to set the position
of the mouse pointer.
It is not possible to go full screen
with <canvas>, and even if, once the edge has been reached, functionality will be broken.
For good reasons, too. Imagine what possible scenarios could (and definitely would) be employed by malicious persons.
Perhaps it's too early to be thinking of something that is almost only of any use in a 3D environment, something that there isn't yet a spec for.
What's your take or solution?
You can get the mouse position inside of a canvas.
function getCursorPosition(e) {
var x;
var y;
if (e.pageX != undefined && e.pageY != undefined) {
x = e.pageX;
y = e.pageY;
}
else {
x = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;
var cell = new Cell(Math.floor(y/kPieceHeight),
Math.floor(x/kPieceWidth));
return cell;
}
From Dive Into HTML5: Let's Call it a Draw(ing Surface)
I don't think there is a good solution for this -- at least, not until we get mouse locking. No matter how elegant your solution, if you make a twitchy mouselook driven game, the user is going to twitch outside the canvas area at some point. Even if they don't accidentally click a link, their immersion will be broken when the view stops responding to their mouse.
For slower paced games, you could:
Use click and drag to turn. Once user starts dragging within the canvas, you use the mouse delta from the point where they started dragging to determine how far to turn. Because the user is holding down the button, they won't accidentally click things.
Hover cursor near the edges of the canvas to turn, similar to an RTS. This would turn more slowly, but is probably the most intuitive, and easiest for a user to accidentally discover.
Use the keyboard to look, like pre-mouse FPS games (such as Doom).
It's worth noting that there is an open feature request in Firefox for mouse locking. But, unfortunately, neither this, nor mouse hiding or fullscreen are part of the WebGL spec.
All of these features are supported by Unity, so that may be a path to look at if you really need FPS controls.