blob: ac3462ed3a49d31f0aa9a5d9dcf8f7b4596b22c3 [file] [log] [blame]
<!-- 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>