- From: Cameron McCormack <cam@mcc.id.au>
- Date: Fri, 30 Mar 2012 10:16:43 +1100
- To: SVG WG <public-svg-wg@w3.org>
Here are my comments on the canvas API additions as input for next week's discussion. Ian Hickson: > I just added a bunch of things to the <canvas> 2D API: > > - Path primitives: > var p = new Path(); > p.rect(0,0,100,100); > context.fill(p); I think having standalone path objects is a good idea. Paths are one of the data types that we have at the moment where you can't create a standalone one. These are the relevant things we've already resolved on: 14 Improve the SVG path DOM APIs 80 Have a DOM method to convert a <text> element to outline path data Whether we'd want to use an SVGPathSegList here is unclear, given that it's a sucky interface with an awkward name. Here's the definition of the Path interface: [Constructor, Constructor(Path path), Constructor(DOMString d)] interface Path { void addPath(Path path, SVGMatrix? transformation); void addPathByStrokingPath(Path path, CanvasDrawingStyles styles, SVGMatrix? transformation); void addText(DOMString text, CanvasDrawingStyles styles, SVGMatrix? transformation, double x, double y, optional double maxWidth); void addPathByStrokingText(DOMString text, CanvasDrawingStyles styles, SVGMatrix? transformation, double x, double y, optional double maxWidth); void addText(DOMString text, CanvasDrawingStyles styles, SVGMatrix? transformation, Path path, optional double maxWidth); void addPathByStrokingText(DOMString text, CanvasDrawingStyles styles, SVGMatrix? transformation, Path path, optional double maxWidth); }; Path implements CanvasPathMethods; and CanvasPathMethods is: interface CanvasPathMethods { // shared path API methods void closePath(); void moveTo(double x, double y); void lineTo(double x, double y); void quadraticCurveTo(double cpx, double cpy, double x, double y); void bezierCurveTo(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y); void arcTo(double x1, double y1, double x2, double y2, double radius); void arcTo(double x1, double y1, double x2, double y2, double radiusX, double radiusY, double rotation); void rect(double x, double y, double w, double h); void arc(double x, double y, double radius, double startAngle, double endAngle, optional boolean anticlockwise = false); void ellipse(double x, double y, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, boolean anticlockwise); }; My concern is how we are going to integrate SVGPathSegList (or maybe SVGAnimatedPathData) with Path. Might we want to allow these path manipulation methods to work on a <path d="">? For example: <path d="M100,100"/> <script> var elt = /* the path element */; elt.d.baseVal.lineTo(200, 300); // or elt.d.path.lineTo(200, 300); </script> The former would require the SVGPathSegList object to have all the same methods as Path. It could be done by SVGPathSegList implements Path; or even having SVGPathSegList inherit from Path. On the other hand, keeping it as a separate ".path" property keeps things simpler. It also mirrors one of the suggested ways we were going to improve SVGAnimatedLength etc., by having additional properties hanging off there like: myRect.x.px = 100; as "px" is the easier way to manipulate the SVGAnimatedLength, "path" could be the easier way to manipulate the SVGAnimatedPathData. SVGPathSegList provides access to individual path segments, which Path does not. Is that something we want to retain, if "path" is a separate property for elt.d as above? The whole SVGPathSegItem thing is what was awkward about SVGPathSegList, but it's possibly useful to allow this kind of individual access to segments somehow. In his examples in the spec, Ian assumes that SVGMatrix will have a constructor (I told him it probably will). I am unsure at the moment what the relationship between all the different Matrix interfaces is -- maybe Dirk knows? I know that if I were writing code I'd rather write "new Matrix(...)" than "new SVGMatrix(...)", especially if this matrix object could be used in non-SVG contexts like on these canvas methods. The "addPathByStrokingPath" is a general path outsetting function, which we've discussed before (in the context of the (harder) path warping). If implementors go for it, that's fine, but we had some concerns about graphics libraries not exposing this kind of functionality. The "addText" and "addPathByStrokingText" functions are for text-along-a-path. These seem to do the same kind of thing that we do (i.e., place each glyph aligned with the tangent of the path), but I did not study it long enough to see if all the edges cases are the same. We should do that. In fact, we should provide an easy referenceable definition in SVG that HTML can link to here, to avoid it being defined in two places. The methods that add a text outline to the current path take a DrawingStyle object which is an object that can be initialised with an element (optionally) but which also has the canvas properties for setting current stroke width, font, etc. on it. This is to handle the problem of what properties will be used for the text. I think we had settled on using a <text> object in the DOM which could then be converted into a path, and getting its properties from the document tree. Ian's proposed way is a bit more flexible, and you don't have to manipulate the document tree to get the right styles and text. I don't have a strong opinion here, but I guess I'd lean towards something that didn't require doing things in the document. > - Ellipses: > // arcs from center point, similar to arc() > context.ellipse(x, y, width/2, height/2, angle, 0, Math.PI*2); > context.stroke(); > // arc corners > context.arcTo(x1, y1, x2, y2, width/2, height/2, angle); Improving arc commands is path data is on our list too, although I don't think we have a concrete proposal yet. We should bear these functions in mind when coming up with the proposal so that if they are similar, we can align on argument order etc. so that we don't have confusing differences like we do currently. > - SVG path description syntax > context.stroke(new Path('M 100,100 h 50 v 50 h 50')); Discussed above. > - Dashed lines > context.setLineDash([3,1,0,1]); // --- . --- . --- . > context.moveTo(100,100); > context.lineTo(200,300); > context.stroke(); We should definitely have the same dashing. I haven't analysed the dashing algorithm in HTML to see how it compares to our less precise definition. :) (Defining dashing more precisely is on our list, too.) Modulo the specifics and probable corner case differences, I don't think there's anything controversial here. > - Text on a path: > var p1 = new Path('M 100 350 q 150 -300 300 0'); > var p2 = new Path(); > var styles = new DrawingStyle(); > styles.font = '20px sans-serif'; > p2.addText('Hello World', styles, null, p1); > context.fill(p2); Discussed above. > - Hit testing: > context.beginPath(); > context.rect(10,10,100,100); > context.fill(); > context.addHitRegion({ id: 'The First Button' }); > context.beginPath(); > context.rect(120,10,100,100); > context.fill(); > context.addHitRegion({ id: 'The Second Button' }); > canvas.onclick = function (event) { > if (event.region) > alert('You clicked ' + event.region); > }); I find the API a bit odd, and I feel like it's been added to satisfy some accessibility use cases that I don't understand just now. Not being able to remove a hit region seems strange too -- I'm not sure how useful it would be without that. > - Region discovery for AT users > context.rect(10,100,100,50); > context.fill(); > context.addHitRegion({ > id: 'button', > control: canvas.getElementsByTagName('button')[0], > }); > context.beginPath(); > context.rect(0,0,100,50); > context.textAlign = 'center'; > context.textBaseline = 'top'; > context.fillText('My Game', 50, 0, 100); > context.addHitRegion({ > id: 'header', > label: 'My Game', > role: 'heading', > }); > // now user can discover (via touch on a touch device, or via the > // virtual cursor in a traditional desktop AT) that there's a heading > // at the top of the canvas that says "My Game" and a button lower > // down that looks (to the AT) exactly like the canvas element's > // first child <button>. Again I am not sure how well in practice this is going to solve the whole "inaccessible canvas applications that probably should have been written in SVG in the first place" problem, but I won't pretend to be an expert in that area. > - Automatic cursor control > context.addHitRegion({ > path: new Path('M 10 10 h 20 v 20 h -20 z'), > cursor: 'url(fight.png)', > }); > context.addHitRegion({ > path: new Path('M 50 30 h 20 v 20 h -20 z'), > cursor: 'url(quaff.png)', > }); No comment; if hit regions are added supporting cursors on them makes sense. It's actually probably a pretty reasonable thing -- it's difficult to get cursor changes working properly if you are writing a canvas-based game for example. > - APIs that take SVGMatrix objects for transforms > // transform a path when you add it to another path > var duck = new Path('M 0 0 c 40 48 120 -32 160 -6 c 0 0 5 4 10 '+ > '-3 c 10 -103 50 -83 90 -42 c 0 0 20 12 30 7 c '+ > '-2 12 -18 17 -40 17 c -55 -2 -40 25 -20 35 c '+ > '30 20 35 65 -30 71 c -50 4 -170 4 -200 -79z'); > var identity = new SVGMatrix(); // constructor will be in SVG2 DOM > var threeDucks = new Path(); > threeDucks.addPath(duck, identity.translate(0,0)); > threeDucks.addPath(duck, identity.translate(100,0)); > threeDucks.addPath(duck, identity.translate(200,0)); > > // set the transform using an SVGMatrix > cantext.currentTransform = context.currentTransform.flipX(); Seems fine, apart from my wondering whether and how SVGMatrix and CSSMatrix might ever be merged and what's happening with that general Matrix proposal dino had. > - many more metrics from measureText() > var metrics = context.measureText('Hello World'); > var bL = metrics.actualBoundingBoxLeft; > var bR = metrics.actualBoundingBoxRight; > var bU = metrics.actualBoundingBoxAscent; > var bD = metrics.actualBoundingBoxDescent; > context.fillStyle = 'black'; > context.fillRect(x-bL, y-bU, bL+bR, bU+bD); > context.fillStyle = 'white'; > context.fillText(x, y, 'Hello World'); > // there are also values for all the baselines We've talked about exposing font metrics before but never had any proposals. Seems reasonable at first glance, though some of those names are a bit wordy. We could definitely look at exposing the same metrics objects from our <text> elements. > - ability to transform a pattern > var identity = new SVGMatrix(); > context.fillStyle = context.createPattern(img, 'repeat'); > context.fillStyle.setTransform(identity.rotate(angle)); > context.fillRect(0, 0, canvas.width, canvas.height); A thought: it would be good if our SVGMatrix constructor could take a transform item list: var rotation = new SVGMatrix("rotate(45)"); > - various other additions > // reset the clip region > context.resetClip(); > // reset the transform > context.resetTransform(); Not relevant for us. I think the biggest issue for us to think about is how Path and SVGPathSegList relate.
Received on Thursday, 29 March 2012 23:17:16 UTC