| <!-- The <crosshair-sk> custom element declaration. |
| |
| The crosshair-sk element works with the canvas-layers-sk element |
| and draws a crosshair on one of the given layers. Use the update_on |
| attribute to control when the crosshair is moved. |
| |
| Attributes: |
| x, y - The x,y coords of the crosshair, as measured in the natural |
| dimensions of the image. I.e. not in the coords of the img |
| if it has a different size as determined by CSS styling. |
| |
| target - The id of the canvas-layers-sk element we are drawing |
| a crosshair on. |
| |
| name - The name of the layer in the canvas-layer-sk element |
| we are drawing a crosshair on. |
| |
| hidden - If true then don't actually draw the crosshair. |
| |
| update_on - Update the crosshair: |
| "move" - when the mouse moves. |
| "click" - when the user clicks. |
| "none" - (default) do not update the crosshair based on user action. |
| |
| Events: |
| crosshair - Produced if the crosshair is moved by clicking in the image. |
| The coordinates of the click are in e.detail.x and e.detail.y. They |
| are in offsets from the image origin in the images natural dimensions. |
| That is, even if the image is sized using CSS the x and y values |
| will be reported as values in the images original size. |
| |
| Methods: |
| None. |
| |
| --> |
| <link rel="import" href="/res/imp/bower_components/polymer/polymer.html"> |
| <dom-module id="crosshair-sk"> |
| <template> |
| </template> |
| <script> |
| Polymer({ |
| is: 'crosshair-sk', |
| |
| properties: { |
| x: { |
| type: Number, |
| value: 0, |
| observer: '_xChanged', |
| reflectToAttribute: true, |
| notify: true |
| }, |
| y: { |
| type: Number, |
| value: 0, |
| observer: '_yChanged', |
| reflectToAttribute: true, |
| notify: true |
| }, |
| target: { |
| type: String, |
| value: "", |
| observer: '_layerChanged', |
| reflectToAttribute: true |
| }, |
| hidden: { |
| type: Boolean, |
| value: false, |
| observer: '_hiddenChanged', |
| reflectToAttribute: true |
| }, |
| name: { |
| type: String, |
| value: "", |
| observer: '_layerChanged', |
| reflectToAttribute: true |
| }, |
| update_on: { |
| // Valid values are "click", "move", and "none". |
| type: String, |
| value: "none", |
| observer: '_registerCallbacks', |
| reflectToAttribute: true |
| }, |
| }, |
| |
| ready: function () { |
| // The current location of the crosshair, in CSS units. |
| this.thumb = { |
| x: 0, |
| y: 0, |
| }; |
| |
| // If we are tracking mousemove events we'll store the coords here. |
| this._move = { |
| x: 0, |
| y: 0, |
| }; |
| |
| // If we are tracking mousemove events we'll store the previous coords |
| // here, so we only redraw the crosshair if it's actually moved. |
| this._lastMove = { |
| x: 0, |
| y: 0, |
| }; |
| this._drawCrosshairBound = this._drawCrosshair.bind(this); |
| this._moveEventHandlerBound = this._moveEventHandler.bind(this); |
| this._clickEventHandlerBound = this._clickEventHandler.bind(this); |
| this._moveAnimationFrameBound = this._moveAnimationFrame.bind(this); |
| }, |
| |
| _xChanged: function (newValue, oldValue) { |
| if (this._img && oldValue != newValue) { |
| this.thumb.x = this._img.width * (this.x / this._img.naturalWidth); |
| this._drawCrosshair(); |
| this._event(); |
| } |
| }, |
| |
| _yChanged: function (newValue, oldValue) { |
| if (this._img && oldValue != newValue) { |
| this.thumb.y = this._img.height * (this.y / this._img.naturalHeight); |
| this._drawCrosshair(); |
| this._event(); |
| } |
| }, |
| |
| _layerChanged: function() { |
| if (!(this.target && this.name)) { |
| return; |
| } |
| $$$("#" + this.target, this._findParent()).addEventListener('canvas-layers-updated', this._canvasLayersUpdated.bind(this)); |
| }, |
| |
| _hiddenChanged: function() { |
| if (this._ctx) { |
| this._drawCrosshair(); |
| } |
| }, |
| |
| _findParent: function() { |
| var p = this.parentNode; |
| while (p.parentNode != null) { |
| p = p.parentNode; |
| } |
| return p |
| }, |
| |
| _canvasLayersUpdated: function() { |
| var layer = $$$("#" + this.target, Polymer.dom(this).parentNode); |
| if (this._canvas) { |
| // If we are switching to a new canvas then unregister all of our old callbacks. |
| this._unregisterAllCallbacks(); |
| } |
| this._canvas = layer.canvas(this.name); |
| this._img = layer.img(); |
| if (this._canvas) { |
| this._registerCallbacks(); |
| this._ctx = this._canvas.getContext('2d'); |
| } |
| }, |
| |
| _registerCallbacks: function() { |
| if (!this._canvas) { |
| return; |
| } |
| this._canvas.addEventListener('canvas-update', this._drawCrosshairBound); |
| if (this.update_on === "click") { |
| this._canvas.addEventListener('click', this._clickEventHandlerBound); |
| this._canvas.removeEventListener('mousemove', this._moveEventHandlerBound); |
| } else if (this.update_on === "move") { |
| this._canvas.addEventListener('mousemove', this._moveEventHandlerBound); |
| this._canvas.removeEventListener('click', this._clickEventHandlerBound); |
| window.requestAnimationFrame(this._moveAnimationFrameBound); |
| } else { |
| this._canvas.removeEventListener('mousemove', this._moveEventHandlerBound); |
| this._canvas.removeEventListener('click', this._clickEventHandlerBound); |
| } |
| }, |
| |
| _unregisterAllCallbacks: function() { |
| this._canvas.removeEventListener('mousemove', this._moveEventHandlerBound); |
| this._canvas.removeEventListener('click', this._clickEventHandlerBound); |
| this._canvas.removeEventListener('canvas-update', this._drawCrosshairBound); |
| }, |
| |
| _moveEventHandler: function (e) { |
| // Only record the values, don't do any work here. |
| // The actual updating takes place in the RaF _moveAnimationFrame. |
| this._move.x = e.clientX; |
| this._move.y = e.clientY; |
| }, |
| |
| _clickEventHandler: function (e) { |
| this._moveCrosshair(e.clientX, e.clientY); |
| }, |
| |
| _moveAnimationFrame: function() { |
| if (this._lastMove.x != this._move.x || this._lastMove.y != this._move.y) { |
| this._moveCrosshair(this._move.x, this._move.y); |
| this._lastMove.x = this._move.x; |
| this._lastMove.y = this._move.y; |
| } |
| if (this.update_on === "move") { |
| window.requestAnimationFrame(this._moveAnimationFrameBound); |
| } |
| }, |
| |
| _moveCrosshair: function (clientX, clientY) { |
| if (this._img.width == 0 || this._img.height == 0) { |
| return |
| } |
| var p = sk.elePos(this._canvas); |
| this.thumb.x = clientX - p.x; |
| this.thumb.y = clientY - p.y; |
| this._drawCrosshair(); |
| this.x = Math.floor(this.thumb.x / this._img.width * this._img.naturalWidth); |
| this.y = Math.floor(this.thumb.y / this._img.height * this._img.naturalHeight); |
| this._event(); |
| }, |
| |
| _event: function() { |
| var detail = { |
| x: this.x, |
| y: this.y |
| }; |
| var evt = new CustomEvent('crosshair', { |
| detail: detail, |
| bubbles: true |
| }); |
| this.dispatchEvent(evt); |
| }, |
| |
| _drawCrosshair: function () { |
| this._ctx.clearRect(0, 0, this._ctx.canvas.width, this._ctx.canvas.height); |
| if (this.hidden) { |
| return; |
| } |
| this._ctx.lineWidth = 1; |
| this._ctx.strokeStyle = '#F00'; |
| this._ctx.beginPath(); |
| this._ctx.moveTo(0, this.thumb.y + 0.5); |
| this._ctx.lineTo(this._ctx.canvas.width, this.thumb.y + 0.5); |
| this._ctx.stroke(); |
| this._ctx.beginPath(); |
| this._ctx.moveTo(this.thumb.x + 0.5, 0); |
| this._ctx.lineTo(this.thumb.x + 0.5, this._ctx.canvas.height); |
| this._ctx.stroke(); |
| } |
| }); |
| </script> |
| </dom-module> |