| <!-- The <canvas-layers-sk> custom element declaration. |
| |
| This is a utility element that is used around another element, which places |
| layers of multiple canvas elements directly over it element. Useful for |
| drawing annotations, crosshairs, etc. over an image or canvas. |
| |
| The canvas listens for the builtin event "resize" on window, and also for |
| the custom event "partial-resize" also on window. You can send a |
| "partial-resize" custom event to window to trigger the canvas layers to check |
| the size of the image and resize appropriately. |
| |
| If the subject is an image, this element will also listen for its load event |
| and resize if the image changes. |
| |
| Initially sk-canvas-layers tries to find a child img node to use as the subject |
| but if none is found, you must call changeSubject(element) before it will work. |
| |
| Attributes: |
| layers - A JSON serialized array of layer names. There will be one canvas |
| for each name. Call canvas(name) to get the underlying canvas object. |
| Note that the order in layers determines the DOM order, with the last |
| member of layers being the last DOM canvas, which, for example, will |
| be the only canvas to get 'click' events because it will be on top. |
| |
| Events: |
| canvas-update - Triggered when the underlying image has changed. |
| |
| canvas-layers-updated - Triggered when the templating has finished |
| and the canvases are in place. |
| |
| Methods: |
| canvas(name) - Returns the canvas object for the layer 'name'. See |
| the layers attribute. |
| |
| changeSubject(element) - Used to replace the subject element after creation. |
| Opted for this instead of putting an observer on the subjectId because query |
| selectors can erroneously match deleted elements. |
| |
| subject() - return the current subject element. |
| |
| --> |
| |
| <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> |
| <dom-module id="canvas-layers-sk"> |
| <style> |
| :host { |
| display: inline-block; |
| position: relative; |
| } |
| |
| canvas { |
| position: absolute; |
| top: 0; |
| left: 0; |
| } |
| </style> |
| <template> |
| <content></content> |
| <template is="dom-repeat" items="{{ layers }}" as="layer"> |
| <canvas id$="{{ id }}-{{ layer }}" width=500 height=500> |
| </template> |
| </template> |
| </dom-module> |
| |
| <script> |
| Polymer({ |
| is: "canvas-layers-sk", |
| |
| properties: { |
| layers: { |
| type: Array, |
| value: function() { return []; }, |
| reflectToAttribute: false, |
| }, |
| }, |
| |
| ready: function() { |
| // _subject is the element which this stack of layers is drawn on top of. |
| this._subject = $$$('#img', this); |
| if (!this._subject) { |
| // if the subject cannot be identified by the 'img' id, look for any img element. |
| this._subject = $$$('img', this); |
| } |
| this._imageLoadedBound = this._imageLoaded.bind(this); |
| if (this._subject) { |
| this._subject.addEventListener('load', this._imageLoadedBound ); |
| } |
| this.addEventListener('dom-change', this._imageLoadedBound); |
| this.addEventListener('dom-change', this._domChanged.bind(this)); |
| this._resizing = false; |
| window.addEventListener('resize', function() { |
| if (this._resizing == true) { |
| return; |
| } |
| this._resizing = true; |
| window.requestAnimationFrame(function(){ |
| this._resizing = false; |
| this._imageLoaded(); |
| }.bind(this)); |
| }.bind(this)); |
| window.addEventListener('partial-resize', function() { |
| this._imageLoaded(); |
| }.bind(this)); |
| this._imageLoaded(); |
| }, |
| |
| changeSubject(element) { |
| this._subject = element; |
| this._domChanged(); |
| }, |
| |
| _domChanged: function() { |
| this.dispatchEvent(new CustomEvent('canvas-layers-updated', { bubbles: true })); |
| }, |
| |
| _imageLoaded: function() { |
| // What I really want here is to know the size of _subject after the #rendered div has decided |
| // whether or not it will have scrollbars, but to know that I must first make all other |
| // canvases irrelevant to the overflow of that div's content. |
| $$('canvas', this).forEach(function(c) { |
| if (c !== this._subject) { |
| c.width = 1; |
| c.height = 1; |
| } |
| }.bind(this)); |
| // now, only _subject is affecting the presense of scrollbars on #rendered and |
| // getComputedStyle is correct. |
| let strW = window.getComputedStyle(this._subject, null).width; |
| let strH = window.getComputedStyle(this._subject, null).height; |
| let detail = { |
| width: parseFloat(strW.substring(0, strW.length-2)), |
| height: parseFloat(strH.substring(0, strH.length-2)), |
| }; |
| $$('canvas', this).forEach(function(c) { |
| if (c !== this._subject) { |
| c.width = detail.width; |
| c.height = detail.height; |
| c.dispatchEvent(new CustomEvent("canvas-update", { detail: detail, bubbles: true })); |
| } |
| }.bind(this)); |
| }, |
| |
| canvas: function(name) { |
| return $$$("#" + this.id + "-" + name, this); |
| }, |
| |
| subject: function() { |
| return this._subject; |
| }, |
| |
| }); |
| </script> |