File: /home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/annotations/button.js
/**
A **Button** is a clickable {{#crossLink "Pin"}}{{/crossLink}} that's attached to the surface of an {{#crossLink "Mesh"}}{{/crossLink}}.
@class Button
@module xeogl
@submodule annotations
@constructor
@param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this Pin in the default
{{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
@param [cfg] {*} Configs
@param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}},
generated automatically when omitted.
@param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to the Button.
@param [cfg.mesh] {Number|String|Mesh} ID or instance of the {{#crossLink "Mesh"}}{{/crossLink}} the Button is attached to.
@param [cfg.bary=[0.3,0.3,0.3]] {Float32Array} Barycentric coordinates of the Button within its triangle.
@param [cfg.primIndex=0] {Number} Index of the triangle containing the Button. Within the {{#crossLink "Mesh"}}{{/crossLink}} {{#crossLink "Geometry"}}{{/crossLink}}
{{#crossLink "Geometry/indices:property"}}{{/crossLink}}, this is the index of the first
element for that triangle.
@param [cfg.offset=0.2] {Number} How far the Button is lifted out of its triangle, along the surface normal vector. This is used when occlusion culling, to ensure that the Button is not lost inside the surface it's attached to.
@param [cfg.occludable=true] {Boolean} Indicates whether occlusion testing is performed for the Button, where it will be flagged invisible whenever it's hidden by something else in the 3D camera.
@param [cfg.glyph=""] {String} Short piece of text to show inside the pin for the Button. Automatically truncated to 2 characters.
@param [cfg.pinShown=true] {Boolean} Specifies whether a UI element is shown at the Button's pin position (typically a circle).
@param [cfg.zIndex] {Number} Optional CSS z-index value for HTML button element.
@extends Pin
*/
xeogl.Button = xeogl.Pin.extend({
type: "xeogl.Button",
_init: function (cfg) {
this._super(cfg);
this._spotClickable = document.createElement("a");
this._spotClickable.href = "javascript:xeogl.scenes[\"" + this.scene.id + "\"].components[\"" + this.id + "\"]._pinClicked()";
this._spotClickable.style["display"] = "block";
this._spotClickable.style["content"] = '';
this._spotClickable.style["position"] = "absolute";
this._spotClickable.style["width"] = "50px";
this._spotClickable.style["height"] = "50px";
this._spotClickable.style["border-radius"] = "25px;";
this._spotClickable.style["visibility"] = "hidden";
this._spotClickable.style["z-index"] = 0;
document.body.appendChild(this._spotClickable);
this._spot = document.createElement("div");
this._spot.style["pointer-events"] = "none";
this._spot.innerText = "i";
this._spot.style["color"] = "#ffffff";
this._spot.style["line-height"] = 1.8;
this._spot.style["text-align"] = "center";
this._spot.style["font-family"] = "monospace";
this._spot.style["font-weight"] = "bold";
this._spot.style["position"] = "absolute";
this._spot.style["width"] = "25px";
this._spot.style["height"] = "25px";
this._spot.style["border-radius"] = "15px";
this._spot.style["border"] = "2px solid #ffffff";
this._spot.style["background"] = "black";
this._spot.style["visibility"] = "hidden";
this._spot.style["box-shadow"] = "5px 5px 15px 1px #000000";
this._spot.style["z-index"] = 0;
const zIndex = cfg.zIndex || 0;
this._spot.style["z-index"] = zIndex;
this._spot.style["zIndex"] = zIndex;
this._spotClickable.style["z-index"] = zIndex;
this._spotClickable.style["zIndex"] = zIndex;
document.body.appendChild(this._spot);
this.glyph = cfg.glyph;
this.pinShown = cfg.pinShown;
this._tick = this.scene.on("tick", this._updateLayout, this);
this.on("visible", this._updateVisibility, this);
// this._updateVisibility();
},
_pinClicked: function () {
/**
Fired whenever the mouse is clicked on this Button's {{#crossLink "Button/pin:property"}}{{/crossLink}}.
@event pinClicked
*/
this.fire("pinClicked", this)
},
_props: {
/**
Short piece of text to show inside the pin for the Button.
Usually this would be a single number or letter.
Automatically truncated to 2 characters.
Fires a {{#crossLink "Button/glyph:event"}}{{/crossLink}} event on change.
@property glyph
@default ""
@type {String}
*/
glyph: {
set: function (glyph) {
if (this._glyph === glyph) {
return;
}
this._glyph = glyph || ""; // TODO: Limit to 2 chars
this._spot.innerText = this._glyph;
/**
Fired whenever this Button's {{#crossLink "Button/glyph:property"}}{{/crossLink}} property changes.
@event glyph
@param value {Number} The property's new value
*/
this.fire("glyph", this._glyph);
},
get: function () {
return this._glyph;
}
},
/**
Specifies whether a UI element is shown at the Button's pin position (typically a circle).
Fires a {{#crossLink "Button/pinShown:event"}}{{/crossLink}} event on change.
@property pinShown
@default true
@type {Boolean}
*/
pinShown: {
set: function (shown) {
shown = shown !== false;
if (this._pinShown === shown) {
return;
}
this._pinShown = shown;
this._spot.style.visibility = this._pinShown ? "visible" : "hidden";
this._spotClickable.style.visibility = this._pinShown ? "visible" : "hidden";
/**
Fired whenever this Button's {{#crossLink "Button/pinShown:property"}}{{/crossLink}} property changes.
@event pinShown
@param value {Number} The property's new value
*/
this.fire("pinShown", this._pinShown);
},
get: function () {
return this._pinShown;
}
}
},
_updateVisibility: function () {
const visible = this.visible;
this._spotClickable.style.visibility = visible && this._pinShown ? "visible" : "hidden";
this._spot.style.visibility = visible && this._pinShown ? "visible" : "hidden";
},
_updateLayout: function () {
const visible = this.visible;
if (visible) {
const canvas = this.scene.canvas.canvas;
const left = canvas.offsetLeft;
const top = canvas.offsetTop;
const canvasPos = this.canvasPos;
this._spot.style.left = (Math.floor(left + canvasPos[0]) - 12) + "px";
this._spot.style.top = (Math.floor(top + canvasPos[1]) - 12) + "px";
this._spotClickable.style.left = (Math.floor(left + canvasPos[0]) - 25 + 1) + "px";
this._spotClickable.style.top = (Math.floor(top + canvasPos[1]) - 25 + 1) + "px";
//this._spot.style["z-index"] = 90005 + Math.floor(this.viewPos[2] * 10) + 1;
}
},
getJSON: function () {
const math = xeogl.math;
const json = {
primIndex: this.primIndex,
bary: math.vecToArray(this.bary),
offset: this.offset,
occludable: this.occludable,
glyph: this._glyph,
pinShown: this._pinShown
};
if (this._attached.mesh) {
json.mesh = this._attached.mesh.id;
}
return json;
},
_destroy: function () {
this._super();
this.scene.off(this._tick);
this._spot.parentNode.removeChild(this._spot);
this._spotClickable.parentNode.removeChild(this._spotClickable);
}
});