FabricJS Canvas, Scrolling parent container moves child hit area - scroll

I am using FabricJS to create an application. I am finding that scrolling a parent div/container offsets the selectable area of an object to the right in direct relation to amount scrolled.
So, if I have a canvas that is 1200x600 and a container div that is 600x600 and I add a rect to that canvas at 400, 120; when I scroll 200px, I can't click on the rect to select it. Rather, I have to move my mouse to 600, 120 (empty space) to get the cross bar and select the rect.
Not sure if this is known, or has a work around - but I would appreciate any help possible.

You'll have to modify FabricJs code to make it work.
The problem is in the getPointer function, if you search for it in all.js you'll notice the comment "this method needs fixing" from kangax.
A workaround can be substituting this function with
function getPointer(event) {
// TODO (kangax): this method needs fixing
return { x: pointerX(event) + document.getElementById("container").scrollLeft, y: pointerY(event) + document.getElementById("container").scrollTop };
}
where "container" is the wrapper div of you canvas. It's not nice, since you have to put the exact id, but it works.
Hope this helps.

Related

Scroll one page to the right in Cypress

How could I scroll one page to the right in Cypress?
E.g. I have a horizontally scrollable area. The area contains elements a, b and if I scroll to the right a and b get destroyed, while c and d get added.
My attempt is to find out the width of the scrollable view and then scroll that amount to the right. But the width does not get retrieved before the scrollTo gets called.
Here is what I am trying to do:
const width = cy.getCy("scroll-viewport").invoke("width");
cy.getCy("scroll-viewport").scrollTo(width, 0);
Your code sample has some mystery about it, for example what is cy.getCy().
I can show you how to do it with standard code, and you can adapt from there.
Assuming scroll-viewport is a selector for the scroll container (the owner of the scroll bar)
cy.get("scroll-viewport").then($scrollContainer => {
const width = $scrollContainer[0].offsetWidth;
$scrollContainer[0].scrollTo(width, 0);
})
Note $scrollContainer[0].scrollWidth gives you the total width after scrolling, in case you need that.

Scrollable drawing in Gtk::Layout

