- From: Charles Pritchard <chuck@jumis.com>
- Date: Wed, 02 Nov 2011 17:52:11 -0700
- 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 UTC