/* * Sprite3D.js * Visit the internets for documentation, updates and examples. * https://github.com/boblemarin/Sprite3D.js * http://minimal.be/lab/Sprite3D * * Copyright (c) 2010 boblemarin emeric@minimal.be http://www.minimal.be * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ /** * Creates an instance of Sprite3D * * @constructor * @author boblemarin * @this {Sprite3D} * @param {Object} element The DOM element to wrap the Sprite3D object around. When no element is provided, a empty div is created and added to the document. */ function Sprite3D(element) { if ( !Sprite3D.prototype._isInit ) Sprite3D.isSupported(); // private variables var p = "", s = "", rx = "", ry = "", rz = "", ts = "", i, alpha = 1; // listeners = {}; this.listeners = {}; // create an empty
domElement
property of the Sprite3D and set its id
property.
* This method does not require a call to the update methods.
* @param {String} id The ID
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setId = function(id) {
this.domElement.id = id;
return this;
};
/**
* Returns the ID of the DOM element associated with the Sprite3D.
* @return {String} The CSS class name
*/
Sprite3D.prototype.getId = function() {
return this.domElement.id;
};
/**
* Allows to set any value in any CSS property of the style object of the DOM element.
* This method is just a helper allowing neverending chaining in the Sprite3D creation syntax.
* For one time modifications, you can simply use the style
property of the Sprite3D.
* This method does not require a call to the update methods.
* @param {String} name The name of the CSS property in which the value will be stored
* @param {String} value The value to assign to the property
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setCSS = function(name, value) {
this.domElement.style[name] = value;
return this;
};
/**
* Returns the value assigned to the provided CSS property.
* @param {String} name The name of the property to get the value from
* @return {String} The value of the CSS property
*/
Sprite3D.prototype.getCSS = function(name) {
return this.domElement.style[name];
};
/**
* Allows direct write access to the innerHTML property of the DOM element.
* @param {String} value The string to write into the innerHTML property
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setInnerHTML = function(value) {
this.domElement.innerHTML = value;
return this;
};
/**
* Sets the size of the tiles in the spritesheet used as background image.
* @param {Number} width The desired width
* @param {Number} height The desired height
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setTileSize = function(width, height) {
this.tileWidth = width;
this.tileHeight = height;
return this;
};
/**
* Modifies the sprites's background image position to display the selected tile.
* For this method to work, you are supposed to set a background image and limit the size of the element using CSS styles,
* and use a sprite sheet where all tiles have the same size. No checking is performed on the provided values.
* @param {Number} tilePosX The horizontal index of the tile to be displayed
* @param {Number} tilePosY The vertical index of the tile to be displayed
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setTilePosition = function(tilePosX, tilePosY) {
this.style.backgroundPosition = "-" + (tilePosX * this.tileWidth) + "px -" + (tilePosY * this.tileHeight) + "px";
return this;
};
/**
* Allows to set a arbitary property value while using the chaining syntax.
* @param {String} label The name of the property
* @param {Object} value The value for that property
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setProperty = function(label, value) {
this[label] = value;
return this;
};
/**
* Sets the order in which transformations are applied.
* If true, rotations are applied before translations. If false, translations are applied before rotations.
* Note that, when applying rotations, the axis of the object rotate, and subsequent translations follow the modified orientation of the axis.
* For a more accurate control, you should use the transformString property.
* @param {boolean} rf true to rotate first, false to translate first
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.setRotateFirst = function(rf) {
this.rotateFirst = rf;
if ( rf ) {
this.transformString = "_rx _ry _rz _p _s";
} else {
this.transformString = "_p _rz _ry _rx _s";
}
return this;
};
/**
* Applies position and rotation values.
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.update = function() {
this.p = "translate3d(" + (this.x - this.regX) + "px," + (this.y - this.regY) + "px," + (this.z - this.regZ) + "px) ";
this.rx = "rotateX(" + this.rotationX + "deg) ";
this.ry = "rotateY(" + this.rotationY + "deg) ";
this.rz = "rotateZ(" + this.rotationZ + "deg) ";
this.s = "scale3d(" + this.scaleX + ", " + this.scaleY + ", " + this.scaleX + ") ";
/*
if (this.rotateFirst)
this.style.webkitTransform = this.rz + this.ry + this.rx + this.p + this.s;
else
this.style.webkitTransform = this.p + this.rx + this.ry + this.rz + this.s;
*/
// var transformString = "_rx _ry _rz _p _s";
this.ts = this.transformString;
this.ts = this.ts.replace( "_p", this.p );
this.ts = this.ts.replace( "_rx", this.rx );
this.ts = this.ts.replace( "_ry", this.ry );
this.ts = this.ts.replace( "_rz", this.rz );
this.ts = this.ts.replace( "_s", this.s );
//this.style.webkitTransform = this.ts;
this.style[this._transformProperty] = this.ts;
return this;
};
/**
* Applies 2D position, rotation and scaling values.
* This method allows to use Sprite3D with browsers that only support 2D transforms.
* When applying the transforms, it uses the x and y position, z rotation and x and y scaling.
* The other values are ignored.
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.update2D = function() {
this.p = "translate(" + (this.x - this.regX) + "px," + (this.y - this.regY) + "px) ";
this.rz = "rotate(" + this.rotationZ + "deg) ";
this.s = "scale(" + this.scaleX + ", " + this.scaleY + ") ";
this.ts = this.transformString;
this.ts = this.ts.replace( "_p", this.p );
this.ts = this.ts.replace( "_rx", "" );
this.ts = this.ts.replace( "_ry", "" );
this.ts = this.ts.replace( "_rz", this.rz );
this.ts = this.ts.replace( "_s", this.s );
//this.style.webkitTransform = this.ts;
this.style[this._transformProperty] = this.ts;
//console.log( "apply 2D transforms using " + this._transformProperty );
return this;
};
/**
* Applies position and rotation values, as well as opacity and size.
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.updateAll = function() {
this.update();
this.style.opacity = this.alpha;
this.style.width = this.width + "px";
this.style.height = this.height + "px";
return this.update();
};
/**
* Calls the update() method on every child of the Sprite3D object.
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.updateChildren = function() {
for (this.i = 0; this.i < this.numChildren; this.i++) {
this.children[this.i].update();
}
return this;
};
/**
* Calls the updateAll() method on every child of the Sprite3D object.
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.updateChildrenAll = function() {
for (this.i = 0; this.i < this.numChildren; this.i++) {
this.children[this.i].updateAll();
}
return this;
};
/**
* Updates itself, then calls the update() method on every child of the Sprite3D object.
* @param {boolean} recursive If set to true, make the update call recursive, update every child's children
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.updateWithChildren = function(recursive) {
this.update();
for (this.i = 0; this.i < this.numChildren; this.i++) {
if (recursive) {
this.children[this.i].updateWithChildren(recursive);
} else {
this.children[this.i].update();
}
}
return this;
};
/**
* Adds a Sprite3D object to this Sprite3D children.
* @param {Sprite3D} e The Sprite3D object to add
* @return {Sprite3D} The reference to the added Sprite3D object
*/
Sprite3D.prototype.addChild = function(e) {
this.numChildren = this.children.push(e);
this.domElement.appendChild(e.domElement);
return e;
};
/**
* Removes a Sprite3D object from this Sprite3D children.
* @param {Sprite3D} child The Sprite3D object to remove
* @return {Sprite3D} The reference to the removed Sprite3D object. null if the child was not found in this Sprite3D children list
*/
Sprite3D.prototype.removeChild = function(child) {
var n = this.children.indexOf(child);
if (n > -1) {
return this.removeChildAt(n);
}
return null;
};
/**
* Removes the nth Sprite3D object from this Sprite3D children.
* @param {number} n The index of the Sprite3D object to remove
* @return {Sprite3D} The reference to the removed Sprite3D object.
*/
Sprite3D.prototype.removeChildAt = function(n) {
--this.numChildren;
this.domElement.removeChild(this.children[n].domElement);
return this.children.splice(n, 1)[0];
};
/**
* Finds and return the Sprite3D object associated with the provided DOM element
* @param {Object} element The DOM element
* @return {Sprite3D} The reference to the associated Sprite3D object. Returns null if no relevant Sprite3D object was found
*/
Sprite3D.prototype.findFromDOMElement = function(element) {
for (this.i = 0; this.i < this.numChildren; this.i++) {
if (element == this.children[this.i].domElement) { return this.children[this.i]; }
}
return null;
};
Sprite3D.prototype.listeners = {};
/**
* Adds an event listener to the DOM element for the provided event id.
* @param {String} event The name of the event to watch
* @param {Function} callback The callback function
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.addEventListener = function(event, callback) {
var fname = event + "_" + callback.name;
if ( this.listeners[fname] == null ) {
var sprite = this;
var fn = function(e) { callback(e, sprite); }
this.listeners[fname] = fn;
this.domElement.addEventListener(event, fn );
}
return this;
};
/**
* Removes an event listener to the DOM element for the provided event id.
* @param {String} event The name of the event to watch
* @param {Function} callback The callback function
* @return {Sprite3D} The reference to this Sprite3D object
*/
Sprite3D.prototype.removeEventListener = function(event, callback) {
var fname = event + "_" + callback.name;
if ( this.listeners[fname] != null ) {
this.domElement.removeEventListener(event, this.listeners[fname] );
delete this.listeners[fname];
}
return this;
};
/**
* Creates a centered empty HTML div element to be used as root container for the other Sprite3D objects.
* @return {Sprite3D} The created Sprite3D object
*/
Sprite3D.createCenteredContainer = function() {
var c = document.createElement('div'),
s = c.style;
if ( !Sprite3D.prototype._isInit ) Sprite3D.isSupported();
s[Sprite3D.prototype._browserPrefix+"Perspective"] = "800" + (Sprite3D.prototype._browserPrefix=="Moz"?"px":"");
s[Sprite3D.prototype._browserPrefix+"PerspectiveOrigin"] = "0 0";
s[Sprite3D.prototype._browserPrefix+"TransformOrigin"] = "0 0";
s[Sprite3D.prototype._browserPrefix+"Transform"] = "translateZ(0px)";
s.position = "absolute";
s.top = "50%";
s.left = "50%";
s.margin = "0px";
s.padding = "0px";
//s.border = "1px solid red"; // <- this one is for debug
document.body.appendChild(c);
return new Sprite3D(c);
};
/**
* Creates a top-left aligned empty HTML div element to be used as root container for the other Sprite3D objects.
* @return {Sprite3D} The created Sprite3D object
*/
Sprite3D.createTopLeftCenteredContainer = function() {
var c = document.createElement('div'),
s = c.style;
if ( !Sprite3D.prototype._isInit ) Sprite3D.isSupported();
s[Sprite3D.prototype._browserPrefix+"Perspective"] = "800" + (Sprite3D.prototype._browserPrefix=="Moz"?"px":"");
//s[Sprite3D.prototype._browserPrefix+"PerspectiveOrigin"] = "0 0";
//s[Sprite3D.prototype._browserPrefix+"TransformOrigin"] = "0 0";
s[Sprite3D.prototype._browserPrefix+"Transform"] = "translateZ(0px)";
//s.webkitPerspective = "800";
// s.webkitPerspectiveOrigin = "0 0";
// s.webkitTransformOrigin = "0 0";
//s.webkitTransform = "translateZ(0px)";
s.position = "relative";
/*
s.position = "absolute";
s.top = "0px";
s.left = "0px";
s.right = "0px"
s.bottom = "0px"
s.margin = "0px";
s.padding = "0px";
s.border = "1px solid red";
*/
/* i left all those comments above because they might be useful in some use cases */
document.body.appendChild(c);
return new Sprite3D(c);
};
/** Private static property. Used internally for browser checking. You should not change its value. */
Sprite3D.prototype._isInit = false;
/** Private static property. Used internally for cross-browser compatibility. You should not change its value. */
Sprite3D.prototype._transformProperty = "webkitTransform";
/** Private static property. Used internally for cross-browser compatibility. You should not change its value. */
Sprite3D.prototype._browserPrefix = "webkit";
/**
* Test for CSS 3D transforms support in the current browser.
* If 3D transforms are not supported, the update() method is replaced by the update2D() method,
* providing an automatic fallback that might save some bits :)
* @return {boolean} True if the 3D transforms are supported by the browser
*/
Sprite3D.isSupported = function() {
var d = document.createElement("div"),
prefixes = ["", "webkit", "Moz", "o", "ms" ],
n = prefixes.length, i;
// check for 3D transforms
for( i = 0; i < n; i++ ) {
if ( ( prefixes[i] + "Perspective" ) in d.style ) {
Sprite3D.prototype._transformProperty = prefixes[i] + "Transform";
Sprite3D.prototype._isInit = true;
Sprite3D.prototype._browserPrefix = prefixes[i];
//console.log( "found support for 3D transforms using prefix: " + prefixes[i] );
return true;
}
}
// check for 2D transforms
for( i = 0; i < n; i++ ) {
if ( ( prefixes[i] + "Transform" ) in d.style ) {
Sprite3D.prototype._transformProperty = prefixes[i] + "Transform";
Sprite3D.prototype._isInit = true;
Sprite3D.prototype._browserPrefix = prefixes[i];
Sprite3D.prototype.update = Sprite3D.prototype.update2D;
//console.log( "found support for 2D transforms using prefix: " + prefixes[i] );
return false;
}
}
//console.log( "no support for CSS transforms.");
return false;
};