W3C home > Mailing lists > Public > public-canvas-api@w3.org > October to December 2011

Basic code examples for pointer regions

From: Charles Pritchard <chuck@jumis.com>
Date: Wed, 02 Nov 2011 17:52:11 -0700
Message-ID: <4EB1E5BB.6090206@jumis.com>
To: Jonas Sicking <jonas@sicking.cc>
CC: "public-canvas-api@w3.org" <public-canvas-api@w3.org>
Jonas, sorry this is a bit lengthy. I've used the Canvas checkbox 
example as well as a fun Canvas demonstration to explain why 
setPathForElement is easier for authors to use and not make mistakes 
with. It's at the bottom.

...

TL;DR: setPathForElement requires less work on implementers, authors and 
specification authoring than the beginElement proposal. beginElement 
would be very useful for recording Canvas calls into a retained format 
such as SVG.

Scroll to the bottom for references to existing code.

...

Following up on various threads about hit testing.

There are two active proposals to manage pointer events with Canvas.

beginElement/setDrawingFor:

setDrawingFor(element) -> setDrawingFor(null)
or in another form:
beginElement(element) -> endElement()

The concept is to record path data and bounding rectangles for drawing 
commands issued between the start and stop methods. putImageData and 
drawImage calls would be treated as rectangles, with drawImage subject 
to the current transformation matrix (as is the case).

setPathForElement, was: setClickableRegion

setClickableRegion(element)

The concept is to use the current path in context, and like 
beginElement, associate the path and bounding rectangle to the target 
element. This method does not work with putImageData nor drawImage.

....

Are they easy for authors to use? Will they support pointer events for 
AT as prescribed by WCAG?

There is a minimal bar for these two criteria:

For ease of use the result of using these methods must be easily 
identifiable as well as enhance the productivity of authors using them.

For WCAG compliance, the methods must, at minimum, record bounding box 
information to expose basic accessibility APIs.

Following from these criteria, in which I believe there is acceptance:

beginElement implementations should have an internal path objects stack 
-- though it is possible to implement beginElement for hit testing using 
only bitmaps the resulting data would be practically opaque to assistive 
technologies. Canvas does not require any particular level of precision 
on those path objects. It is very much implementation dependent.

setPathForElement simply uses the path object already present, a series 
of subpaths. Both methods could benefit from using a bitmap to do hit 
testing on performance constrained systems. The subpaths can be used in 
both systems to reproduce and expose vital information to platform 
accessibilty APIs, as well as hit testing and regenerating a hit testing 
bitmap.

Both methods would increase the productivity of authors as they debug 
and develop their application, they would expose vital information to 
ATs and the results are easily identified.

....

setPathForElement is all that's needed by authors to fulfil WCAG. 
Compared to beginElement it's shorter with far less restrictions and 
considerations.

Blob Sallad is an excellent tech demo for interactivity; I use it all 
the time, it has a free license.
http://www.blobsallad.se/blobsallad.js
http://www.blobsallad.se/iframedsallad.html

To enable accessible hit testing, I'd go to line 880 in the this.draw 
method:
     this.drawBody(ctx, scaleFactor);
And change it to:
     ctx.beginElement(element);
     this.drawBody(ctx, scaleFactor);
     ctx.endElement();
Or, simpler:
     this.drawBody(ctx, scaleFactor);
     this.setPathForElement(element);

As a sidenote, I'd have to pass in the active element, into the 
this.draw method.
This coding pattern is quite common.

In this demo, I wouldn't be altering the mouse events as they've already 
been optimized by the author to listen on the document object.


Immediate problems with beginElement v. setPathForElement:

Note that beginElement requires 2x the amount of code to be inserted. It 
also requires the author to be aware of all of the drawing calls 
happening between the two methods. It requires a level of formality. It 
requires a lot of work on implementers, adding a trace method to most 
Canvas methods. It requires the specification authors to detail new 
exceptions or the various corner-case behaviors when the method is 
called. The method averages a cost about 3x higher than 
setPathForElement, in my estimates.

We can document the corner cases in specs, add the trace methods to 
implementations and as authors, be hyper-aware of when we are running 
beginElement, but that is very costly. For that cost, I think it ought 
to target an output format, such as SVG or JSON. It's a highly desirable 
feature, and it'd work reasonably with this method. But it's only 
desirable when the author actually wants drawing commands to be retained 
in the DOM and/or accessible to the scripting environment.


Consider that a Canvas state may be this:
1: ctx.beginPath();
2: ctx.moveTo(10,10);
3: ctx.beginElement(element);
4: ctx.lineTo(20,20);
5: ctx.lineTo(10,20);
6: ctx.closePath();
7: ctx.fill();
8: ctx.endElement();

It seems that should include the current path, but it gets away from the 
idea that beginElement is run before the path drawing commands. This 
issue does not come up with setPathForElement, and the placement of the 
method is less strict.

Note that setPathForElement could be inserted at any point after line 5. 
endElement would be inserted at any point after line 8. And again, there 
are 2x as many insertions with beginPath.


There's another example, the Checkbox example, floating around.
In it, the author has decided to make the entire region clickable:

ctx.rect(0, 0, 100, 20);

The example illustrates the difficulty of using beginElement efficiently.
The author wrapped the entire drawing code, creating a lot of extra work 
on processor for no gain.
This is not readily apparent, due to the nature of beginElement

With setPathForElement, the author would have been more likely to do 
what they intended to.

Even still, it's important to recognize that the path associated with an 
element does not need to be updated at every drawing call. It only needs 
to be updates when there is an actual mutation needed.

....

I highly recommend considering beginElement in the context of SVG 
interoperation and as part of SVG 2.0, to be handed off to  FX and SVG 
WGs for comment.

I also recommend setPathForElement, but a better name (less camel case), 
be considered as a more appropriate solution for -most- use cases of Canvas.
Received on Thursday, 3 November 2011 00:52:37 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Thursday, 3 November 2011 00:52:37 GMT