/*
|
* Optimized version of PerspectiveTransform.js
|
* by Edan Kwan
|
* website: http://www.edankwan.com/
|
* twitter: https://twitter.com/#!/edankwan
|
* Lab: www.edankwan.com/lab
|
*
|
* The original PerspectiveTransform.js is created by Israel Pastrana
|
* http://www.is-real.net/experiments/css3/wonder-webkit/js/real/display/PerspectiveTransform.js
|
*
|
* Matrix Libraries from a Java port of JAMA: A Java Matrix Package, http://math.nist.gov/javanumerics/jama/
|
* Developed by Dr Peter Coxhead: http://www.cs.bham.ac.uk/~pxc/
|
* Available here: http://www.cs.bham.ac.uk/~pxc/js/
|
*
|
* I simply removed some irrelevant variables and functions and merge everything into a smaller function. I also added some error checking functions and bug fixing things.
|
*/
|
(function (define) {
|
define(function(){
|
|
function PerspectiveTransform(element, width, height, useBackFacing){
|
|
this.element = element;
|
this.style = element.style;
|
this.computedStyle = window.getComputedStyle(element);
|
this.width = width;
|
this.height = height;
|
this.useBackFacing = !!useBackFacing;
|
|
this.topLeft = {x: 0, y: 0};
|
this.topRight = {x: width, y: 0};
|
this.bottomLeft = {x: 0, y: height};
|
this.bottomRight = {x: width, y: height};
|
this.calcStyle = '';
|
}
|
|
PerspectiveTransform.useDPRFix = false;
|
PerspectiveTransform.dpr = 1;
|
|
PerspectiveTransform.prototype = (function(){
|
|
var app = {
|
stylePrefix: ''
|
};
|
|
var aM = [[0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0]];
|
var bM = [0, 0, 0, 0, 0, 0, 0, 0];
|
|
function _setTransformStyleName(){
|
var testStyle = document.createElement('div').style;
|
app.stylePrefix =
|
'webkitTransform' in testStyle ? 'webkit' :
|
'MozTransform' in testStyle ? 'Moz' :
|
'msTransform' in testStyle ? 'ms' :
|
'';
|
PerspectiveTransform.transformStyleName = app.stylePrefix + (app.stylePrefix.length>0?'Transform':'transform');
|
PerspectiveTransform.transformDomStyleName = '-'+app.stylePrefix.toLowerCase()+'-transform';
|
PerspectiveTransform.transformOriginStyleName = app.stylePrefix + (app.stylePrefix.length>0?'TransformOrigin':'transformOrigin');
|
PerspectiveTransform.transformOriginDomStyleName = '-'+app.stylePrefix.toLowerCase()+'-transform-origin';
|
}
|
|
|
// Check the distances between each points and if there is some points with the distance lequal to or less than 1 pixel, then return true. Otherwise return false;
|
function _hasDistancesError(){
|
var lenX = this.topLeft.x - this.topRight.x;
|
var lenY = this.topLeft.y - this.topRight.y;
|
if(Math.sqrt(lenX * lenX + lenY * lenY)<=1) return true;
|
lenX = this.bottomLeft.x - this.bottomRight.x;
|
lenY = this.bottomLeft.y - this.bottomRight.y;
|
if(Math.sqrt(lenX * lenX + lenY * lenY)<=1) return true;
|
lenX = this.topLeft.x - this.bottomLeft.x;
|
lenY = this.topLeft.y - this.bottomLeft.y;
|
if(Math.sqrt(lenX * lenX + lenY * lenY)<=1) return true;
|
lenX = this.topRight.x - this.bottomRight.x;
|
lenY = this.topRight.y - this.bottomRight.y;
|
if( Math.sqrt(lenX * lenX + lenY * lenY)<=1) return true;
|
lenX = this.topLeft.x - this.bottomRight.x;
|
lenY = this.topLeft.y - this.bottomRight.y;
|
if( Math.sqrt(lenX * lenX + lenY * lenY)<=1) return true;
|
lenX = this.topRight.x - this.bottomLeft.x;
|
lenY = this.topRight.y - this.bottomLeft.y;
|
if( Math.sqrt(lenX * lenX + lenY * lenY)<=1) return true;
|
|
return false;
|
}
|
|
// Get the determinant of given 3 points
|
function _getDeterminant(p0, p1, p2){
|
return p0.x * p1.y + p1.x * p2.y + p2.x * p0.y - p0.y * p1.x - p1.y * p2.x - p2.y * p0.x;
|
}
|
|
// Return true if it is a concave polygon or if it is backfacing when the useBackFacing property is false. Otehrwise return true;
|
function _hasPolyonError(){
|
var det1 = _getDeterminant(this.topLeft, this.topRight, this.bottomRight);
|
var det2 = _getDeterminant(this.bottomRight, this.bottomLeft, this.topLeft);
|
if(this.useBackFacing){
|
if(det1*det2<=0) return true;
|
}else{
|
if(det1<=0||det2<=0) return true;
|
}
|
var det1 = _getDeterminant(this.topRight, this.bottomRight, this.bottomLeft);
|
var det2 = _getDeterminant(this.bottomLeft, this.topLeft, this.topRight);
|
if(this.useBackFacing){
|
if(det1*det2<=0) return true;
|
}else{
|
if(det1<=0||det2<=0) return true;
|
}
|
return false;
|
}
|
|
function checkError(){
|
if(_hasDistancesError.apply(this)) return 1; // Points are too close to each other.
|
if(_hasPolyonError.apply(this)) return 2; // Concave or backfacing if the useBackFacing property is false
|
return 0; // no error
|
}
|
|
function calc() {
|
var width = this.width;
|
var height = this.height;
|
|
// get the offset from the transfrom origin of the element
|
var offsetX = 0;
|
var offsetY = 0;
|
var offset = this.computedStyle.getPropertyValue(PerspectiveTransform.transformOriginDomStyleName);
|
if(offset.indexOf('px')>-1){
|
offset = offset.split('px');
|
offsetX = -parseFloat(offset[0]);
|
offsetY = -parseFloat(offset[1]);
|
}else if(offset.indexOf('%')>-1){
|
offset = offset.split('%');
|
offsetX = -parseFloat(offset[0]) * width / 100;
|
offsetY = -parseFloat(offset[1]) * height / 100;
|
}
|
|
// magic here:
|
var dst = [this.topLeft, this.topRight, this.bottomLeft, this.bottomRight];
|
var arr = [0, 1, 2, 3, 4, 5, 6, 7];
|
for(var i = 0; i < 4; i++) {
|
aM[i][0] = aM[i+4][3] = i & 1 ? width + offsetX : offsetX;
|
aM[i][1] = aM[i+4][4] = (i > 1 ? height + offsetY : offsetY);
|
aM[i][6] = (i & 1 ? -offsetX-width : -offsetX) * (dst[i].x + offsetX);
|
aM[i][7] = (i > 1 ? -offsetY-height : -offsetY) * (dst[i].x + offsetX);
|
aM[i+4][6] = (i & 1 ? -offsetX-width : -offsetX) * (dst[i].y + offsetY);
|
aM[i+4][7] = (i > 1 ? -offsetY-height : -offsetY) * (dst[i].y + offsetY);
|
bM[i] = (dst[i].x + offsetX);
|
bM[i + 4] = (dst[i].y + offsetY);
|
aM[i][2] = aM[i+4][5] = 1;
|
aM[i][3] = aM[i][4] = aM[i][5] = aM[i+4][0] = aM[i+4][1] = aM[i+4][2] = 0;
|
}
|
var kmax, sum;
|
var row;
|
var col = [];
|
var i, j, k, tmp;
|
for(var j = 0; j < 8; j++) {
|
for(var i = 0; i < 8; i++) col[i] = aM[i][j];
|
for(i = 0; i < 8; i++) {
|
row = aM[i];
|
kmax = i<j?i:j;
|
sum = 0.0;
|
for(var k = 0; k < kmax; k++) sum += row[k] * col[k];
|
row[j] = col[i] -= sum;
|
}
|
var p = j;
|
for(i = j + 1; i < 8; i++) {
|
if(Math.abs(col[i]) > Math.abs(col[p])) p = i;
|
}
|
if(p != j) {
|
for(k = 0; k < 8; k++) {
|
tmp = aM[p][k];
|
aM[p][k] = aM[j][k];
|
aM[j][k] = tmp;
|
}
|
tmp = arr[p];
|
arr[p] = arr[j];
|
arr[j] = tmp;
|
}
|
if(aM[j][j] != 0.0) for(i = j + 1; i < 8; i++) aM[i][j] /= aM[j][j];
|
}
|
for(i = 0; i < 8; i++) arr[i] = bM[arr[i]];
|
for(k = 0; k < 8; k++) {
|
for(i = k + 1; i < 8; i++) arr[i] -= arr[k] * aM[i][k];
|
}
|
for(k = 7; k > -1; k--) {
|
arr[k] /= aM[k][k];
|
for(i = 0; i < k; i++) arr[i] -= arr[k] * aM[i][k];
|
}
|
|
return this.calcStyle = 'matrix3d(' + arr[0].toFixed(9) + ',' + arr[3].toFixed(9) + ', 0,' + arr[6].toFixed(9) + ',' + arr[1].toFixed(9) + ',' + arr[4].toFixed(9) + ', 0,' + arr[7].toFixed(9) + ',0, 0, 1, 0,' + arr[2].toFixed(9) + ',' + arr[5].toFixed(9) + ', 0, 1)';
|
|
}
|
|
function update(style) {
|
|
style = style || this.calcStyle;
|
|
if(PerspectiveTransform.useDPRFix) {
|
var dpr = PerspectiveTransform.dpr;
|
style = 'scale(' + dpr + ',' + dpr + ')perspective(1000px)' + style + 'translateZ('+ ((1 - dpr) * 1000) + 'px)';
|
}
|
|
// use toFixed() just in case the Number became something like 3.10000001234e-9
|
return this.style[PerspectiveTransform.transformStyleName] = style;
|
}
|
|
_setTransformStyleName();
|
|
app.calc = calc;
|
app.update = update;
|
app.checkError = checkError;
|
|
return app;
|
|
|
})();
|
|
|
return PerspectiveTransform;
|
});
|
}(typeof define === "function" && define.amd ? define : function (app) {
|
window["PerspectiveTransform"] = app();
|
}));
|