- 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