I am trying to create an image manipulation application that has two fundamental layers. The user can 'rotate' between the main and secondary layers which exist within the same location in the window. To accomplish this, I have code like this:
<div ng-hide="layer != 'main'">
<canvas id="main-layer"></canvas>
</div>
<div ng-hide="layer != 'secondary'">
<canvas id="secondary-layer"></canvas>
</div>
The Angular code shouldn't detract from the question here. Whenever the main layer is selected, the secondary layer's CSS display attribute is set to none
display: none !important
The problem is that the selection mechamism of FabricJS does not work on the secondary layer. When I try to either object:selected or mouse:down on an object, nothing happens. The blue mouse dragging selection that happens by default is also not there.
Strangely enough, if I swap the divs containing the canvas, selection works on the secondary layer and 'sort of' works on the main layer (it is 'off' from where the mouse is).
<div ng-hide="layer != 'secondary'">
<canvas id="secondary-layer"></canvas>
</div>
<div ng-hide="layer != 'main'">
<canvas id="main-layer"></canvas>
</div>
Is there a way to actually stack canvases in FabricJS?
UPDATE:
I noticed that if I move to the secondary canvas and summon the Chrome web dev tools, the canvas works perfectly. On the other hand, if I rotate to the main canvas, that one does not work until I summon the Chrome web dev tools. Strange.
I found that that this was actually an AngularJS issue, not a FabricJS issue. The problem stems from the fact that whenever I rotate from one layer to the other, I call calcOffset() immediately without waiting for the scope to properly propagate to the DOM. This means that FabricJS has no idea that the canvas is there. I solved this issue by essentially waiting till the scope had propagated by adding:
$timeout(function() { scope.canvas.calcOffset(); }, 0);
Related
I'm currently working on a website, which has a page with 2 big images (combined around 9000px) in them. The idea is that people can click and drag, to see the sides of the image, just like google maps.
At first I built it with just the 2 images next to each other. When animating the parent div on drag, I managed to only get a composite paint once in a while, but no actual paint (draws). I did this by editing the translate3d on the mouse events.
This all worked like a charm and very smooth, until I added an extra div to the parent. Now when I change the translate3d, it has to do a complete recalculate style -> layout -> paint -> composite layers.
So at first the structure was:
<div class="container" style="transform: translate3d(-50%)">
<img src="path/to/img.jpg"><img src="path/to/img.jpg">
</div>
When changing the translate3d, all was good, just had a Composite Layers once in a while.
However, when I change it to the following, all goes bananas:
<div class="container" style="transform: translate3d(-50%)">
<img src="path/to/img.jpg"><img src="path/to/img.jpg">
<div>+</div>
</div>
So like said before, now when editing translate3d, it has to do a complete Recalculate style.
I made sure the div and images both had layers by adding translateZ(0) on them, that didn't help though :(
I'm pretty sure I don't understand the whole paint sequence of chrome to the detail, but after countless hours of reading I can't find the exact thing. I hope someone can shed some light on this matter.
Thanks a bunch!
Edit after Paul's reply:
The Recalculate Style is triggered by the following line:
this.background.style.transform = 'translate3d(' + (this.panoramaX - this.previewOffset) + '%, 0, 0)';
I need to show a popup layer on a scene, creating a semi-transparent background layer that will also prevent touch events propagation. I am using the latest cocos2d-x v. 3.0-alpha-0.
What I want to achieve is a popup layer that fully handles touches (eg. buttons, menu items, scroll views, etc.), laying on a background layer (for design purposes), that covers the current scene. All items in the scene should not respond to touches any more.
Is this achievable using the new EventDispatcher class? I've been able to disable all touches to the main scene, but all instances of MenuItem that live in the scene are still touchable and active.
How can I achieve this? And, also, how can I create a touch listener that prevents all touches to the main scene but not to the popup?
You can disable menu items by setting setDisable property of menuitems to false.
Example
_menuItem->setEnabled(false);
For Layers use setTouchEnabled property
_backGroungLayer->setTouchEnabled(false);
Make sure that popup layer is not child of layer you want to disable.
To disable all items in menus do this
Suppose _menu contain various menuitems.
CCARRAY_FOREACH(_menu->getChildren(), item)
{
item.isEnabled=NO;
}
if you want to disable selected items just give them tags.There is no need to make any list.
I had the same problem and solved it with mm. It was dirty, but it worked:
Create a button using ccui.button.
Set the button size to your screen size.
Add this button as a background to your popup layer.
This will prevent any thing behind it being clicked.
By default, all CCMenu's have a set priority (kCCMenuHandlerPriority = -128) in cocos2d 2.1. So in a class (usually a CCNode descendant) that wants to swallow everything and preempt anything i do like in this dialog sequencer example below :
- (void)onEnter {
backdrop_.visible = self.isBackDropShown;
MPLOG(#"Adding self as a swallower touch delegate, above the rest of the planet.");
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:_dialogTouchPriority swallowsTouches:YES];
for (CCMenu *mn in _menus) {
mn.touchPriotity = _dialogTouchPriority -1 ;
}
[super onEnter];
}
where _dialogTouchPriority is kCCMenuHandlerPriority-1 by default. It will be served before everything 'below'. It is a bad hack (cocos2d internals could be changed and break this), i know , but bullet proof. Use carefully, make certain you only ever have one of these in your scene.
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 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');
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.