I'd like to use custom drawing within a Gtk::Layout. That is, I'm using the C++ bindings for Gtk3 (GTKmm 3.14.0), and I have embedded widgets placed on the "canvas", on top of my custom drawing. Basically this works just fine.
Now the problem is related to scrolling. Gtk::Layout can be placed into a Gtk::ScrolledWindow, and when the scrollable area is set to something larger than the visible allocation, scrollbars will show up. Unfortunately, those scrollbars influence only the placement of the embedded widgets, while my custom drawing remains at a fixed position within the window.
This means, both the Gtk::Allocation and the cairo context seem to be related to precisely the visible area, not to the extended virtual "canvas". I could work around that problem by accessing the adjustments from the scrollbars and then translate the cairo context accordingly...
My question is:
is this the proper way to handle such a scrollable drawing?
or is there some way to let the framework do this work for me?
Judging from the source code of gtk+3.0-3.14.5 (which is in Debian/Stable), the Gtk::Layout does nothing to adjust the drawing context. It just invokes the inherited draw() function from GtkWidget. On the other hand, Gtk::Layout is a full-blown container (it inherits from Gtk::Container), and it is scrollable, which together means that it handles gtk_layout_size_allocate() by passing a suitable allocation (screen area) to each of the embedded child widgets -- and in this respect it does handle the moving and clipping related to scrolling the virtual canvas (calls gdk_window_move_resize()).
Thus, if we want to combine the embedded child widgets with custom drawing, we need to bridge this discrepancy manually. This is quite easy actually: all we need to do is to look into the Gtk::Adjusments corresponding to the scrollbars. Because the value of these adjusments is precisely the upper left corner of the visible viewport. Now, if we want our custom drawing to use absolute canvas coordinates, we just have to translate() the given Cairo context. Beware: it is important to save() the state and to restore() it to pristine state when done, otherwise those translations will accumulate.
Here is some example code to demonstrate this custom drawing
we derive a custom container class called Canvas from Gtk::Layout
we override the on_draw() handler, because only there all size allocation to embedded child widgets have been processed
Layering: child widgets are always drawn in the order they have been added to the Gtk::Layout container. Any custom drawing done before invoking the inherited on_draw() function will be below those widgets; any drawing done afterwards will happen on top of them.
if necessary, we can use the foreach(callback) mechanism to visit all child widgets to find out their current position and extension
void
Canvas::determineExtension()
{
if (not recalcExtension_) return;
uint extH=20, extV=20;
Gtk::Container::ForeachSlot callback
= [&](Gtk::Widget& chld)
{
auto alloc = chld.get_allocation();
uint x = alloc.get_x();
uint y = alloc.get_y();
x += alloc.get_width();
y += alloc.get_height();
extH = max (extH, x);
extV = max (extV, y);
};
foreach(callback);
recalcExtension_ = false;
set_size (extH, extV); // define extension of the virtual canvas
}
bool
Canvas::on_draw(Cairo::RefPtr<Cairo::Context> const& cox)
{
if (shallDraw_)
{
uint extH, extV;
determineExtension();
get_size (extH, extV);
auto adjH = get_hadjustment();
auto adjV = get_vadjustment();
double offH = adjH->get_value();
double offV = adjV->get_value();
cox->save();
cox->translate(-offH, -offV);
// draw red diagonal line
cox->set_source_rgb(0.8, 0.0, 0.0);
cox->set_line_width (10.0);
cox->move_to(0, 0);
cox->line_to(extH, extV);
cox->stroke();
cox->restore();
// cause child widgets to be redrawn
bool event_is_handled = Gtk::Layout::on_draw(cox);
// any drawing which follows happens on top of child widgets...
cox->save();
cox->translate(-offH, -offV);
cox->set_source_rgb(0.2, 0.4, 0.9);
cox->set_line_width (2.0);
cox->rectangle(0,0, extH, extV);
cox->stroke();
cox->restore();
return event_is_handled;
}
else
return Gtk::Layout::on_draw(cox);
}

HTML Canvas clip area - Context restore?

