I have a canvas with the following size: 500x200. Inside this canvas i'm drawing some number of blocks (actually - table cells). Information about how much blocks i should draw i'm getting via AJAX, but size for every cell is fixed - 100x50. So, i can display inside my canvas only 5 blocks horizontally and 4 vertically. But what about other blocks? What if script return a table 30x30 cells. How can i side scroll (mouse preferred) my canvas so user can the rest of the cells (no zoom out, only scrolling).
If you need any more information, please, tell me and i will provide it.
Thank you.
The easiest way to accomplish this is to implement mouse-panning.
On the mouse down event, begin panning and save the mouse position
On the mouse move event, translate the context (ctx.translate(x,y)) by the difference between the current mouse position and the original position, then redraw the scene.
On the mouse up event, stop panning.
There are harder ways. You could implement scrollbars inside the canvas, as Mozilla Bespin has done (...which became Mozilla Skywriter which then merged with Ace and dropped all Canvas use). The code that they used was pretty good.
Or you could implement DOM scrollbars for use with your canvas, which isn't exactly easy to get right in all cases. This involves adding several dummy divs in order to give the appearance and function of real scrollbars. I have done this but the code remains unreleased for now. But thats no reason you can't give it a try if thats what you really want.
Check out a great tutorial at: http://www.brighthub.com/hubfolio/matthew-casperson/blog/archive/2009/06/29/game-development-with-javascript-and-the-canvas-element.aspx
It will give you an answer to your question and much much more...
I'm with Simon Sarris on this, but as an alternative, you could clone the canvas, and replace it with a blank canvas, and then render the original canvas as an image. I've some MooTools js that goes like this, which is fine for my use, by ymmv:
var destinationCanvas = this.canvas.clone()l
destinationCanvas.cloneEvents( this.canvas, 'mousemove');
var destCtx = destinationCanvas.getContext('2d');
destCtx.drawImage(
this.canvas,
(this.options.scrollPx)*-1,
0
);
destinationCanvas.replaces( this.canvas );
this.canvas.destroy();
this.canvas = destinationCanvas;
this.ctx = destCtx; // this.canvas.getContext('2d');
Related
Firstly, thanks to whoever developed and help maintain svg-pan-zoom. It's been really helpful. Big props :)
Anyway, I have a quick question to the svg-pan-zoom community. Is it at all possible to use svg-pan-zoom on a svg that has a HTML div inside of it (which will be inside a foreignObject)?
I need a HTML element (a form specifically) to 'follow' the movements of this svg when it's being panned and zoomed around. Its position relative to the svg elements shouldn't change, it has to move with them.
I have tried doing it completely separately (separate code to control the panning of the HTML form) but it's very gimmicky and I ran into some technical issues I'd rather not deal with. (For example, moving the div doesn't move the svg with it.) I'd really appreciate it if someone could tell me if svg-pan-zoom supports foreignObjects, and what to do to move HTML elements with the svg.
Thanks in advance.
To follow up, I ended up just disabling the default zoom and pan options, and then wrote a function to pan both the svg and the form element so they'd move together.
(Some of this code was inspired by another Stack Exchange answer that I looked up briefly and can no longer find. I'd reference it later if I remember.)
Like most solutions, solving the problem this way brought up more issues. For instance, the form moves around perfectly with the mouse, but for some reason the function panBy(xpixel, ypixel) pans by way too much (a tiny mouse movement throws it out of the page.)
I guess this is my solution, though it has that issue. I'd been stuck on this so long and would appreciate it a ton of someone can help me figure out why panBy is acting weird.
// Pan when mouse is dragged
$(window).on('mousedown', function(event){
var initClick = {
x : event.pageX,
y : event.pageY
};
var initPosition = $('#form1').offset();
var handlers = {
mousemove : function(event){
panZoom.panBy({x: event.pageX - initClick.x, y: event.pageY - initClick.y});
$('#form1').css({"top": initPosition.top + (event.pageY - initClick.y),
"left": initPosition.left + (event.pageX - initClick.x)});
},
mouseup : function(event){
$(this).off(handlers);
}
};
$(document).on(handlers);
});
The idea is simple, create a star with text and rotate it.
But its not smooth after making a quick script
here is my fiddle
The star is moving oke, but the text is shaking like a snake :)
Cause
The reason why this happening is due to the browser's text rendering engine. They are translating each point in the text path using the same rotation matrix but due to the engine you will get rounding errors in there causing the text to "jibber".
This is not only happening for canvas but for CSS transformations as well, with text.
Solution
Simply render the text to a canvas "layer" and use that as an image which you rotate instead of the text itself. This rasterizes the text in its normal position and the transformation happens on the bitmap instead which most browsers handle pretty well.
Here is one example integrating the answer I linked to in the comments. I'm showing only the main text as it works as a comparer as well, before and after:
// canvas layer
var tcanvas = document.createElement('canvas'); //tcanvas must be in global scope
var tctx = tcanvas.getContext('2d');
tcanvas.width = canvas.width;
tcanvas.height = canvas.height;
tctx.translate(250, 250);
tctx.fillStyle = "black";
tctx.font = "bold 60px Arial";
tctx.textAlign = 'center';
tctx.fillText('€ 1215,34', 0, 0);
Now the layer is ready and we can replace the text drawing methods with a single drawImage instead:
c.drawImage(tcanvas, -x, -y);
Result of this modification
To draw the "extra" just move those lines down to the layer creation as well. Note that tcanvas in the example must be accessible globally.
If the rotation speed of the text is not intentional just remove the second call to rotate you have there before rendering the text.
Tip: instead of redrawing gradients and the star just render it once to another layer and rotate that as an image as well. This will be much more efficient. You could also avoid save/restore (which are relative costly) by just accumulating the step on rotate() itself and use setTransform to transform the matrix using absolute values.
Ways to optimize memory usage: for text layer use a canvas size where the text fits in exact (don't use fixed values as text size may vary in size and position from browser to browser depending on the text rendering engine), same for star if you go with a layer for that as well.
Hope this helps!
The shake in your text comes from the fact that the context is not in a proper state when you draw the text : you just did quite some operations on it before.
I just added a
c.restore();
c.save();
c.translate(x,y);
before the text part of your code, and the text is solidly hung to the star now :
http://jsfiddle.net/gamealchemist/xUr4f/1/
Edit : There are in fact 2 issues at stake here : 1) the text rotation is not quite on track with the star and 2) the rounding of the text coordinates makes the text shake.
Both Chrome and FF exhibit 1) of course, and with a clean context 1) disappear on both.
For 2) : Chrome is ok with non-integer coordinates text, but FF does round, which creates a shake on a rotating text.
Only solution i see it to 'print' the text on a canvas, then have the canvas rotate. I did it for the 'extra' in this fiddle :
http://jsfiddle.net/xUr4f/4/
There's a loss of quality compared to the fillText, but unless the coordinates rounding can be avoided, it seems as the best solution.
Is it possible to only trigger a div's mouseover when the cursor is over an opaque part of the div's background image? Perhaps via Javascript?
All I can find with Google are old IE PNG fixes.
This looks like a similar question to this one: Hit detection on non-transparent pixel
I suppose this could also be done for background image by getting the attribute with jQuery:
$('#myDiv').css('background-image');
I haven't personally done this, but it seems like a viable solution. This will only work for modern browsers, but you should be able to make it back-compatible with excanvas.
It is possible, just not very easily. You'll have to use a lot of Javascript.
You'd want to attach to your <div>'s onmousemove event, which returns the X,Y coordinates of the cursor. Your event handler function would then test to see if the cursor is in the correct place in order to trigger an alternative onmouseover event.
Implementing the "is the cursor over an opaque pixel or not?" test can be done two ways: the first is to create a simple mathematical expression (say if the opaque parts of the image make neat rectangles, circles or polygons). The more difficult (and less browser-supported) way is to load the background image into a Canvas object and then get the current pixel value's opacity figure and take it from there, like so:
var pixel = canvas.getImageData(x, y, 1, 1).data;
var alpha = pixel[3]; // assuming RGBA
if( alpha > threshold ) onMouseOver(); // raise the event
Another alternative is to create an entirely transparent div (or some other element) positioned and sized so that it only covers the opaque part of the div below, then just test the mouseover of that element's box.
It's a bit of tweaking but why don't you add a class to your opaque div, and use JavaScript to check for it?
In jQuery:
$('div').mouseover(function(){
if ($(this).is('.opaque')) {
//Some actions
}
});
I'm wonderring if anyone can help me with making sure my... uhhh ... Z-index (bad pun, you'll see why in a moment) is in the wrong order. I've been doing this for a few hours straight now and my eyes are going buggy - but - maybe leaving a question on Stack overnight will help push this in the right direction.
I've been working on the code for https://github.com/AlexChesser/jsSprite and I'm as far as the 6th test. Use the W key to run, A and D to turn left and right: http://chesser.ca/jsSprite/06-brainnsss....php (Gettit? Z-Index?! Hilarious).
Anyways, You'll notice that if you run around the screen for a bit. the individual Zombies' white squares / dirty rectangles overlap the other zombies' squares. When working with multiple overlapping sprites, how does one go about making sure they all get drawn without upsetting any of the other sprites?
(You see z is for zombies, but z index like when you're dealing with overlapping in CSS - probably way funnier when you've been coding for a number of hours straight).
Thanks for your
Brainsss......
It's not a z-index issue, your zombies themselves are okay.
Your problem is really with the second line of drawFrame
drawFrame: function(){
Sprite.ctx.clearRect(0,0,Sprite.width,Sprite.height); //clear previous frame
// I am trouble:
MainContext.clearRect(Sprite.Xpos, Sprite.Ypos, Sprite.width, Sprite.height);
It is clearing a rectangle of the main canvas where the zombie once was every time you draw a zombie, which can affect nearby objects!
So instead you should be clearing the entire canvas each time.
Try commenting out the MainContext.clearRect in drawFrame, and instead add one in runloop like below. It should fix your problems.
runloop = function(m) {
// New clear put here!
MainContext.clearRect(0,0,canvas.width,canvas.height);
m.drawFrame();
for (Z in Zarr) { // For ZOMBIE in "Zombie Array" Aaaaarrrgghhh...
Zarr[Z].pointTo(m);
Zarr[Z].drawFrame();
MainContext.drawImage(Zarr[Z].canvas, Zarr[Z].Xpos, Zarr[Z].Ypos);
};
MainContext.drawImage(m.canvas, m.Xpos, m.Ypos);
};
How about sorting your array (Zarr) by the y coordinate Ypos of each zombie before rendering? Or are you getting at the problem with (a lack of) transparency?
I'm using a flip mechanism to navigate through my site (flip file & demo). The problem is, once it's flipped the content been displayed good just like I want it, but there's some offset from the flipped (right) parts en the solid left part (visible when you look closely). Also the right part is now a little blurred (which is the disturbing part of my issue). This all caused by the flip (I think the rotationY is causing the problem).
When I click a button I do the following:
flip=new Flip(currentPage,nextPage,richting);
content.addChild(flip);
currentPage=nextPage;
nextPage = new MovieClip();
there is a fix for it, consider the following:
// store original matrix
var origMatrix:Matrix = box.transform.matrix;
// set initial position
box.rotationY = -180;
// start animation
TweenLite.to(box, 1, {rotationY:0, onComplete:cleanBlur})
// execute after animation complete
function cleanBlur():void {
box.transform.matrix = origMatrix;
}
maybe you can find better results using other 3d library.
EDIT: sorry the "box" object, I was testing in flash, but box would be any of your pages to flip. Just apply the same logic.
Matteo at Flash & Math has an excellent solution for this. He actually found that when you bring an object into native 3D space it expands the object by one pixel in both the width and height. This can be counteracted by scaling your object back and then setting it's z to 0 which will scale it back up. Now the object is ready to play with without the blur.
http://www.flashandmath.com/flashcs4/blursol/index.html
adding: This fixes the scale issue, but not the blurriness. You will still need to use the matrix transformation fix posted above.