From b56b16d1cfd42a434c7fe6b565cfdef7ebc07db3 Mon Sep 17 00:00:00 2001 From: Henrik Ingo Date: Sat, 21 Oct 2017 13:50:20 +0300 Subject: [PATCH] Add support for data-rotate-order="xyz" attribute. It turns out in CSS 3D, the order in which you specify for example the rotateX(), rotateY() and rotateZ() transformations matter. Each rotation is relative to the objects then-current position. Impress.js being hardwired to always do rotateX->rotateY->rotateZ was therefore limiting, and in fact there are some positions that can never be reached with an xyz order. The new data-rotate-order="" attribute allows to specify the order as a permutation of the 3 letters x, y, z, thus relaxing this limitation. See http://openlife.cc/blogs/2016/october/3d-rotations-css-and-impressjs for (much) more details. --- js/impress.js | 44 +++++++++++++++++++++++++++++++++++++------- src/impress.js | 44 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 14 deletions(-) diff --git a/js/impress.js b/js/impress.js index bf13517..08b0400 100644 --- a/js/impress.js +++ b/js/impress.js @@ -54,6 +54,28 @@ } )(); + var validateOrder = function( order, fallback ) { + var validChars = "xyz"; + var returnStr = ""; + if ( typeof order === "string" ) { + for ( var i in order.split( "" ) ) { + if ( validChars.indexOf( order[ i ] >= 0 ) ) { + returnStr += order[ i ]; + + // Each of x,y,z can be used only once. + validChars = validChars.split( order[ i ] ).join( "" ); + } + } + } + if ( returnStr ) { + return returnStr; + } else if ( fallback !== undefined ) { + return fallback; + } else { + return "xyz"; + } + }; + // `css` function applies the styles given in `props` object to the element // given as `el`. It runs all property names through `pfx` function to make // sure proper prefixed version of the property is used. @@ -79,11 +101,17 @@ // By default the rotations are in X Y Z order that can be reverted by passing `true` // as second parameter. var rotate = function( r, revert ) { - var rX = " rotateX(" + r.x + "deg) ", - rY = " rotateY(" + r.y + "deg) ", - rZ = " rotateZ(" + r.z + "deg) "; + var order = r.order ? r.order : "xyz"; + var css = ""; + var axes = order.split( "" ); + if ( revert ) { + axes = axes.reverse(); + } - return revert ? rZ + rY + rX : rX + rY + rZ; + for ( var i = 0; i < axes.length; i++ ) { + css += " rotate" + axes[ i ].toUpperCase() + "(" + r[ axes[ i ] ] + "deg)"; + } + return css; }; // `scale` builds a scale transform string for given data. @@ -259,7 +287,8 @@ rotate: { x: lib.util.toNumber( data.rotateX ), y: lib.util.toNumber( data.rotateY ), - z: lib.util.toNumber( data.rotateZ || data.rotate ) + z: lib.util.toNumber( data.rotateZ || data.rotate ), + order: validateOrder( data.rotateOrder ) }, scale: lib.util.toNumber( data.scale, 1 ), transitionDuration: lib.util.toNumber( @@ -359,7 +388,7 @@ // Set a default initial state of the canvas currentState = { translate: { x: 0, y: 0, z: 0 }, - rotate: { x: 0, y: 0, z: 0 }, + rotate: { x: 0, y: 0, z: 0, order: "xyz" }, scale: 1 }; @@ -459,7 +488,8 @@ rotate: { x: -step.rotate.x, y: -step.rotate.y, - z: -step.rotate.z + z: -step.rotate.z, + order: step.rotate.order }, translate: { x: -step.translate.x, diff --git a/src/impress.js b/src/impress.js index 6587fce..d8b243a 100644 --- a/src/impress.js +++ b/src/impress.js @@ -54,6 +54,28 @@ } )(); + var validateOrder = function( order, fallback ) { + var validChars = "xyz"; + var returnStr = ""; + if ( typeof order === "string" ) { + for ( var i in order.split( "" ) ) { + if ( validChars.indexOf( order[ i ] >= 0 ) ) { + returnStr += order[ i ]; + + // Each of x,y,z can be used only once. + validChars = validChars.split( order[ i ] ).join( "" ); + } + } + } + if ( returnStr ) { + return returnStr; + } else if ( fallback !== undefined ) { + return fallback; + } else { + return "xyz"; + } + }; + // `css` function applies the styles given in `props` object to the element // given as `el`. It runs all property names through `pfx` function to make // sure proper prefixed version of the property is used. @@ -79,11 +101,17 @@ // By default the rotations are in X Y Z order that can be reverted by passing `true` // as second parameter. var rotate = function( r, revert ) { - var rX = " rotateX(" + r.x + "deg) ", - rY = " rotateY(" + r.y + "deg) ", - rZ = " rotateZ(" + r.z + "deg) "; + var order = r.order ? r.order : "xyz"; + var css = ""; + var axes = order.split( "" ); + if ( revert ) { + axes = axes.reverse(); + } - return revert ? rZ + rY + rX : rX + rY + rZ; + for ( var i = 0; i < axes.length; i++ ) { + css += " rotate" + axes[ i ].toUpperCase() + "(" + r[ axes[ i ] ] + "deg)"; + } + return css; }; // `scale` builds a scale transform string for given data. @@ -259,7 +287,8 @@ rotate: { x: lib.util.toNumber( data.rotateX ), y: lib.util.toNumber( data.rotateY ), - z: lib.util.toNumber( data.rotateZ || data.rotate ) + z: lib.util.toNumber( data.rotateZ || data.rotate ), + order: validateOrder( data.rotateOrder ) }, scale: lib.util.toNumber( data.scale, 1 ), transitionDuration: lib.util.toNumber( @@ -359,7 +388,7 @@ // Set a default initial state of the canvas currentState = { translate: { x: 0, y: 0, z: 0 }, - rotate: { x: 0, y: 0, z: 0 }, + rotate: { x: 0, y: 0, z: 0, order: "xyz" }, scale: 1 }; @@ -459,7 +488,8 @@ rotate: { x: -step.rotate.x, y: -step.rotate.y, - z: -step.rotate.z + z: -step.rotate.z, + order: step.rotate.order }, translate: { x: -step.translate.x,