Am trying to set a "dirty zone" on my canvas to prevent the repainting of unmoved items (background image, static items, etc.)
i.e. only the background painted behind a moving player needs to be redrawn
EDIT: As suggested, here's the jsfiddle of it
http://jsfiddle.net/7kbzj/3/
The "update" method doesn't work out there, so it's moveSprite() you can get run by clicking the "move sprite" link... Basically, the clipping zone shouldmove by 10px to the right each time you click. Clipping mask stays at initial position, only the re-paint occurs. Weird o_O
So as I init my canvas, once the background is painted, set I use the ctx.save() method:
function init() {
canvas = document.getElementById('kCanvas');
ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(0,128,0)";
ctx.fillRect (0,0,320,240);
ctx.save();
setInterval(function () { update(); }, tpf);
}
In order to see the clipping works, I draw a different color background (blue one) in the area that I wanted clipped... the result is bad, only the first clipped area is painted blue :(
function update() {
setDirtyArea(x,y,w+1,h)
ctx.fillStyle = "rgb(0,0,128)";
ctx.fillRect (0,0,320,240);
x++;
// paint image
ctx.clearRect(x,y,w,h);
ctx.drawImage(imageObj, x, y);
}
function setDirtyArea(x,y,w,h) {
ctx.restore();
// define new dirty zone
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
}
I'd love to se the blue zone propagate itself towards the right of the screen... please help, I don't understand what's wrong!
Thanks,
J.
You need to wrap the actual drawing and clipping of the box with the save and restore methods. and include the closePath method. I have modified your fiddle to work the way I believe you are trying to make it.
http://jsfiddle.net/jaredwilli/7kbzj/7/
ctx.save(); // save the context
// define new dirty zone
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.clip();
ctx.restore(); // restore the context
I also have learned that using save and restore can get really complex, and confusing to know which context your in. It came up with a pretty huge canvas app im working on, and i found that indenting your canvas code helps immensely. Especially the save/restores. I have even decided it should be considered a best practice, so the more people who know and do it the better.
Hope this helps.

Raphael JS : mouseover/mouseout - problem with text-labels

I use Raphael JS to create an SVG-map with area's and textlabels. I want the area to highlight when you move the mouse over it.
I have this working now, but when I move the mouse over the label (in the center of the area) the mouseout-event for that area is triggered, so the area is unhighlighted again.
Is there any way to prevent this from happening, or a workaround ?
Create a rect with opacity set to 0 over the text and attach the event handlers on that rect. You can calculate the dimensions of the rect using getBBox() of the text.
Creating a set via Paper#set was the approach that worked for me. e.g.:
var st = paper.set();
st.push.apply(st, vectors); // `vectors`: my array of "hoverable" vectors
st.push(text); // `text`: my text vector for `vectors`
st.hover(function () {
text.show();
}, function () {
text.hide();
});

Convert a given point from the window’s base coordinate system to the screen coordinate system

I am trying to figure out the way to convert a given point from the window’s base coordinate system to the screen coordinate system. I mean something like - (NSPoint)convertBaseToScreen:(NSPoint)point.
But I want it from quartz/carbon.
I have CGContextRef and its Bounds with me. But the bounds are with respect to Window to which CGContextRef belongs. For Example, if window is at location (100, 100, 50, 50) with respect to screen the contextRef for window would be (0,0, 50, 50). i.e. I am at location (0,0) but actually on screen I am at (100,100). I
Any suggestion are appreciated.
Thank you.
The window maintains its own position in global screen space and the compositor knows how to put that window's image at the correct location in screen space. The context itself, however doesn't have a location.
Quartz Compositor knows where the window is positioned on the screen, but Quartz 2D doesn't know anything more than how big the area it is supposed to draw in is. It has no idea where Quartz Compositor is going to put the drawing once it is done.
Similarly, when putting together the contents of a window, the frameworks provide the view system. The view system allows the OS to create contexts for drawing individual parts of a window and manages the placement of the results of drawing in those views, usually by manipulating the context's transform, or by creating temporary offscreen contexts. The context itself, however, doesn't know where the final graphic is going to be rendered.
I'm not sure if you can use directly CGContextRef, you need window or view reference or something like do the conversion.
The code I use does the opposite convert mouse coordinates from global (screen) to view local and it goes something like this:
Point mouseLoc; // point you want to convert to global coordinates
HIPoint where; // final coordinates
PixMapHandle portPixMap;
// portpixmap is needed to get correct offset otherwise y coord off at least by menu bar height
portPixMap = portPixMap = GetPortPixMap( GetWindowPort( GetControlOwner( view ) ) );
QDGlobalToLocalPoint(GetWindowPort( GetControlOwner( view ), &mouseLoc);
where.x = mouseLoc.h - (**portPixMap).bounds.left;
where.y = mouseLoc.v - (**portPixMap).bounds.top;
HIViewConvertPoint( &where, NULL, view );
so I guess the opposite is needed for you (haven't tested if it actually works):
void convert_point_to_screen(HIView view, HIPoint *point)
{
Point point; // used for QD calls
PixMapHandle portPixMap = GetPortPixMap( GetWindowPort( GetControlOwner( view ) ) );
HIViewConvertPoint( &where, view, NULL ); // view local to window local coordtinates
point.h = where->x + (**portPixMap).bounds.left;
point.w = where->y + (**portPixMap).bounds.top;
QDLocalToGlobalPoint(GetWindowPort( GetControlOwner( view ), &point);
// convert Point to HIPoint
where->x = point.h;
where->y = point.v;
}

Resources