Add relative move and rotate to rel plugin (#794)

The relative position in rel plugin is currently based on the world coordinate. So for the same effect, like fly in from the right-hand side, we must use different `data-rel-x/y/z` value. Why not let the plugin do the hard part?

So I introduce a `data-rel-position`, when set to `relative`, all relative attribute is based on the position and rotation of previous slide. So no matter the rotation of previous slide, data-rel-x="1000" always looks like fly in from the right-hand side. We can change the position and rotation of one slide, and the position of all following slides will be changed too.

When `data-rel-position` is set to `relative`, relative rotation has a clear meaning. It describes the relative rotations between slides. We don't need to set rotations for all slide, setting the key slides is enough. If `data-rel-position` is not relative, the effect of `data-rel-rotate-x/y/z` is not clear, so they're only used when `data-rel-position="relative"`.

After the introduction of relative rotation, there're 6 attribute that will inherit from previous slide. If we want to set a relative X move, we have to set all other 5 attributes to 0. It's boring. So a `data-rel-clear` is used to set all 6 attributes to 0, and then the value specified in current slide is applied. 

The `examples/3D-positions/index.html` shows some usage. As you can see, the html code of two slide ring is the same, and slides except for the first two in a ring has no position attributes. It work by inheriting the previous one.

This PR invokes a lot math calculations. Basically, the rotation of a slide is translated into the coordinate describing the directions of X/Y/Z axes. And `data-rel-x/y/z` can be easily calculated by that. The rotations is the hard part, I mainly use the algorithm in the Quaternions and spatial rotation - Wikipedia to compose two and more rotations.  I'm not a math guy, hope I don't make much mistakes.
This commit is contained in:
thawk
2022-04-25 02:37:50 +08:00
committed by GitHub
parent d3760dfa0a
commit 629f7686f3
18 changed files with 10537 additions and 101 deletions

View File

@@ -5,7 +5,7 @@ var Terser = require("terser");
var files = ['src/impress.js']; var files = ['src/impress.js'];
// Libraries from src/lib // Libraries from src/lib
files.push('src/lib/gc.js', 'src/lib/util.js') files.push('src/lib/gc.js', 'src/lib/util.js', 'src/lib/rotation.js')
// Plugins from src/plugins // Plugins from src/plugins
files.push('src/plugins/autoplay/autoplay.js', files.push('src/plugins/autoplay/autoplay.js',
'src/plugins/blackout/blackout.js', 'src/plugins/blackout/blackout.js',
@@ -71,4 +71,4 @@ html += '</body>\n</html>'
filename = path.resolve(__dirname, 'examples', 'index.html'); filename = path.resolve(__dirname, 'examples', 'index.html');
fs.writeFileSync(filename, html); fs.writeFileSync(filename, html);
console.log(filename); console.log(filename);

View File

@@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>relative rotations</title>
<link href="..\..\css\impress-common.css" rel="stylesheet" />
<style type="text/css" media="screen">
#overview {
background: none;
border: none;
box-shadow: none;
width: 1800px;
height: 1300px;
}
#overview div {
width: 100%;
height: 100%;
}
.step {
position: relative;
width: 1000px;
height: 1000px;
padding: 40px 60px;
margin: 20px auto;
box-sizing: border-box;
line-height: 1.5;
background-color: yellow;
border-radius: 10px;
box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
font-family: 'Open Sans', Arial, sans-serif;
font-size: 40pt;
letter-spacing: -1px;
border: solid 2px red;
opacity: 40%;
}
.step.active {
opacity: 100%;
}
.step.ring2 {
background-color: cyan;
}
.step.box {
background-color: purple;
opacity: 70%;
}
.step.box1 {
background-color: lightblue;
}
</style>
</head>
<body class="impress-not-supported">
<div id="impress" data-width="2000" data-height="1500">
<div id="overview" class="step overview" data-rel-position="relative" data-x="-1000" data-y="-1500" data-z="100" data-scale="3" data-rotate-x="45" data-rotate-y="10">
<div>
<h2>Demo of <code>data-rel-position</code></h2>
<p>This demo use <code>data-rel-position="relative"</code><br>
and <code>data-rel-rotate-x/y/z</code><br>
to easy 3D positioning of slides.</p>
</div>
</div>
<div id="box-front" class="step box" data-x="-3000" data-y="0" data-z="0" data-rotate-x="0" data-rotate-y="0" data-rotate-z="0">Front
<p>There's two nested box here.</p>
</div>
<div id="box-front1" class="step box1" data-rel-reset data-rel-z="-200" data-scale="0.6">Inside Front</div>
<div id="box-right1" class="step box1" data-rel-reset data-rel-x="300" data-rel-z="-300" data-rel-rotate-y="90" data-scale="0.6">Inside Right</div>
<div id="box-right" class="step box" data-rel-reset data-rel-z="200">Right</div>
<div id="box-back" class="step box" data-rel-reset data-rel-x="500" data-rel-z="-500" data-rel-rotate-y="90">Back</div>
<div id="box-back1" class="step box1" data-rel-reset data-rel-z="-200" data-scale="0.6">Inside Back</div>
<div id="box-top1" class="step box1" data-rel-reset data-rel-y="-300" data-rel-z="-300" data-rel-rotate-x="90" data-scale="0.6">Inside Top</div>
<div id="box-top" class="step box" data-rel-reset data-rel-z="200">Top</div>
<div id="box-left" class="step box" data-rel-reset data-rel-x="500" data-rel-z="-500" data-rel-rotate-y="90">Left</div>
<div id="box-left1" class="step box1" data-rel-reset data-rel-z="-200" data-scale="0.6">Inside Left</div>
<div id="box-bottom1" class="step box1" data-rel-reset data-rel-x="300" data-rel-z="-300" data-rel-rotate-y="90" data-scale="0.6">Inside Bottom</div>
<div id="box-bottom" class="step box" data-rel-reset data-rel-z="200">Bottom</div>
<div id="ring1-1" class="step" data-rel-reset="all" data-x="0" data-y="0" data-z="0" data-rotate-y="10">
<p>Slide one</p>
<p>This is a ring of 8 slides.</p>
<p>It's easy constucted with data-rel-position="relative" without calculates the coordinates of all slides.</p>
</div>
<div id="ring1-2" class="step" data-rel-rotate-y="45" data-rel-z="-354" data-rel-x="854">
<p>Slide two</p>
<p>The position of this slide is calculated as relatived position and rotation of the first slide.</p>
<p>The following slides don't need to set any position attributes, they are inherit from this slide.</p>
</div>
<div id="ring1-3" class="step">
<p>Slide three</p>
</div>
<div id="ring1-4" class="step">
<p>Slide four</p>
</div>
<div id="ring1-5" class="step">
<p>Slide five</p>
</div>
<div id="ring1-6" class="step">
<p>Slide six</p>
</div>
<div id="ring1-7" class="step">
<p>Slide seven</p>
</div>
<div id="ring1-8" class="step">
<p>Slide eight</p>
</div>
<div id="ring2-1" class="step ring2" data-rel-reset="all" data-x="-500" data-y="0" data-z="-1514" data-rotate-x="90" data-rotate-y="270" data-rotate-z="0">
<p>Slide one</p>
<p>This is another ring of slides.</p>
<p>Except for the this slide, its code is just cloned from the yellow ring.</p>
<p>Just change the position of first slide, all the following slides are position relatived to it.</p>
</div>
<div id="ring2-2" class="step ring2" data-rel-rotate-y="45" data-rel-z="-354" data-rel-x="854" data-rel-y="0">
<p>Slide two</p>
</div>
<div id="ring2-3" class="step ring2">
<p>Slide three</p>
</div>
<div id="ring2-4" class="step ring2">
<p>Slide four</p>
</div>
<div id="ring2-5" class="step ring2">
<p>Slide five</p>
</div>
<div id="ring2-6" class="step ring2">
<p>Slide six</p>
</div>
<div id="ring2-7" class="step ring2">
<p>Slide seven</p>
</div>
<div id="ring2-8" class="step ring2">
<p>Slide eight</p>
</div>
</div>
<div id="impress-toolbar"></div>
<div id="impress-help"></div>
<script type="text/javascript" src="../../js/impress.js"></script>
<script>impress().init();</script>
</body>
<html>

View File

@@ -188,7 +188,7 @@
Notation shouldn't be a surprise. We use `data-rotate="30"` attribute, meaning that this Notation shouldn't be a surprise. We use `data-rotate="30"` attribute, meaning that this
element should be rotated by 30 degrees clockwise. element should be rotated by 30 degrees clockwise.
--> -->
<div class="step slide" data-rel-x="2200" data-rel-y="600" data-rotate="30"> <div class="step slide" data-rel-position="relative" data-rel-x="2200" data-rel-y="600" data-rel-rotate-z="30">
<h1>A blockquote &amp; image</h1> <h1>A blockquote &amp; image</h1>
<img src="images/3476636111_c551295ca4_b.jpg" <img src="images/3476636111_c551295ca4_b.jpg"
alt="Mother Teresa holding a newborn baby" alt="Mother Teresa holding a newborn baby"
@@ -200,10 +200,14 @@
</blockquote> </blockquote>
<div class="notes"> <div class="notes">
We use <code>data-rel-position="relative"</code> to make <code>data-rel-rotate-*</code> work,
and make <code>data-rel-x/data-rel-y/data-rel-y</code> be calculated at the coordination related to previous slide.
The relative position and rotation will be inherited by following slides,
so it's not necessary to repeat it again and again.
</div> </div>
</div> </div>
<div class="step slide" data-rel-x="1600" data-rel-y="1600" data-rotate="60"> <div class="step slide">
<h1>More text styles</h1> <h1>More text styles</h1>
<p>As usual, use <em>em</em> to emphasize, <br /> <p>As usual, use <em>em</em> to emphasize, <br />
<strong>strong</strong> for strong, <u>u</u> for underline,<br /> <strong>strong</strong> for strong, <u>u</u> for underline,<br />
@@ -216,7 +220,7 @@
</div> </div>
</div> </div>
<div class="step slide" data-rel-x="600" data-rel-y="2200" data-rotate="90"> <div id="motions" class="step slide">
<h1>Motion effects 101</h1> <h1>Motion effects 101</h1>
<p>Items on the slide can</p> <p>Items on the slide can</p>
<p class="fly-in fly-out">Fly in</p> <p class="fly-in fly-out">Fly in</p>
@@ -246,22 +250,32 @@
</div> </div>
</div> </div>
<div id="addons" class="step slide title" data-rel-x="-600" data-rel-y="2200" data-rotate="120"> <div id="zoom" class="step" data-rel-reset data-rel-x="-0.25w" data-rel-y="0.5h" data-scale="0.5">
<div class="notes">
<p>This step zoom in to left bottom of previous slide to see to small text.</p>
<p>It's a empty and transparent.</p>
<p><code>data-rel-reset</code> is used to prevent this step from inheriting the relative positioning from previous slide.</p>
</div>
</div>
<div id="addons" class="step slide title" data-rel-to="motions">
<h2>Add-ons</h2> <h2>Add-ons</h2>
<div class="notes"> <div class="notes">
<p>This version of impress.js includes several add-ons, striving to make this a <p>This version of impress.js includes several add-ons, striving to make this a
full featured presentation app.</p> full featured presentation app.</p>
<p>The previous step breaks the slide flow, changes the relative position and rotation.</p>
<p>This slide use <code>data-rel-to</code> to inherit relative position and rotation from the slide before previous slide.</p>
</div> </div>
</div> </div>
<div class="step slide" data-rel-x="-1600" data-rel-y="1600" data-rotate="150" data-autoplay="3"> <div class="step slide" data-autoplay="3">
<h1>Impress.js plugins</h1> <h1>Impress.js plugins</h1>
<ul> <ul>
<li>A new <a href="https://github.com/impress/impress.js/blob/master/src/plugins/README.md">plugin framework</a> allows for rich extensibility, <li>A new <a href="https://github.com/impress/impress.js/blob/master/src/plugins/README.md">plugin framework</a> allows for rich extensibility,
without bloating the core rendering library. without bloating the core rendering library.
<ul> <ul>
<li class="substep">Press 'P' to open a presenter console.</li> <li class="substep">Press 'P' to open a presenter console.</li>
<li class="substep">When you move the mouse, navigation controls are visible on your bottom left</li> <li class="substep">When you move the mouse, navigation controls are visible on your bottom right</li>
<li class="substep">Autoplay makes the slides advance after a timeout</li> <li class="substep">Autoplay makes the slides advance after a timeout</li>
<li class="substep">Relative positioning plugin is often a more convenient way to position your slides when editing. (<a href="https://github.com/impress/impress.js/blob/master/examples/classic-slides/index.html">See html for this presentation.</a>)</li> <li class="substep">Relative positioning plugin is often a more convenient way to position your slides when editing. (<a href="https://github.com/impress/impress.js/blob/master/examples/classic-slides/index.html">See html for this presentation.</a>)</li>
</ul> </ul>
@@ -277,7 +291,7 @@
</div> </div>
</div> </div>
<div class="step slide" data-rel-x="-2200" data-rel-y="600" data-rotate="180"> <div class="step slide">
<h1>Highlight.js</h1> <h1>Highlight.js</h1>
<pre><code> <pre><code>
// `init` API function that initializes (and runs) the presentation. // `init` API function that initializes (and runs) the presentation.
@@ -303,7 +317,7 @@
</div> </div>
</div> </div>
<div class="step slide" data-rel-x="-2200" data-rel-y="-600" data-rotate="210"> <div class="step slide">
<h1>Mermaid.js</h1> <h1>Mermaid.js</h1>
<div class="mermaid"> <div class="mermaid">
%% This is a comment in mermaid markup %% This is a comment in mermaid markup
@@ -330,7 +344,7 @@
</div> </div>
</div> </div>
<div id="markdown" class="step slide markdown" data-rel-x="-1600" data-rel-y="-1600" data-rotate="240"> <div id="markdown" class="step slide markdown">
# Markdown.js # Markdown.js
* [Markdown.js](https://github.com/evilstreak/markdown-js) integration: for authors in a hurry! * [Markdown.js](https://github.com/evilstreak/markdown-js) integration: for authors in a hurry!
@@ -344,7 +358,7 @@
* [A more advanced Markdown presentation is here.](../markdown/) * [A more advanced Markdown presentation is here.](../markdown/)
</div> </div>
<div id="acme" class="step slide" data-rel-x="-600" data-rel-y="-2200" data-rotate="270"> <div id="acme" class="step slide">
<ul> <ul>
<li>Remember, in <em>impress.js</em> the full power of HTML5, CSS3 &amp; JavaScript is always at your fingertips!</li> <li>Remember, in <em>impress.js</em> the full power of HTML5, CSS3 &amp; JavaScript is always at your fingertips!</li>
<li>For example, you can use tables, forms, or dynamic charts as you would on any web page:</li> <li>For example, you can use tables, forms, or dynamic charts as you would on any web page:</li>

View File

@@ -5,6 +5,7 @@
<body><h1>Example presentations</h1> <body><h1>Example presentations</h1>
<ul><br /> <ul><br />
<li><a href="2D-navigation/">2D-navigation</a></li> <li><a href="2D-navigation/">2D-navigation</a></li>
<li><a href="3D-positions/">3D-positions</a></li>
<li><a href="3D-rotations/">3D-rotations</a></li> <li><a href="3D-rotations/">3D-rotations</a></li>
<li><a href="classic-slides/">classic-slides</a></li> <li><a href="classic-slides/">classic-slides</a></li>
<li><a href="cube/">cube</a></li> <li><a href="cube/">cube</a></li>

View File

@@ -1297,6 +1297,436 @@
} )( document, window ); } )( document, window );
/**
* Helper functions for rotation.
*
* Tommy Tam (c) 2021
* MIT License
*/
( function( document, window ) {
"use strict";
// Singleton library variables
var roots = [];
var libraryFactory = function( rootId ) {
if ( roots[ "impress-root-" + rootId ] ) {
return roots[ "impress-root-" + rootId ];
}
/**
* Round the number to 2 decimals, it's enough for use
*/
var roundNumber = function( num ) {
return Math.round( ( num + Number.EPSILON ) * 100 ) / 100;
};
/**
* Get the length/norm of a vector.
*
* https://en.wikipedia.org/wiki/Norm_(mathematics)
*/
var vectorLength = function( vec ) {
return Math.sqrt( vec.x * vec.x + vec.y * vec.y + vec.z * vec.z );
};
/**
* Dot product of two vectors.
*
* https://en.wikipedia.org/wiki/Dot_product
*/
var vectorDotProd = function( vec1, vec2 ) {
return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z;
};
/**
* Cross product of two vectors.
*
* https://en.wikipedia.org/wiki/Cross_product
*/
var vectorCrossProd = function( vec1, vec2 ) {
return {
x: vec1.y * vec2.z - vec1.z * vec2.y,
y: vec1.z * vec2.x - vec1.x * vec2.z,
z: vec1.x * vec2.y - vec1.y * vec2.x
};
};
/**
* Determine wheter a vector is a zero vector
*/
var isZeroVector = function( vec ) {
return !roundNumber( vec.x ) && !roundNumber( vec.y ) && !roundNumber( vec.z );
};
/**
* Scalar triple product of three vectors.
*
* It can be used to determine the handness of vectors.
*
* https://en.wikipedia.org/wiki/Triple_product#Scalar_triple_product
*/
var tripleProduct = function( vec1, vec2, vec3 ) {
return vectorDotProd( vectorCrossProd( vec1, vec2 ), vec3 );
};
/**
* The world/absolute unit coordinates.
*
* This coordinate is used by browser to position objects.
* It will not be affected by object rotations.
* All relative positions will finally be converted to this
* coordinate to be used.
*/
var worldUnitCoordinate = {
x: { x:1, y:0, z:0 },
y: { x:0, y:1, z:0 },
z: { x:0, y:0, z:1 }
};
/**
* Make quaternion from rotation axis and angle.
*
* q = [ cos(½θ), sin(½θ) axis ]
*
* If the angle is zero, returns the corresponded quaternion
* of axis.
*
* If the angle is not zero, returns the rotating quaternion
* which corresponds to rotation about the axis, by the angle θ.
*
* https://en.wikipedia.org/wiki/Quaternion
* https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
*/
var makeQuaternion = function( axis, theta = 0 ) {
var r = 0;
var t = 1;
if ( theta ) {
var radians = theta * Math.PI / 180;
r = Math.cos( radians / 2 );
t = Math.sin( radians / 2 ) / vectorLength( axis );
}
var q = [ r, axis.x * t, axis.y * t, axis.z * t ];
return q;
};
/**
* Extract vector from quaternion
*/
var quaternionToVector = function( quaternion ) {
return {
x: roundNumber( quaternion[ 1 ] ),
y: roundNumber( quaternion[ 2 ] ),
z: roundNumber( quaternion[ 3 ] )
};
};
/**
* Returns the conjugate quaternion of a quaternion
*
* https://en.wikipedia.org/wiki/Quaternion#Conjugation,_the_norm,_and_reciprocal
*/
var conjugateQuaternion = function( quaternion ) {
return [ quaternion[ 0 ], -quaternion[ 1 ], -quaternion[ 2 ], -quaternion[ 3 ] ];
};
/**
* Left multiple two quaternion.
*
* Is's used to combine two rotating quaternion into one.
*/
var leftMulQuaternion = function( q1, q2 ) {
return [
( q1[ 0 ] * q2[ 0 ] - q1[ 1 ] * q2[ 1 ] - q1[ 2 ] * q2[ 2 ] - q1[ 3 ] * q2[ 3 ] ),
( q1[ 1 ] * q2[ 0 ] + q1[ 0 ] * q2[ 1 ] - q1[ 3 ] * q2[ 2 ] + q1[ 2 ] * q2[ 3 ] ),
( q1[ 2 ] * q2[ 0 ] + q1[ 3 ] * q2[ 1 ] + q1[ 0 ] * q2[ 2 ] - q1[ 1 ] * q2[ 3 ] ),
( q1[ 3 ] * q2[ 0 ] - q1[ 2 ] * q2[ 1 ] + q1[ 1 ] * q2[ 2 ] + q1[ 0 ] * q2[ 3 ] )
];
};
/**
* Convert a rotation into a quaternion
*/
var rotationToQuaternion = function( baseCoordinate, rotation ) {
var order = rotation.order ? rotation.order : "xyz";
var axes = order.split( "" );
var result = [ 1, 0, 0, 0 ];
for ( var i = 0; i < axes.length; i++ ) {
var deg = rotation[ axes[ i ] ];
if ( !deg || ( Math.abs( deg ) < 0.0001 ) ) {
continue;
}
// All CSS rotation is based on the rotated coordinate
// So we need to calculate the rotated coordinate first
var coordinate = baseCoordinate;
if ( i > 0 ) {
coordinate = {
x: rotateByQuaternion( baseCoordinate.x, result ),
y: rotateByQuaternion( baseCoordinate.y, result ),
z: rotateByQuaternion( baseCoordinate.z, result )
};
}
result = leftMulQuaternion(
makeQuaternion( coordinate[ axes[ i ] ], deg ),
result );
}
return result;
};
/**
* Rotate a vector by a quaternion.
*/
var rotateByQuaternion = function( vec, quaternion ) {
var q = makeQuaternion( vec );
q = leftMulQuaternion(
leftMulQuaternion( quaternion, q ),
conjugateQuaternion( quaternion ) );
return quaternionToVector( q );
};
/**
* Rotate a vector by rotaion sequence.
*/
var rotateVector = function( baseCoordinate, vec, rotation ) {
var quaternion = rotationToQuaternion( baseCoordinate, rotation );
return rotateByQuaternion( vec, quaternion );
};
/**
* Given a rotation, return the rotationed coordinate
*/
var rotateCoordinate = function( coordinate, rotation ) {
var quaternion = rotationToQuaternion( coordinate, rotation );
return {
x: rotateByQuaternion( coordinate.x, quaternion ),
y: rotateByQuaternion( coordinate.y, quaternion ),
z: rotateByQuaternion( coordinate.z, quaternion )
};
};
/**
* Return the angle between two vector.
*
* The axis is used to determine the rotation direction.
*/
var angleBetweenTwoVector = function( axis, vec1, vec2 ) {
var vecLen1 = vectorLength( vec1 );
var vecLen2 = vectorLength( vec2 );
if ( !vecLen1 || !vecLen2 ) {
return 0;
}
var cos = vectorDotProd( vec1, vec2 ) / vecLen1 / vecLen2 ;
var angle = Math.acos( cos ) * 180 / Math.PI;
if ( tripleProduct( vec1, vec2, axis ) > 0 ) {
return angle;
} else {
return -angle;
}
};
/**
* Return the angle between a vector and a plane.
*
* The plane is determined by an axis and a vector on the plane.
*/
var angleBetweenPlaneAndVector = function( axis, planeVec, rotatedVec ) {
var norm = vectorCrossProd( axis, planeVec );
if ( isZeroVector( norm ) ) {
return 0;
}
return 90 - angleBetweenTwoVector( axis, rotatedVec, norm );
};
/**
* Calculated a order specified rotation sequence to
* transform from the world coordinate to required coordinate.
*/
var coordinateToOrderedRotation = function( coordinate, order ) {
var axis0 = order[ 0 ];
var axis1 = order[ 1 ];
var axis2 = order[ 2 ];
var reversedOrder = order.split( "" ).reverse().join( "" );
var rotate2 = angleBetweenPlaneAndVector(
coordinate[ axis2 ],
worldUnitCoordinate[ axis0 ],
coordinate[ axis0 ] );
// The r2 is the reverse of rotate for axis2
// The coordinate1 is the coordinate before rotate of axis2
var r2 = { order: reversedOrder };
r2[ axis2 ] = -rotate2;
var coordinate1 = rotateCoordinate( coordinate, r2 );
// Calculate the rotation for axis1
var rotate1 = angleBetweenTwoVector(
coordinate1[ axis1 ],
worldUnitCoordinate[ axis0 ],
coordinate1[ axis0 ] );
// Calculate the rotation for axis0
var rotate0 = angleBetweenTwoVector(
worldUnitCoordinate[ axis0 ],
worldUnitCoordinate[ axis1 ],
coordinate1[ axis1 ] );
var rotation = { };
rotation.order = order;
rotation[ axis0 ] = roundNumber( rotate0 );
rotation[ axis1 ] = roundNumber( rotate1 );
rotation[ axis2 ] = roundNumber( rotate2 );
return rotation;
};
/**
* Returns the possible rotations from unit coordinate
* to specified coordinate.
*/
var possibleRotations = function( coordinate ) {
var orders = [ "xyz", "xzy", "yxz", "yzx", "zxy", "zyx" ];
var rotations = [ ];
for ( var i = 0; i < orders.length; ++i ) {
rotations.push(
coordinateToOrderedRotation( coordinate, orders[ i ] )
);
}
return rotations;
};
/**
* Calculate a degree which in range (-180, 180] of baseDeg
*/
var nearestAngle = function( baseDeg, deg ) {
while ( deg > baseDeg + 180 ) {
deg -= 360;
}
while ( deg < baseDeg - 180 ) {
deg += 360;
}
return deg;
};
/**
* Given a base rotation and multiple rotations, return the best one.
*
* The best one is the one has least rotate from base.
*/
var bestRotation = function( baseRotate, rotations ) {
var bestScore;
var bestRotation;
for ( var i = 0; i < rotations.length; ++i ) {
var rotation = {
order: rotations[ i ].order,
x: nearestAngle( baseRotate.x, rotations[ i ].x ),
y: nearestAngle( baseRotate.y, rotations[ i ].y ),
z: nearestAngle( baseRotate.z, rotations[ i ].z )
};
var score = Math.abs( rotation.x - baseRotate.x ) +
Math.abs( rotation.y - baseRotate.y ) +
Math.abs( rotation.z - baseRotate.z );
if ( !i || ( score < bestScore ) ) {
bestScore = score;
bestRotation = rotation;
}
}
return bestRotation;
};
/**
* Given a coordinate, return the best rotation to achieve it.
*
* The baseRotate is used to select the near rotation from it.
*/
var coordinateToRotation = function( baseRotate, coordinate ) {
var rotations = possibleRotations( coordinate );
return bestRotation( baseRotate, rotations );
};
/**
* Apply a relative rotation to the base rotation.
*
* Calculate the coordinate after the rotation on each axis,
* and finally find out a one step rotation has the effect
* of two rotation.
*
* If there're multiple way to accomplish, select the one
* that is nearest to the base.
*
* Return one rotation has the same effect.
*/
var combineRotations = function( rotations ) {
// No rotation
if ( rotations.length <= 0 ) {
return { x:0, y:0, z:0, order:"xyz" };
}
// Find out the base coordinate
var coordinate = worldUnitCoordinate;
// One by one apply rotations in order
for ( var i = 0; i < rotations.length; i++ ) {
coordinate = rotateCoordinate( coordinate, rotations[ i ] );
}
// Calculate one rotation from unit coordinate to rotated
// coordinate. Because there're multiple possibles,
// select the one nearest to the base
var rotate = coordinateToRotation( rotations[ 0 ], coordinate );
return rotate;
};
var translateRelative = function( relative, prevRotation ) {
var result = rotateVector(
worldUnitCoordinate, relative, prevRotation );
result.rotate = combineRotations(
[ prevRotation, relative.rotate ] );
return result;
};
var lib = {
translateRelative: translateRelative
};
roots[ "impress-root-" + rootId ] = lib;
return lib;
};
// Let impress core know about the existence of this library
window.impress.addLibraryFactory( { rotation: libraryFactory } );
} )( document, window );
/** /**
* Autoplay plugin - Automatically advance slideshow after N seconds * Autoplay plugin - Automatically advance slideshow after N seconds
* *
@@ -3671,9 +4101,9 @@
( function( document, window ) { ( function( document, window ) {
"use strict"; "use strict";
var api;
var startingState = {}; var startingState = {};
var api;
var toNumber; var toNumber;
var toNumberAdvanced; var toNumberAdvanced;
@@ -3683,12 +4113,21 @@
if ( !prev ) { if ( !prev ) {
// For the first step, inherit these defaults // For the first step, inherit these defaults
prev = { x:0, y:0, z:0, relative: { x:0, y:0, z:0 } }; prev = {
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order:"xyz" },
relative: {
position: "absolute",
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order:"xyz" }
}
};
} }
var ref = prev;
if ( data.relTo ) { if ( data.relTo ) {
var ref = document.getElementById( data.relTo ); ref = document.getElementById( data.relTo );
if ( ref ) { if ( ref ) {
// Test, if it is a previous step that already has some assigned position data // Test, if it is a previous step that already has some assigned position data
@@ -3696,7 +4135,42 @@
prev.x = toNumber( ref.getAttribute( "data-x" ) ); prev.x = toNumber( ref.getAttribute( "data-x" ) );
prev.y = toNumber( ref.getAttribute( "data-y" ) ); prev.y = toNumber( ref.getAttribute( "data-y" ) );
prev.z = toNumber( ref.getAttribute( "data-z" ) ); prev.z = toNumber( ref.getAttribute( "data-z" ) );
prev.relative = {};
var prevPosition = ref.getAttribute( "data-rel-position" ) || "absolute";
if ( prevPosition !== "relative" ) {
// For compatibility with the old behavior, doesn't inherit otherthings,
// just like a reset.
prev.rotate = { x:0, y:0, z:0, order: "xyz" };
prev.relative = {
position: "absolute",
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order:"xyz" }
};
} else {
// For data-rel-position="relative", inherit all
prev.rotate.y = toNumber( ref.getAttribute( "data-rotate-y" ) );
prev.rotate.x = toNumber( ref.getAttribute( "data-rotate-x" ) );
prev.rotate.z = toNumber(
ref.getAttribute( "data-rotate-z" ) ||
ref.getAttribute( "data-rotate" ) );
// We also inherit relatives from relTo slide
prev.relative = {
position: prevPosition,
x: toNumberAdvanced( ref.getAttribute( "data-rel-x" ), 0 ),
y: toNumberAdvanced( ref.getAttribute( "data-rel-y" ), 0 ),
z: toNumberAdvanced( ref.getAttribute( "data-rel-z" ), 0 ),
rotate: {
x: toNumberAdvanced( ref.getAttribute( "data-rel-rotate-x" ), 0 ),
y: toNumberAdvanced( ref.getAttribute( "data-rel-rotate-y" ), 0 ),
z: toNumberAdvanced( ref.getAttribute( "data-rel-rotate-z" ), 0 ),
order: ( ref.getAttribute( "data-rel-rotate-order" ) || "xyz" )
}
};
}
} else { } else {
window.console.error( window.console.error(
"impress.js rel plugin: Step \"" + data.relTo + "\" is not defined " + "impress.js rel plugin: Step \"" + data.relTo + "\" is not defined " +
@@ -3717,34 +4191,95 @@
} }
} }
// While ``data-rel-reset="relative"`` or just ``data-rel-reset``,
// ``data-rel-x/y/z`` and ``data-rel-rotate-x/y/z`` will have default value of 0,
// instead of inherit from previous slide.
//
// If ``data-rel-reset="all"``, ``data-rotate-*`` are not inherited from previous slide too.
// So ``data-rel-reset="all" data-rotate-x="90"`` means
// ``data-rotate-x="90" data-rotate-y="0" data-rotate-z="0"``, we doesn't need to
// bother clearing all unneeded attributes.
var inheritRotation = true;
if ( el.hasAttribute( "data-rel-reset" ) ) {
// Don't inherit from prev, just use the relative setting for current element
prev.relative = {
position: prev.relative.position,
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order: "xyz" } };
if ( data.relReset === "all" ) {
inheritRotation = false;
}
}
var step = { var step = {
x: toNumber( data.x, prev.x ), x: toNumber( data.x, prev.x ),
y: toNumber( data.y, prev.y ), y: toNumber( data.y, prev.y ),
z: toNumber( data.z, prev.z ), z: toNumber( data.z, prev.z ),
rotate: {
x: toNumber( data.rotateX, 0 ),
y: toNumber( data.rotateY, 0 ),
z: toNumber( data.rotateZ || data.rotate, 0 ),
order: data.rotateOrder || "xyz"
},
relative: { relative: {
position: data.relPosition || prev.relative.position,
x: toNumberAdvanced( data.relX, prev.relative.x ), x: toNumberAdvanced( data.relX, prev.relative.x ),
y: toNumberAdvanced( data.relY, prev.relative.y ), y: toNumberAdvanced( data.relY, prev.relative.y ),
z: toNumberAdvanced( data.relZ, prev.relative.z ) z: toNumberAdvanced( data.relZ, prev.relative.z ),
rotate: {
x: toNumber( data.relRotateX, prev.relative.rotate.x ),
y: toNumber( data.relRotateY, prev.relative.rotate.y ),
z: toNumber( data.relRotateZ, prev.relative.rotate.z ),
order: data.rotateOrder || "xyz"
}
} }
}; };
// The final relatives maybe or maybe not the same with orignal data-rel-*
var relative = step.relative;
if ( step.relative.position === "relative" && inheritRotation ) {
// Calculate relatives based on previous slide
relative = api.lib.rotation.translateRelative(
step.relative, prev.rotate );
// Convert rotations to values that works with step.rotate
relative.rotate.x -= step.rotate.x;
relative.rotate.y -= step.rotate.y;
relative.rotate.z -= step.rotate.z;
}
// Relative position is ignored/zero if absolute is given. // Relative position is ignored/zero if absolute is given.
// Note that this also has the effect of resetting any inherited relative values. // Note that this also has the effect of resetting any inherited relative values.
if ( data.x !== undefined ) { if ( data.x !== undefined ) {
step.relative.x = 0; relative.x = step.relative.x = 0;
} }
if ( data.y !== undefined ) { if ( data.y !== undefined ) {
step.relative.y = 0; relative.y = step.relative.y = 0;
} }
if ( data.z !== undefined ) { if ( data.z !== undefined ) {
step.relative.z = 0; relative.z = step.relative.z = 0;
}
if ( data.rotateX !== undefined || !inheritRotation ) {
relative.rotate.x = step.relative.rotate.x = 0;
}
if ( data.rotateY !== undefined || !inheritRotation ) {
relative.rotate.y = step.relative.rotate.y = 0;
}
if ( data.rotateZ !== undefined || data.rotate !== undefined || !inheritRotation ) {
relative.rotate.z = step.relative.rotate.z = 0;
} }
// Apply relative position to absolute position, if non-zero step.x = step.x + relative.x;
// Note that at this point, the relative values contain a number value of pixels. step.y = step.y + relative.y;
step.x = step.x + step.relative.x; step.z = step.z + relative.z;
step.y = step.y + step.relative.y; step.rotate.x = step.rotate.x + relative.rotate.x;
step.z = step.z + step.relative.z; step.rotate.y = step.rotate.y + relative.rotate.y;
step.rotate.z = step.rotate.z + relative.rotate.z;
return step; return step;
}; };
@@ -3766,7 +4301,16 @@
z: el.getAttribute( "data-z" ), z: el.getAttribute( "data-z" ),
relX: el.getAttribute( "data-rel-x" ), relX: el.getAttribute( "data-rel-x" ),
relY: el.getAttribute( "data-rel-y" ), relY: el.getAttribute( "data-rel-y" ),
relZ: el.getAttribute( "data-rel-z" ) relZ: el.getAttribute( "data-rel-z" ),
rotateX: el.getAttribute( "data-rotate-x" ),
rotateY: el.getAttribute( "data-rotate-y" ),
rotateZ: el.getAttribute( "data-rotate-z" ),
rotate: el.getAttribute( "data-rotate" ),
relRotateX: el.getAttribute( "data-rel-rotate-x" ),
relRotateY: el.getAttribute( "data-rel-rotate-y" ),
relRotateZ: el.getAttribute( "data-rel-rotate-z" ),
relPosition: el.getAttribute( "data-rel-position" ),
rotateOrder: el.getAttribute( "data-rotate-order" )
} ); } );
var step = computeRelativePositions( el, prev ); var step = computeRelativePositions( el, prev );
@@ -3774,6 +4318,17 @@
el.setAttribute( "data-x", step.x ); el.setAttribute( "data-x", step.x );
el.setAttribute( "data-y", step.y ); el.setAttribute( "data-y", step.y );
el.setAttribute( "data-z", step.z ); el.setAttribute( "data-z", step.z );
el.setAttribute( "data-rotate-x", step.rotate.x );
el.setAttribute( "data-rotate-y", step.rotate.y );
el.setAttribute( "data-rotate-z", step.rotate.z );
el.setAttribute( "data-rotate-order", step.rotate.order );
el.setAttribute( "data-rel-position", step.relative.position );
el.setAttribute( "data-rel-x", step.relative.x );
el.setAttribute( "data-rel-y", step.relative.y );
el.setAttribute( "data-rel-z", step.relative.z );
el.setAttribute( "data-rel-rotate-x", step.relative.rotate.x );
el.setAttribute( "data-rel-rotate-y", step.relative.rotate.y );
el.setAttribute( "data-rel-rotate-z", step.relative.rotate.z );
prev = step; prev = step;
} }
}; };
@@ -3787,28 +4342,27 @@
event.detail.api.lib.gc.pushCallback( function() { event.detail.api.lib.gc.pushCallback( function() {
var steps = startingState[ root.id ]; var steps = startingState[ root.id ];
var step; var step;
var attrs = [
[ "x", "relX" ],
[ "y", "relY" ],
[ "z", "relZ" ],
[ "rotate-x", "relRotateX" ],
[ "rotate-y", "relRotateY" ],
[ "rotate-z", "relRotateZ" ],
[ "rotate-order", "relRotateOrder" ]
];
while ( step = steps.pop() ) { while ( step = steps.pop() ) {
// Reset x/y/z in cases where this plugin has changed it. // Reset x/y/z in cases where this plugin has changed it.
if ( step.relX !== null ) { for ( var i = 0; i < attrs.length; i++ ) {
if ( step.x === null ) { if ( step[ attrs[ i ][ 1 ] ] !== null ) {
step.el.removeAttribute( "data-x" ); if ( step[ attrs[ i ][ 0 ] ] === null ) {
} else { step.el.removeAttribute( "data-" + attrs[ i ][ 0 ] );
step.el.setAttribute( "data-x", step.x ); } else {
} step.el.setAttribute(
} "data-" + attrs[ i ][ 0 ], step[ attrs[ i ][ 0 ] ] );
if ( step.relY !== null ) { }
if ( step.y === null ) {
step.el.removeAttribute( "data-y" );
} else {
step.el.setAttribute( "data-y", step.y );
}
}
if ( step.relZ !== null ) {
if ( step.z === null ) {
step.el.removeAttribute( "data-z" );
} else {
step.el.setAttribute( "data-z", step.z );
} }
} }
} }

View File

@@ -8,6 +8,7 @@ module.exports = function(config) {
// base path that will be used to resolve all patterns (eg. files, exclude) // base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '', basePath: '',
hostname: '127.0.0.1',
// frameworks to use // frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
@@ -23,11 +24,15 @@ module.exports = function(config) {
"test/non_default.js", "test/non_default.js",
"src/plugins/navigation/navigation_tests.js", "src/plugins/navigation/navigation_tests.js",
"test/plugins/rel/relative_to_screen_size_tests.js", "test/plugins/rel/relative_to_screen_size_tests.js",
"test/plugins/rel/rotation_tests.js",
"test/plugins/rel/padding_tests.js",
"test/plugins/rel/rel_to_tests.js",
// Presentation files, for the iframe // Presentation files, for the iframe
{pattern: "test/*.html", watched: true, served: true, included: false}, {pattern: "test/*.html", watched: true, served: true, included: false},
{pattern: "test/plugins/*/*.html", watched: true, served: true, included: false}, {pattern: "test/plugins/*/*.html", watched: true, served: true, included: false},
// JS files for iframe // JS files for iframe
{pattern: "js/impress.js", watched: true, served: true, included: false}, {pattern: "js/impress.js", watched: true, served: true, included: false},
{pattern: "node_modules/qunit-assert-close/qunit-assert-close.js", watched: false, served: true, included: true},
{pattern: "node_modules/syn/dist/global/syn.js", watched: false, served: true, included: false} {pattern: "node_modules/syn/dist/global/syn.js", watched: false, served: true, included: false}
], ],

8477
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,9 +24,9 @@
"scripts": { "scripts": {
"all": "npm run build && npm run test && npm run lint", "all": "npm run build && npm run test && npm run lint",
"build": "node build.js", "build": "node build.js",
"lint": "jshint src test/*.js && jscs src test/*.js", "lint": "npm exec -- jshint src test/*.js && npm exec -- jscs src test/*.js",
"new-lint": "eslint src test", "new-lint": "npm exec -- eslint src test",
"test": "karma start --log-level debug --single-run=true" "test": "npm exec -- karma start --log-level debug --single-run=true"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^6.8.0", "eslint": "^6.8.0",
@@ -39,8 +39,8 @@
"ls": "^0.2.1", "ls": "^0.2.1",
"puppeteer": "^2.1.1", "puppeteer": "^2.1.1",
"qunit": "^2.9.3", "qunit": "^2.9.3",
"qunit-assert-close": "^2.1.2",
"syn": "^0.14.1", "syn": "^0.14.1",
"terser": "^4.6.7" "terser": "^4.6.7"
}, }
"dependencies": {}
} }

View File

@@ -13,6 +13,7 @@
<div id="qunit"></div> <div id="qunit"></div>
<div id="qunit-fixture"></div> <div id="qunit-fixture"></div>
<script src="node_modules/qunit/qunit/qunit.js"></script> <script src="node_modules/qunit/qunit/qunit.js"></script>
<script src="node_modules/qunit-assert-close/qunit-assert-close.js"></script>
<!-- The QUnit tests. --> <!-- The QUnit tests. -->
<script src="test/helpers.js"></script> <script src="test/helpers.js"></script>
<!-- Core tests --> <!-- Core tests -->
@@ -21,5 +22,8 @@
<!-- Plugins --> <!-- Plugins -->
<script src="src/plugins/navigation/navigation_tests.js"></script> <script src="src/plugins/navigation/navigation_tests.js"></script>
<script src="test/plugins/rel/relative_to_screen_size_tests.js"></script> <script src="test/plugins/rel/relative_to_screen_size_tests.js"></script>
<script src="test/plugins/rel/rotation_tests.js"></script>
<script src="test/plugins/rel/padding_tests.js"></script>
<script src="test/plugins/rel/rel_to_tests.js"></script>
</body> </body>
</html> </html>

429
src/lib/rotation.js Normal file
View File

@@ -0,0 +1,429 @@
/**
* Helper functions for rotation.
*
* Tommy Tam (c) 2021
* MIT License
*/
( function( document, window ) {
"use strict";
// Singleton library variables
var roots = [];
var libraryFactory = function( rootId ) {
if ( roots[ "impress-root-" + rootId ] ) {
return roots[ "impress-root-" + rootId ];
}
/**
* Round the number to 2 decimals, it's enough for use
*/
var roundNumber = function( num ) {
return Math.round( ( num + Number.EPSILON ) * 100 ) / 100;
};
/**
* Get the length/norm of a vector.
*
* https://en.wikipedia.org/wiki/Norm_(mathematics)
*/
var vectorLength = function( vec ) {
return Math.sqrt( vec.x * vec.x + vec.y * vec.y + vec.z * vec.z );
};
/**
* Dot product of two vectors.
*
* https://en.wikipedia.org/wiki/Dot_product
*/
var vectorDotProd = function( vec1, vec2 ) {
return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z;
};
/**
* Cross product of two vectors.
*
* https://en.wikipedia.org/wiki/Cross_product
*/
var vectorCrossProd = function( vec1, vec2 ) {
return {
x: vec1.y * vec2.z - vec1.z * vec2.y,
y: vec1.z * vec2.x - vec1.x * vec2.z,
z: vec1.x * vec2.y - vec1.y * vec2.x
};
};
/**
* Determine wheter a vector is a zero vector
*/
var isZeroVector = function( vec ) {
return !roundNumber( vec.x ) && !roundNumber( vec.y ) && !roundNumber( vec.z );
};
/**
* Scalar triple product of three vectors.
*
* It can be used to determine the handness of vectors.
*
* https://en.wikipedia.org/wiki/Triple_product#Scalar_triple_product
*/
var tripleProduct = function( vec1, vec2, vec3 ) {
return vectorDotProd( vectorCrossProd( vec1, vec2 ), vec3 );
};
/**
* The world/absolute unit coordinates.
*
* This coordinate is used by browser to position objects.
* It will not be affected by object rotations.
* All relative positions will finally be converted to this
* coordinate to be used.
*/
var worldUnitCoordinate = {
x: { x:1, y:0, z:0 },
y: { x:0, y:1, z:0 },
z: { x:0, y:0, z:1 }
};
/**
* Make quaternion from rotation axis and angle.
*
* q = [ cos(½θ), sin(½θ) axis ]
*
* If the angle is zero, returns the corresponded quaternion
* of axis.
*
* If the angle is not zero, returns the rotating quaternion
* which corresponds to rotation about the axis, by the angle θ.
*
* https://en.wikipedia.org/wiki/Quaternion
* https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
*/
var makeQuaternion = function( axis, theta = 0 ) {
var r = 0;
var t = 1;
if ( theta ) {
var radians = theta * Math.PI / 180;
r = Math.cos( radians / 2 );
t = Math.sin( radians / 2 ) / vectorLength( axis );
}
var q = [ r, axis.x * t, axis.y * t, axis.z * t ];
return q;
};
/**
* Extract vector from quaternion
*/
var quaternionToVector = function( quaternion ) {
return {
x: roundNumber( quaternion[ 1 ] ),
y: roundNumber( quaternion[ 2 ] ),
z: roundNumber( quaternion[ 3 ] )
};
};
/**
* Returns the conjugate quaternion of a quaternion
*
* https://en.wikipedia.org/wiki/Quaternion#Conjugation,_the_norm,_and_reciprocal
*/
var conjugateQuaternion = function( quaternion ) {
return [ quaternion[ 0 ], -quaternion[ 1 ], -quaternion[ 2 ], -quaternion[ 3 ] ];
};
/**
* Left multiple two quaternion.
*
* Is's used to combine two rotating quaternion into one.
*/
var leftMulQuaternion = function( q1, q2 ) {
return [
( q1[ 0 ] * q2[ 0 ] - q1[ 1 ] * q2[ 1 ] - q1[ 2 ] * q2[ 2 ] - q1[ 3 ] * q2[ 3 ] ),
( q1[ 1 ] * q2[ 0 ] + q1[ 0 ] * q2[ 1 ] - q1[ 3 ] * q2[ 2 ] + q1[ 2 ] * q2[ 3 ] ),
( q1[ 2 ] * q2[ 0 ] + q1[ 3 ] * q2[ 1 ] + q1[ 0 ] * q2[ 2 ] - q1[ 1 ] * q2[ 3 ] ),
( q1[ 3 ] * q2[ 0 ] - q1[ 2 ] * q2[ 1 ] + q1[ 1 ] * q2[ 2 ] + q1[ 0 ] * q2[ 3 ] )
];
};
/**
* Convert a rotation into a quaternion
*/
var rotationToQuaternion = function( baseCoordinate, rotation ) {
var order = rotation.order ? rotation.order : "xyz";
var axes = order.split( "" );
var result = [ 1, 0, 0, 0 ];
for ( var i = 0; i < axes.length; i++ ) {
var deg = rotation[ axes[ i ] ];
if ( !deg || ( Math.abs( deg ) < 0.0001 ) ) {
continue;
}
// All CSS rotation is based on the rotated coordinate
// So we need to calculate the rotated coordinate first
var coordinate = baseCoordinate;
if ( i > 0 ) {
coordinate = {
x: rotateByQuaternion( baseCoordinate.x, result ),
y: rotateByQuaternion( baseCoordinate.y, result ),
z: rotateByQuaternion( baseCoordinate.z, result )
};
}
result = leftMulQuaternion(
makeQuaternion( coordinate[ axes[ i ] ], deg ),
result );
}
return result;
};
/**
* Rotate a vector by a quaternion.
*/
var rotateByQuaternion = function( vec, quaternion ) {
var q = makeQuaternion( vec );
q = leftMulQuaternion(
leftMulQuaternion( quaternion, q ),
conjugateQuaternion( quaternion ) );
return quaternionToVector( q );
};
/**
* Rotate a vector by rotaion sequence.
*/
var rotateVector = function( baseCoordinate, vec, rotation ) {
var quaternion = rotationToQuaternion( baseCoordinate, rotation );
return rotateByQuaternion( vec, quaternion );
};
/**
* Given a rotation, return the rotationed coordinate
*/
var rotateCoordinate = function( coordinate, rotation ) {
var quaternion = rotationToQuaternion( coordinate, rotation );
return {
x: rotateByQuaternion( coordinate.x, quaternion ),
y: rotateByQuaternion( coordinate.y, quaternion ),
z: rotateByQuaternion( coordinate.z, quaternion )
};
};
/**
* Return the angle between two vector.
*
* The axis is used to determine the rotation direction.
*/
var angleBetweenTwoVector = function( axis, vec1, vec2 ) {
var vecLen1 = vectorLength( vec1 );
var vecLen2 = vectorLength( vec2 );
if ( !vecLen1 || !vecLen2 ) {
return 0;
}
var cos = vectorDotProd( vec1, vec2 ) / vecLen1 / vecLen2 ;
var angle = Math.acos( cos ) * 180 / Math.PI;
if ( tripleProduct( vec1, vec2, axis ) > 0 ) {
return angle;
} else {
return -angle;
}
};
/**
* Return the angle between a vector and a plane.
*
* The plane is determined by an axis and a vector on the plane.
*/
var angleBetweenPlaneAndVector = function( axis, planeVec, rotatedVec ) {
var norm = vectorCrossProd( axis, planeVec );
if ( isZeroVector( norm ) ) {
return 0;
}
return 90 - angleBetweenTwoVector( axis, rotatedVec, norm );
};
/**
* Calculated a order specified rotation sequence to
* transform from the world coordinate to required coordinate.
*/
var coordinateToOrderedRotation = function( coordinate, order ) {
var axis0 = order[ 0 ];
var axis1 = order[ 1 ];
var axis2 = order[ 2 ];
var reversedOrder = order.split( "" ).reverse().join( "" );
var rotate2 = angleBetweenPlaneAndVector(
coordinate[ axis2 ],
worldUnitCoordinate[ axis0 ],
coordinate[ axis0 ] );
// The r2 is the reverse of rotate for axis2
// The coordinate1 is the coordinate before rotate of axis2
var r2 = { order: reversedOrder };
r2[ axis2 ] = -rotate2;
var coordinate1 = rotateCoordinate( coordinate, r2 );
// Calculate the rotation for axis1
var rotate1 = angleBetweenTwoVector(
coordinate1[ axis1 ],
worldUnitCoordinate[ axis0 ],
coordinate1[ axis0 ] );
// Calculate the rotation for axis0
var rotate0 = angleBetweenTwoVector(
worldUnitCoordinate[ axis0 ],
worldUnitCoordinate[ axis1 ],
coordinate1[ axis1 ] );
var rotation = { };
rotation.order = order;
rotation[ axis0 ] = roundNumber( rotate0 );
rotation[ axis1 ] = roundNumber( rotate1 );
rotation[ axis2 ] = roundNumber( rotate2 );
return rotation;
};
/**
* Returns the possible rotations from unit coordinate
* to specified coordinate.
*/
var possibleRotations = function( coordinate ) {
var orders = [ "xyz", "xzy", "yxz", "yzx", "zxy", "zyx" ];
var rotations = [ ];
for ( var i = 0; i < orders.length; ++i ) {
rotations.push(
coordinateToOrderedRotation( coordinate, orders[ i ] )
);
}
return rotations;
};
/**
* Calculate a degree which in range (-180, 180] of baseDeg
*/
var nearestAngle = function( baseDeg, deg ) {
while ( deg > baseDeg + 180 ) {
deg -= 360;
}
while ( deg < baseDeg - 180 ) {
deg += 360;
}
return deg;
};
/**
* Given a base rotation and multiple rotations, return the best one.
*
* The best one is the one has least rotate from base.
*/
var bestRotation = function( baseRotate, rotations ) {
var bestScore;
var bestRotation;
for ( var i = 0; i < rotations.length; ++i ) {
var rotation = {
order: rotations[ i ].order,
x: nearestAngle( baseRotate.x, rotations[ i ].x ),
y: nearestAngle( baseRotate.y, rotations[ i ].y ),
z: nearestAngle( baseRotate.z, rotations[ i ].z )
};
var score = Math.abs( rotation.x - baseRotate.x ) +
Math.abs( rotation.y - baseRotate.y ) +
Math.abs( rotation.z - baseRotate.z );
if ( !i || ( score < bestScore ) ) {
bestScore = score;
bestRotation = rotation;
}
}
return bestRotation;
};
/**
* Given a coordinate, return the best rotation to achieve it.
*
* The baseRotate is used to select the near rotation from it.
*/
var coordinateToRotation = function( baseRotate, coordinate ) {
var rotations = possibleRotations( coordinate );
return bestRotation( baseRotate, rotations );
};
/**
* Apply a relative rotation to the base rotation.
*
* Calculate the coordinate after the rotation on each axis,
* and finally find out a one step rotation has the effect
* of two rotation.
*
* If there're multiple way to accomplish, select the one
* that is nearest to the base.
*
* Return one rotation has the same effect.
*/
var combineRotations = function( rotations ) {
// No rotation
if ( rotations.length <= 0 ) {
return { x:0, y:0, z:0, order:"xyz" };
}
// Find out the base coordinate
var coordinate = worldUnitCoordinate;
// One by one apply rotations in order
for ( var i = 0; i < rotations.length; i++ ) {
coordinate = rotateCoordinate( coordinate, rotations[ i ] );
}
// Calculate one rotation from unit coordinate to rotated
// coordinate. Because there're multiple possibles,
// select the one nearest to the base
var rotate = coordinateToRotation( rotations[ 0 ], coordinate );
return rotate;
};
var translateRelative = function( relative, prevRotation ) {
var result = rotateVector(
worldUnitCoordinate, relative, prevRotation );
result.rotate = combineRotations(
[ prevRotation, relative.rotate ] );
return result;
};
var lib = {
translateRelative: translateRelative
};
roots[ "impress-root-" + rootId ] = lib;
return lib;
};
// Let impress core know about the existence of this library
window.impress.addLibraryFactory( { rotation: libraryFactory } );
} )( document, window );

View File

@@ -22,6 +22,13 @@ Following html attributes are supported for step elements:
data-rel-z data-rel-z
data-rel-to data-rel-to
data-rel-rotate-x
data-rel-rotate-y
data-rel-rotate-z
data-rel-position
data-rel-reset
Non-zero values are also inherited from the previous step. This makes it easy to Non-zero values are also inherited from the previous step. This makes it easy to
create a boring presentation where each slide shifts for example 1000px down create a boring presentation where each slide shifts for example 1000px down
from the previous. from the previous.
@@ -47,6 +54,31 @@ If you need a reference to a step that is shown later make use of the goto plugi
<div id="shown-earlier" class="step" data-rel-to="shown-later" data-rel-x="1000" data-rel-y="500" data-goto-prev="shown-first" data-goto-next="shown-later"> <div id="shown-earlier" class="step" data-rel-to="shown-later" data-rel-x="1000" data-rel-y="500" data-goto-prev="shown-first" data-goto-next="shown-later">
<div id="shown-last" class="step" data-goto-prev="shown-later"> <div id="shown-last" class="step" data-goto-prev="shown-later">
Relative positioning
--------------------
All `data-rel-x`/`y`/`z` is used world coordinate by default. So the value should be alternated according to the rotation state of previous slides.
To easy relative positioning, the `data-rel-position` attribute is introduced.
`data-rel-position` has a default value of "absolute", which works like this attribute is not introduced.
When `data-rel-position="relative"`, everything changed. The rotation of previous is no need to be considered, you can set all the `data-rel-` attributes as if the previous has no rotation. This plugin will calculate the acual position according to the position and rotation of the previous slide and the relative settings. This make it possible to split a presentation into parts, construct each parts standalone without consider the final position, and then assemble them together.
For example, if you want the slide slided in from the right hand side, all you need is `data-rel-x="1000"`, no matter the rotation of the previous slide. If the previous slide has `data-rotate-z="90"`, the actual attribute of this slide will work like `data-rel-y="1000"`.
Not only relative positions, the relative rotations can be used while `data-rel-position="relative"`.
For example, `data-rel-rotate-y="45"` will make the slide has an angle of 45 degree to the previous slide. It make it easy to build a circle and do other complicated positioning.
If not set, the `data-rel-position` attribute will be inherited from previous slide. So we only need to set it at the first slide, then all done.
To avoid the boring need to set most `data-rel-*` to zero, but set only one or two ones, `data-rel-reset` attribute can be used:
- `data-rel-reset` equals to: `data-rel-x="0" data-rel-y="0" data-rel-z="0" data-rel-rotate-x="0" data-rel-rotate-y="0" data-rel-rotate-z="0"`
- `data-rel-reset="all"` works like `data-rel-reset`, in additions `data-rotate-x="0" data-rotate-y="0" data-rotate-z="0"`
When `data-rel-position="relative"` and `data-rel-to` is specified, `data-rotate-*` and `data-rel-*` will be inherited from specified slide too.
IMPORTANT: Incompatible change IMPORTANT: Incompatible change
------------------------------ ------------------------------

View File

@@ -48,9 +48,9 @@
( function( document, window ) { ( function( document, window ) {
"use strict"; "use strict";
var api;
var startingState = {}; var startingState = {};
var api;
var toNumber; var toNumber;
var toNumberAdvanced; var toNumberAdvanced;
@@ -60,12 +60,21 @@
if ( !prev ) { if ( !prev ) {
// For the first step, inherit these defaults // For the first step, inherit these defaults
prev = { x:0, y:0, z:0, relative: { x:0, y:0, z:0 } }; prev = {
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order:"xyz" },
relative: {
position: "absolute",
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order:"xyz" }
}
};
} }
var ref = prev;
if ( data.relTo ) { if ( data.relTo ) {
var ref = document.getElementById( data.relTo ); ref = document.getElementById( data.relTo );
if ( ref ) { if ( ref ) {
// Test, if it is a previous step that already has some assigned position data // Test, if it is a previous step that already has some assigned position data
@@ -73,7 +82,42 @@
prev.x = toNumber( ref.getAttribute( "data-x" ) ); prev.x = toNumber( ref.getAttribute( "data-x" ) );
prev.y = toNumber( ref.getAttribute( "data-y" ) ); prev.y = toNumber( ref.getAttribute( "data-y" ) );
prev.z = toNumber( ref.getAttribute( "data-z" ) ); prev.z = toNumber( ref.getAttribute( "data-z" ) );
prev.relative = {};
var prevPosition = ref.getAttribute( "data-rel-position" ) || "absolute";
if ( prevPosition !== "relative" ) {
// For compatibility with the old behavior, doesn't inherit otherthings,
// just like a reset.
prev.rotate = { x:0, y:0, z:0, order: "xyz" };
prev.relative = {
position: "absolute",
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order:"xyz" }
};
} else {
// For data-rel-position="relative", inherit all
prev.rotate.y = toNumber( ref.getAttribute( "data-rotate-y" ) );
prev.rotate.x = toNumber( ref.getAttribute( "data-rotate-x" ) );
prev.rotate.z = toNumber(
ref.getAttribute( "data-rotate-z" ) ||
ref.getAttribute( "data-rotate" ) );
// We also inherit relatives from relTo slide
prev.relative = {
position: prevPosition,
x: toNumberAdvanced( ref.getAttribute( "data-rel-x" ), 0 ),
y: toNumberAdvanced( ref.getAttribute( "data-rel-y" ), 0 ),
z: toNumberAdvanced( ref.getAttribute( "data-rel-z" ), 0 ),
rotate: {
x: toNumberAdvanced( ref.getAttribute( "data-rel-rotate-x" ), 0 ),
y: toNumberAdvanced( ref.getAttribute( "data-rel-rotate-y" ), 0 ),
z: toNumberAdvanced( ref.getAttribute( "data-rel-rotate-z" ), 0 ),
order: ( ref.getAttribute( "data-rel-rotate-order" ) || "xyz" )
}
};
}
} else { } else {
window.console.error( window.console.error(
"impress.js rel plugin: Step \"" + data.relTo + "\" is not defined " + "impress.js rel plugin: Step \"" + data.relTo + "\" is not defined " +
@@ -94,34 +138,95 @@
} }
} }
// While ``data-rel-reset="relative"`` or just ``data-rel-reset``,
// ``data-rel-x/y/z`` and ``data-rel-rotate-x/y/z`` will have default value of 0,
// instead of inherit from previous slide.
//
// If ``data-rel-reset="all"``, ``data-rotate-*`` are not inherited from previous slide too.
// So ``data-rel-reset="all" data-rotate-x="90"`` means
// ``data-rotate-x="90" data-rotate-y="0" data-rotate-z="0"``, we doesn't need to
// bother clearing all unneeded attributes.
var inheritRotation = true;
if ( el.hasAttribute( "data-rel-reset" ) ) {
// Don't inherit from prev, just use the relative setting for current element
prev.relative = {
position: prev.relative.position,
x:0, y:0, z:0,
rotate: { x:0, y:0, z:0, order: "xyz" } };
if ( data.relReset === "all" ) {
inheritRotation = false;
}
}
var step = { var step = {
x: toNumber( data.x, prev.x ), x: toNumber( data.x, prev.x ),
y: toNumber( data.y, prev.y ), y: toNumber( data.y, prev.y ),
z: toNumber( data.z, prev.z ), z: toNumber( data.z, prev.z ),
rotate: {
x: toNumber( data.rotateX, 0 ),
y: toNumber( data.rotateY, 0 ),
z: toNumber( data.rotateZ || data.rotate, 0 ),
order: data.rotateOrder || "xyz"
},
relative: { relative: {
position: data.relPosition || prev.relative.position,
x: toNumberAdvanced( data.relX, prev.relative.x ), x: toNumberAdvanced( data.relX, prev.relative.x ),
y: toNumberAdvanced( data.relY, prev.relative.y ), y: toNumberAdvanced( data.relY, prev.relative.y ),
z: toNumberAdvanced( data.relZ, prev.relative.z ) z: toNumberAdvanced( data.relZ, prev.relative.z ),
rotate: {
x: toNumber( data.relRotateX, prev.relative.rotate.x ),
y: toNumber( data.relRotateY, prev.relative.rotate.y ),
z: toNumber( data.relRotateZ, prev.relative.rotate.z ),
order: data.rotateOrder || "xyz"
}
} }
}; };
// The final relatives maybe or maybe not the same with orignal data-rel-*
var relative = step.relative;
if ( step.relative.position === "relative" && inheritRotation ) {
// Calculate relatives based on previous slide
relative = api.lib.rotation.translateRelative(
step.relative, prev.rotate );
// Convert rotations to values that works with step.rotate
relative.rotate.x -= step.rotate.x;
relative.rotate.y -= step.rotate.y;
relative.rotate.z -= step.rotate.z;
}
// Relative position is ignored/zero if absolute is given. // Relative position is ignored/zero if absolute is given.
// Note that this also has the effect of resetting any inherited relative values. // Note that this also has the effect of resetting any inherited relative values.
if ( data.x !== undefined ) { if ( data.x !== undefined ) {
step.relative.x = 0; relative.x = step.relative.x = 0;
} }
if ( data.y !== undefined ) { if ( data.y !== undefined ) {
step.relative.y = 0; relative.y = step.relative.y = 0;
} }
if ( data.z !== undefined ) { if ( data.z !== undefined ) {
step.relative.z = 0; relative.z = step.relative.z = 0;
}
if ( data.rotateX !== undefined || !inheritRotation ) {
relative.rotate.x = step.relative.rotate.x = 0;
}
if ( data.rotateY !== undefined || !inheritRotation ) {
relative.rotate.y = step.relative.rotate.y = 0;
}
if ( data.rotateZ !== undefined || data.rotate !== undefined || !inheritRotation ) {
relative.rotate.z = step.relative.rotate.z = 0;
} }
// Apply relative position to absolute position, if non-zero step.x = step.x + relative.x;
// Note that at this point, the relative values contain a number value of pixels. step.y = step.y + relative.y;
step.x = step.x + step.relative.x; step.z = step.z + relative.z;
step.y = step.y + step.relative.y; step.rotate.x = step.rotate.x + relative.rotate.x;
step.z = step.z + step.relative.z; step.rotate.y = step.rotate.y + relative.rotate.y;
step.rotate.z = step.rotate.z + relative.rotate.z;
return step; return step;
}; };
@@ -143,7 +248,16 @@
z: el.getAttribute( "data-z" ), z: el.getAttribute( "data-z" ),
relX: el.getAttribute( "data-rel-x" ), relX: el.getAttribute( "data-rel-x" ),
relY: el.getAttribute( "data-rel-y" ), relY: el.getAttribute( "data-rel-y" ),
relZ: el.getAttribute( "data-rel-z" ) relZ: el.getAttribute( "data-rel-z" ),
rotateX: el.getAttribute( "data-rotate-x" ),
rotateY: el.getAttribute( "data-rotate-y" ),
rotateZ: el.getAttribute( "data-rotate-z" ),
rotate: el.getAttribute( "data-rotate" ),
relRotateX: el.getAttribute( "data-rel-rotate-x" ),
relRotateY: el.getAttribute( "data-rel-rotate-y" ),
relRotateZ: el.getAttribute( "data-rel-rotate-z" ),
relPosition: el.getAttribute( "data-rel-position" ),
rotateOrder: el.getAttribute( "data-rotate-order" )
} ); } );
var step = computeRelativePositions( el, prev ); var step = computeRelativePositions( el, prev );
@@ -151,6 +265,17 @@
el.setAttribute( "data-x", step.x ); el.setAttribute( "data-x", step.x );
el.setAttribute( "data-y", step.y ); el.setAttribute( "data-y", step.y );
el.setAttribute( "data-z", step.z ); el.setAttribute( "data-z", step.z );
el.setAttribute( "data-rotate-x", step.rotate.x );
el.setAttribute( "data-rotate-y", step.rotate.y );
el.setAttribute( "data-rotate-z", step.rotate.z );
el.setAttribute( "data-rotate-order", step.rotate.order );
el.setAttribute( "data-rel-position", step.relative.position );
el.setAttribute( "data-rel-x", step.relative.x );
el.setAttribute( "data-rel-y", step.relative.y );
el.setAttribute( "data-rel-z", step.relative.z );
el.setAttribute( "data-rel-rotate-x", step.relative.rotate.x );
el.setAttribute( "data-rel-rotate-y", step.relative.rotate.y );
el.setAttribute( "data-rel-rotate-z", step.relative.rotate.z );
prev = step; prev = step;
} }
}; };
@@ -164,28 +289,27 @@
event.detail.api.lib.gc.pushCallback( function() { event.detail.api.lib.gc.pushCallback( function() {
var steps = startingState[ root.id ]; var steps = startingState[ root.id ];
var step; var step;
var attrs = [
[ "x", "relX" ],
[ "y", "relY" ],
[ "z", "relZ" ],
[ "rotate-x", "relRotateX" ],
[ "rotate-y", "relRotateY" ],
[ "rotate-z", "relRotateZ" ],
[ "rotate-order", "relRotateOrder" ]
];
while ( step = steps.pop() ) { while ( step = steps.pop() ) {
// Reset x/y/z in cases where this plugin has changed it. // Reset x/y/z in cases where this plugin has changed it.
if ( step.relX !== null ) { for ( var i = 0; i < attrs.length; i++ ) {
if ( step.x === null ) { if ( step[ attrs[ i ][ 1 ] ] !== null ) {
step.el.removeAttribute( "data-x" ); if ( step[ attrs[ i ][ 0 ] ] === null ) {
} else { step.el.removeAttribute( "data-" + attrs[ i ][ 0 ] );
step.el.setAttribute( "data-x", step.x ); } else {
} step.el.setAttribute(
} "data-" + attrs[ i ][ 0 ], step[ attrs[ i ][ 0 ] ] );
if ( step.relY !== null ) { }
if ( step.y === null ) {
step.el.removeAttribute( "data-y" );
} else {
step.el.setAttribute( "data-y", step.y );
}
}
if ( step.relZ !== null ) {
if ( step.z === null ) {
step.el.removeAttribute( "data-z" );
} else {
step.el.setAttribute( "data-z", step.z );
} }
} }
} }

View File

@@ -0,0 +1,160 @@
QUnit.module( "rel plugin padding tests" );
QUnit.test( "padding_relative", function( assert ) {
window.console.log( "Begin padding_relative" );
var done = assert.async();
loadIframe( "test/plugins/rel/padding_tests_presentation.html", assert, function() {
initPresentation( assert, function() {
var iframe = document.getElementById( "presentation-iframe" );
var iframeDoc = iframe.contentDocument;
var root = iframeDoc.querySelector( "div#impress" );
var origin = iframeDoc.querySelector( "div#origin" );
var origin_x = iframeDoc.querySelector( "div#origin_x" );
var origin_y = iframeDoc.querySelector( "div#origin_y" );
var origin_z = iframeDoc.querySelector( "div#origin_z" );
var x = iframeDoc.querySelector( "div#x" );
var x_x = iframeDoc.querySelector( "div#x_x" );
var x_y = iframeDoc.querySelector( "div#x_y" );
var x_z = iframeDoc.querySelector( "div#x_z" );
var y = iframeDoc.querySelector( "div#y" );
var y_x = iframeDoc.querySelector( "div#y_x" );
var y_y = iframeDoc.querySelector( "div#y_y" );
var y_z = iframeDoc.querySelector( "div#y_z" );
var z = iframeDoc.querySelector( "div#z" );
var z_x = iframeDoc.querySelector( "div#z_x" );
var z_y = iframeDoc.querySelector( "div#z_y" );
var z_z = iframeDoc.querySelector( "div#z_z" );
var reset = iframeDoc.querySelector( "div#reset" );
assert.close( origin.dataset.x, 0, 1, "origin data-x attribute" );
assert.close( origin.dataset.y, 0, 1, "origin data-y attribute" );
assert.close( origin.dataset.z, 0, 1, "origin data-z attribute" );
assert.close( origin.dataset.rotateX, 0, 1, "origin data-rotate-x" );
assert.close( origin.dataset.rotateY, 0, 1, "origin data-rotate-y" );
assert.close( origin.dataset.rotateZ, 0, 1, "origin data-rotate-z" );
assert.close( origin_x.dataset.x, 500, 1, "origin_x data-x attribute" );
assert.close( origin_x.dataset.y, 0, 1, "origin_x data-y attribute" );
assert.close( origin_x.dataset.z, 0, 1, "origin_x data-z attribute" );
assert.close( origin_x.dataset.rotateX, 0, 1, "origin_x data-rotate-x" );
assert.close( origin_x.dataset.rotateY, 0, 1, "origin_x data-rotate-y" );
assert.close( origin_x.dataset.rotateZ, 0, 1, "origin_x data-rotate-z" );
assert.close( origin_y.dataset.x, 0, 1, "origin_y data-x attribute" );
assert.close( origin_y.dataset.y, 500, 1, "origin_y data-y attribute" );
assert.close( origin_y.dataset.z, 0, 1, "origin_y data-z attribute" );
assert.close( origin_y.dataset.rotateX, 0, 1, "origin_y data-rotate-x" );
assert.close( origin_y.dataset.rotateY, 0, 1, "origin_y data-rotate-y" );
assert.close( origin_y.dataset.rotateZ, 0, 1, "origin_y data-rotate-z" );
assert.close( origin_z.dataset.x, 0, 1, "origin_z data-x attribute" );
assert.close( origin_z.dataset.y, 0, 1, "origin_z data-y attribute" );
assert.close( origin_z.dataset.z, 500, 1, "origin_z data-z attribute" );
assert.close( origin_z.dataset.rotateX, 0, 1, "origin_z data-rotate-x" );
assert.close( origin_z.dataset.rotateY, 0, 1, "origin_z data-rotate-y" );
assert.close( origin_z.dataset.rotateZ, 0, 1, "origin_z data-rotate-z" );
assert.close( x.dataset.x, 0, 1, "x data-x attribute" );
assert.close( x.dataset.y, 0, 1, "x data-y attribute" );
assert.close( x.dataset.z, 0, 1, "x data-z attribute" );
assert.close( x.dataset.rotateX, 90, 1, "x data-rotate-x" );
assert.close( x.dataset.rotateY, 0, 1, "x data-rotate-y" );
assert.close( x.dataset.rotateZ, 0, 1, "x data-rotate-z" );
assert.close( x_x.dataset.x, 500, 1, "x_x data-x attribute" );
assert.close( x_x.dataset.y, 0, 1, "x_x data-y attribute" );
assert.close( x_x.dataset.z, 0, 1, "x_x data-z attribute" );
assert.close( x_x.dataset.rotateX, 90, 1, "x_x data-rotate-x" );
assert.close( x_x.dataset.rotateY, 0, 1, "x_x data-rotate-y" );
assert.close( x_x.dataset.rotateZ, 0, 1, "x_x data-rotate-z" );
assert.close( x_y.dataset.x, 0, 1, "x_y data-x attribute" );
assert.close( x_y.dataset.y, 0, 1, "x_y data-y attribute" );
assert.close( x_y.dataset.z, 500, 1, "x_y data-z attribute" );
assert.close( x_y.dataset.rotateX, 90, 1, "x_y data-rotate-x" );
assert.close( x_y.dataset.rotateY, 0, 1, "x_y data-rotate-y" );
assert.close( x_y.dataset.rotateZ, 0, 1, "x_y data-rotate-z" );
assert.close( x_z.dataset.x, 0, 1, "x_z data-x attribute" );
assert.close( x_z.dataset.y, -500, 1, "x_z data-y attribute" );
assert.close( x_z.dataset.z, 0, 1, "x_z data-z attribute" );
assert.close( x_z.dataset.rotateX, 90, 1, "x_z data-rotate-x" );
assert.close( x_z.dataset.rotateY, 0, 1, "x_z data-rotate-y" );
assert.close( x_z.dataset.rotateZ, 0, 1, "x_z data-rotate-z" );
assert.close( y.dataset.x, 0, 1, "y data-x attribute" );
assert.close( y.dataset.y, 0, 1, "y data-y attribute" );
assert.close( y.dataset.z, 0, 1, "y data-z attribute" );
assert.close( y.dataset.rotateX, 0, 1, "y data-rotate-x" );
assert.close( y.dataset.rotateY, 90, 1, "y data-rotate-y" );
assert.close( y.dataset.rotateZ, 0, 1, "y data-rotate-z" );
assert.close( y_x.dataset.x, 0, 1, "y_x data-x attribute" );
assert.close( y_x.dataset.y, 0, 1, "y_x data-y attribute" );
assert.close( y_x.dataset.z, -500, 1, "y_x data-z attribute" );
assert.close( y_x.dataset.rotateX, 0, 1, "y_x data-rotate-x" );
assert.close( y_x.dataset.rotateY, 90, 1, "y_x data-rotate-y" );
assert.close( y_x.dataset.rotateZ, 0, 1, "y_x data-rotate-z" );
assert.close( y_y.dataset.x, 0, 1, "y_y data-x attribute" );
assert.close( y_y.dataset.y, 500, 1, "y_y data-y attribute" );
assert.close( y_y.dataset.z, 0, 1, "y_y data-z attribute" );
assert.close( y_y.dataset.rotateX, 0, 1, "y_y data-rotate-x" );
assert.close( y_y.dataset.rotateY, 90, 1, "y_y data-rotate-y" );
assert.close( y_y.dataset.rotateZ, 0, 1, "y_y data-rotate-z" );
assert.close( y_z.dataset.x, 500, 1, "y_z data-x attribute" );
assert.close( y_z.dataset.y, 0, 1, "y_z data-y attribute" );
assert.close( y_z.dataset.z, 0, 1, "y_z data-z attribute" );
assert.close( y_z.dataset.rotateX, 0, 1, "y_z data-rotate-x" );
assert.close( y_z.dataset.rotateY, 90, 1, "y_z data-rotate-y" );
assert.close( y_z.dataset.rotateZ, 0, 1, "y_z data-rotate-z" );
assert.close( z.dataset.x, 0, 1, "z data-x attribute" );
assert.close( z.dataset.y, 0, 1, "z data-y attribute" );
assert.close( z.dataset.z, 0, 1, "z data-z attribute" );
assert.close( z.dataset.rotateX, 0, 1, "z data-rotate-x" );
assert.close( z.dataset.rotateY, 0, 1, "z data-rotate-y" );
assert.close( z.dataset.rotateZ, 90, 1, "z data-rotate-z" );
assert.close( z_x.dataset.x, 0, 1, "z_x data-x attribute" );
assert.close( z_x.dataset.y, 500, 1, "z_x data-y attribute" );
assert.close( z_x.dataset.z, 0, 1, "z_x data-z attribute" );
assert.close( z_x.dataset.rotateX, 0, 1, "z_x data-rotate-x" );
assert.close( z_x.dataset.rotateY, 0, 1, "z_x data-rotate-y" );
assert.close( z_x.dataset.rotateZ, 90, 1, "z_x data-rotate-z" );
assert.close( z_y.dataset.x, -500, 1, "z_y data-x attribute" );
assert.close( z_y.dataset.y, 0, 1, "z_y data-y attribute" );
assert.close( z_y.dataset.z, 0, 1, "z_y data-z attribute" );
assert.close( z_y.dataset.rotateX, 0, 1, "z_y data-rotate-x" );
assert.close( z_y.dataset.rotateY, 0, 1, "z_y data-rotate-y" );
assert.close( z_y.dataset.rotateZ, 90, 1, "z_y data-rotate-z" );
assert.close( z_z.dataset.x, 0, 1, "z_z data-x attribute" );
assert.close( z_z.dataset.y, 0, 1, "z_z data-y attribute" );
assert.close( z_z.dataset.z, 500, 1, "z_z data-z attribute" );
assert.close( z_z.dataset.rotateX, 0, 1, "z_z data-rotate-x" );
assert.close( z_z.dataset.rotateY, 0, 1, "z_z data-rotate-y" );
assert.close( z_z.dataset.rotateZ, 90, 1, "z_z data-rotate-z" );
assert.close( reset.dataset.x, 100, 1, "reset data-x attribute" );
assert.close( reset.dataset.y, 200, 1, "reset data-y attribute" );
assert.close( reset.dataset.z, 800, 1, "reset data-z attribute" );
assert.close( reset.dataset.rotateX, 0, 1, "reset data-rotate-x" );
assert.close( reset.dataset.rotateY, 0, 1, "reset data-rotate-y" );
assert.close( reset.dataset.rotateZ, 0, 1, "reset data-rotate-z" );
done();
console.log( "End padding test (sync)" );
} )
} ); // LoadIframe()
} );

View File

@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>The presentation steps used in an iframe in rel/padding_tests.js</title>
<style type="text/css" media="screen">
.step {
position: relative;
width: 500px;
height: 500px;
padding: 40px 60px;
margin: 20px auto;
box-sizing: border-box;
line-height: 1.5;
background-color: yellow;
border-radius: 10px;
box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
font-family: 'Open Sans', Arial, sans-serif;
font-size: 40pt;
letter-spacing: -1px;
border: solid 1px red;
opacity: 50%;
}
</style>
</head>
<body class="impress-not-supported">
<div id="impress">
<div id="origin" class="step" data-rel-position="relative" data-x="0" data-y="0" data-z="0"></div>
<div id="origin_x" class="step" data-rel-to="origin" data-rel-reset data-rel-x="500"></div>
<div id="origin_y" class="step" data-rel-to="origin" data-rel-reset data-rel-y="500"></div>
<div id="origin_z" class="step" data-rel-to="origin" data-rel-reset data-rel-z="500"></div>
<div id="x" class="step" data-rel-reset="all" data-x="0" data-y="0" data-z="0" data-rotate-x="90"></div>
<div id="x_x" class="step" data-rel-reset data-rel-to="x" data-rel-x="500"></div>
<div id="x_y" class="step" data-rel-reset data-rel-to="x" data-rel-y="500"></div>
<div id="x_z" class="step" data-rel-reset data-rel-to="x" data-rel-z="500"></div>
<div id="y" class="step" data-rel-reset="all" data-x="0" data-y="0" data-z="0" data-rotate-y="90"></div>
<div id="y_x" class="step" data-rel-reset data-rel-to="y" data-rel-x="500"></div>
<div id="y_y" class="step" data-rel-reset data-rel-to="y" data-rel-y="500"></div>
<div id="y_z" class="step" data-rel-reset data-rel-to="y" data-rel-z="500"></div>
<div id="z" class="step" data-rel-reset="all" data-x="0" data-y="0" data-z="0" data-rotate-z="90"></div>
<div id="z_x" class="step" data-rel-reset data-rel-to="z" data-rel-x="500"></div>
<div id="z_y" class="step" data-rel-reset data-rel-to="z" data-rel-y="500"></div>
<div id="z_z" class="step" data-rel-reset data-rel-to="z" data-rel-z="500"></div>
<div id="reset" class="step" data-rel-reset="all" data-rel-x="100" data-rel-y="200" data-rel-z="300">
by data prel-reset="all", paddings should be calculated as if data-rotate-*=0
</div>
<div id="overview" class="step overview" data-x="0" data-y="-1000" data-z="100" data-scale="4" data-rotate-x="45">
</div>
</div>
<script src="../../../js/impress.js"></script>
<!-- <script>impress().init();</script> -->
</body>
<html>

View File

@@ -0,0 +1,147 @@
QUnit.module( "rel plugin rel_to tests" );
QUnit.test( "rel_to", function( assert ) {
window.console.log( "Begin rel_to" );
var done = assert.async();
loadIframe( "test/plugins/rel/rel_to_tests_presentation.html", assert, function() {
initPresentation( assert, function() {
var iframe = document.getElementById( "presentation-iframe" );
var iframeDoc = iframe.contentDocument;
var root = iframeDoc.querySelector( "div#impress" );
var abs1 = iframeDoc.querySelector( "div#abs-1" );
var abs2 = iframeDoc.querySelector( "div#abs-2" );
var abs3 = iframeDoc.querySelector( "div#abs-3" );
var abs4 = iframeDoc.querySelector( "div#abs-4" );
var abs5 = iframeDoc.querySelector( "div#abs-5" );
var abs6 = iframeDoc.querySelector( "div#abs-6" );
var relative1 = iframeDoc.querySelector( "div#relative-1" );
var relative2 = iframeDoc.querySelector( "div#relative-2" );
var relative3 = iframeDoc.querySelector( "div#relative-3" );
var relative4 = iframeDoc.querySelector( "div#relative-4" );
var relative5 = iframeDoc.querySelector( "div#relative-5" );
var relative6 = iframeDoc.querySelector( "div#relative-6" );
var relative7 = iframeDoc.querySelector( "div#relative-7" );
var relative8 = iframeDoc.querySelector( "div#relative-8" );
var relative9 = iframeDoc.querySelector( "div#relative-9" );
var relative10 = iframeDoc.querySelector( "div#relative-10" );
assert.close( abs1.dataset.x, 0, 1, "abs-1 data-x attribute" );
assert.close( abs1.dataset.y, 0, 1, "abs-1 data-y attribute" );
assert.close( abs1.dataset.z, 0, 1, "abs-1 data-z attribute" );
assert.close( abs1.dataset.rotateX, 0, 1, "abs-1 data-rotate-x" );
assert.close( abs1.dataset.rotateY, 0, 1, "abs-1 data-rotate-y" );
assert.close( abs1.dataset.rotateZ, 0, 1, "abs-1 data-rotate-z" );
assert.close( abs2.dataset.x, 854, 1, "abs-2 data-x attribute" );
assert.close( abs2.dataset.y, -125, 1, "abs-2 data-y attribute" );
assert.close( abs2.dataset.z, -354, 1, "abs-2 data-z attribute" );
assert.close( abs2.dataset.rotateX, 0, 1, "abs-2 data-rotate-x attribute" );
assert.close( abs2.dataset.rotateY, 45, 1, "abs-2 data-rotate-y attribute" );
assert.close( abs2.dataset.rotateZ, 0, 1, "abs-2 data-rotate-z attribute" );
assert.close( abs3.dataset.x, 1708, 1, "abs-3 data-x attribute" );
assert.close( abs3.dataset.y, -250, 1, "abs-3 data-y attribute" );
assert.close( abs3.dataset.z, -708, 1, "abs-3 data-z attribute" );
assert.close( abs3.dataset.rotateX, 0, 1, "abs-3 data-rotate-x attribute" );
assert.close( abs3.dataset.rotateY, 45, 1, "abs-3 data-rotate-y attribute" );
assert.close( abs3.dataset.rotateZ, 0, 1, "abs-3 data-rotate-z attribute" );
assert.close( abs4.dataset.x, 1808, 1, "abs-4 data-x attribute" );
assert.close( abs4.dataset.y, -250, 1, "abs-4 data-y attribute" );
assert.close( abs4.dataset.z, -708, 1, "abs-4 data-z attribute" );
assert.close( abs4.dataset.rotateX, 0, 1, "abs-4 data-rotate-x attribute" );
assert.close( abs4.dataset.rotateY, 0, 1, "abs-4 data-rotate-y attribute" );
assert.close( abs4.dataset.rotateZ, 0, 1, "abs-4 data-rotate-z attribute" );
assert.close( abs5.dataset.x, 1708, 1, "abs-5 data-x attribute" );
assert.close( abs5.dataset.y, -250, 1, "abs-5 data-y attribute" );
assert.close( abs5.dataset.z, -708, 1, "abs-5 data-z attribute" );
assert.close( abs5.dataset.rotateX, 0, 1, "abs-5 data-rotate-x attribute" );
assert.close( abs5.dataset.rotateY, 0, 1, "abs-5 data-rotate-y attribute" );
assert.close( abs5.dataset.rotateZ, 0, 1, "abs-5 data-rotate-z attribute" );
assert.close( abs6.dataset.x, 1708, 1, "abs-6 data-x attribute" );
assert.close( abs6.dataset.y, -250, 1, "abs-6 data-y attribute" );
assert.close( abs6.dataset.z, -708, 1, "abs-6 data-z attribute" );
assert.close( abs6.dataset.rotateX, 0, 1, "abs-6 data-rotate-x attribute" );
assert.close( abs6.dataset.rotateY, 0, 1, "abs-6 data-rotate-y attribute" );
assert.close( abs6.dataset.rotateZ, 0, 1, "abs-6 data-rotate-z attribute" );
assert.close( relative1.dataset.x, 0, 1, "relative-1 data-x attribute" );
assert.close( relative1.dataset.y, 1000, 1, "relative-1 data-y attribute" );
assert.close( relative1.dataset.z, 0, 1, "relative-1 data-z attribute" );
assert.close( relative1.dataset.rotateX, 0, 1, "relative-1 data-rotate-x" );
assert.close( relative1.dataset.rotateY, 0, 1, "relative-1 data-rotate-y" );
assert.close( relative1.dataset.rotateZ, 0, 1, "relative-1 data-rotate-z" );
assert.close( relative2.dataset.x, 854, 1, "relative-2 data-x attribute" );
assert.close( relative2.dataset.y, 875, 1, "relative-2 data-y attribute" );
assert.close( relative2.dataset.z, -354, 1, "relative-2 data-z attribute" );
assert.close( relative2.dataset.rotateX, 0, 1, "relative-2 data-rotate-x attribute" );
assert.close( relative2.dataset.rotateY, 45, 1, "relative-2 data-rotate-y attribute" );
assert.close( relative2.dataset.rotateZ, 0, 1, "relative-2 data-rotate-z attribute" );
assert.close( relative3.dataset.x, 1208, 1, "relative-3 data-x attribute" );
assert.close( relative3.dataset.y, 750, 1, "relative-3 data-y attribute" );
assert.close( relative3.dataset.z, -1208, 1, "relative-3 data-z attribute" );
assert.close( relative3.dataset.rotateX, 0, 1, "relative-3 data-rotate-x attribute" );
assert.close( relative3.dataset.rotateY, 90, 1, "relative-3 data-rotate-y attribute" );
assert.close( relative3.dataset.rotateZ, 0, 1, "relative-3 data-rotate-z attribute" );
assert.close( relative4.dataset.x, 1308, 1, "relative-4 data-x attribute" );
assert.close( relative4.dataset.y, 750, 1, "relative-4 data-y attribute" );
assert.close( relative4.dataset.z, -1208, 1, "relative-4 data-z attribute" );
assert.close( relative4.dataset.rotateX, 0, 1, "relative-4 data-rotate-x attribute" );
assert.close( relative4.dataset.rotateY, 0, 1, "relative-4 data-rotate-y attribute" );
assert.close( relative4.dataset.rotateZ, 0, 1, "relative-4 data-rotate-z attribute" );
assert.close( relative5.dataset.x, 854, 1, "relative-5 data-x attribute" );
assert.close( relative5.dataset.y, 625, 1, "relative-5 data-y attribute" );
assert.close( relative5.dataset.z, -2062, 1, "relative-5 data-z attribute" );
assert.close( relative5.dataset.rotateX, 0, 1, "relative-5 data-rotate-x" );
assert.close( relative5.dataset.rotateY, 135, 1, "relative-5 data-rotate-y" );
assert.close( relative5.dataset.rotateZ, 0, 1, "relative-5 data-rotate-z" );
assert.close( relative6.dataset.x, 0, 1, "relative-6 data-x attribute" );
assert.close( relative6.dataset.y, 500, 1, "relative-6 data-y attribute" );
assert.close( relative6.dataset.z, -2416, 1, "relative-6 data-z attribute" );
assert.close( relative6.dataset.rotateX, 0, 1, "relative-6 data-rotate-x" );
assert.close( relative6.dataset.rotateY, 180, 1, "relative-6 data-rotate-y" );
assert.close( relative6.dataset.rotateZ, 0, 1, "relative-6 data-rotate-z" );
assert.close( relative7.dataset.x, 1208, 1, "relative-7 data-x attribute" );
assert.close( relative7.dataset.y, 750, 1, "relative-7 data-y attribute" );
assert.close( relative7.dataset.z, -1208, 1, "relative-7 data-z attribute" );
assert.close( relative7.dataset.rotateX, 0, 1, "relative-7 data-rotate-x" );
assert.close( relative7.dataset.rotateY, 90, 1, "relative-7 data-rotate-y" );
assert.close( relative7.dataset.rotateZ, 0, 1, "relative-7 data-rotate-z" );
assert.close( relative8.dataset.x, 1208, 1, "relative-8 data-x attribute" );
assert.close( relative8.dataset.y, 750, 1, "relative-8 data-y attribute" );
assert.close( relative8.dataset.z, -1208, 1, "relative-8 data-z attribute" );
assert.close( relative8.dataset.rotateX, 0, 1, "relative-8 data-rotate-x" );
assert.close( relative8.dataset.rotateY, 90, 1, "relative-8 data-rotate-y" );
assert.close( relative8.dataset.rotateZ, 0, 1, "relative-8 data-rotate-z" );
assert.close( relative9.dataset.x, 1208, 1, "relative-9 data-x attribute" );
assert.close( relative9.dataset.y, 750, 1, "relative-9 data-y attribute" );
assert.close( relative9.dataset.z, -1208, 1, "relative-9 data-z attribute" );
assert.close( relative9.dataset.rotateX, 0, 1, "relative-9 data-rotate-x" );
assert.close( relative9.dataset.rotateY, 0, 1, "relative-9 data-rotate-y" );
assert.close( relative9.dataset.rotateZ, 0, 1, "relative-9 data-rotate-z" );
assert.close( relative10.dataset.x, 1208, 1, "relative-10 data-x attribute" );
assert.close( relative10.dataset.y, 750, 1, "relative-10 data-y attribute" );
assert.close( relative10.dataset.z, -1208, 1, "relative-10 data-z attribute" );
assert.close( relative10.dataset.rotateX, 0, 1, "relative-10 data-rotate-x" );
assert.close( relative10.dataset.rotateY, 0, 1, "relative-10 data-rotate-y" );
assert.close( relative10.dataset.rotateZ, 0, 1, "relative-10 data-rotate-z" );
done();
console.log( "End rel_to test (sync)" );
} )
} ); // LoadIframe()
} );

View File

@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>The presentation steps used in an iframe in rel/rotation_tests.js</title>
<style type="text/css" media="screen">
.step {
position: relative;
width: 1000px;
height: 1000px;
padding: 40px 60px;
margin: 20px auto;
box-sizing: border-box;
line-height: 1.5;
background-color: none;
border-radius: 10px;
box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
font-family: 'Open Sans', Arial, sans-serif;
font-size: 40pt;
letter-spacing: -1px;
border: solid 1px red;
}
</style>
</head>
<body class="impress-not-supported">
<div id="impress" data-width="1920" data-height="1200">
<div id="overview" class="step overview" data-x="0" data-y="-1000" data-z="100" data-scale="4" data-rotate-x="45">
<h1>Testing related to <code>data-rel-to</code></h1>
</div>
<div id="abs-1" class="step" data-x="0" data-y="0" data-z="0">
<p>Testing default rel-position</p>
</div>
<div id="abs-2" class="step" data-rel-rotate-y="45" data-rel-z="-354" data-rel-x="854" data-rel-y="-125">
<p>default rel-position, rel-rotate not work</p>
</div>
<div id="abs-3" class="step">
<p>Should inherit rel-x/y/z, not rel-rotate-y</p>
</div>
<div id="abs-4" class="step" data-rel-reset data-rel-x="100">
<p>position out of flow</p>
</div>
<div id="abs-5" class="step" data-rel-to="abs-3">
<p>Should re-position at abs-3, but doesn't inherit from it</p>
</div>
<div id="abs-6" class="step">
<p>Should be the same as abs-5</p>
</div>
<div id="relative-1" class="step" data-rel-reset="all" data-rel-position="relative" data-x="0" data-y="1000" data-z="0">
<p>Testing rel-position="relative"</p>
</div>
<div id="relative-2" class="step" data-rel-rotate-y="45" data-rel-z="-354" data-rel-x="854" data-rel-y="-125">
<p>All rel-* will be inherited from previous slide</p>
</div>
<div id="relative-3" class="step">
<p>inherit again</p>
</div>
<div id="relative-4" class="step" data-rel-reset="all" data-rel-x="100">
<p>position out of flow</p>
</div>
<div id="relative-5" class="step" data-rel-to="relative-3">
<p>Should re-position at relative-3, and inherit all relatives from it</p>
</div>
<div id="relative-6" class="step">
<p>keep inheriting</p>
</div>
<div id="relative-7" class="step" data-rel-to="relative-3" data-rel-reset>
<p>Should re-position at relative-3, but clear all relative attributes</p>
</div>
<div id="relative-8" class="step">
<p>keep inheriting</p>
</div>
<div id="relative-9" class="step" data-rel-to="relative-3" data-rel-reset="all">
<p>Should re-position at relative-3, but clear data-rotate-* in addition to all relative attributes</p>
</div>
<div id="relative-10" class="step">
<p>keep inheriting</p>
</div>
</div>
<script src="../../../js/impress.js"></script>
<!-- <script>impress().init();</script> -->
</body>
<html>

View File

@@ -0,0 +1,101 @@
QUnit.module( "rel plugin rotation tests" );
QUnit.test( "rotation_relative", function( assert ) {
window.console.log( "Begin rotation_relative" );
var done = assert.async();
loadIframe( "test/plugins/rel/rotation_tests_presentation.html", assert, function() {
initPresentation( assert, function() {
var iframe = document.getElementById( "presentation-iframe" );
var iframeDoc = iframe.contentDocument;
var root = iframeDoc.querySelector( "div#impress" );
var step1 = iframeDoc.querySelector( "div#step-1" );
var step2 = iframeDoc.querySelector( "div#step-2" );
var step3 = iframeDoc.querySelector( "div#step-3" );
var step4 = iframeDoc.querySelector( "div#step-4" );
var step5 = iframeDoc.querySelector( "div#step-5" );
var step6 = iframeDoc.querySelector( "div#step-6" );
var step7 = iframeDoc.querySelector( "div#step-7" );
var step8 = iframeDoc.querySelector( "div#step-8" );
var reset = iframeDoc.querySelector( "div#reset" );
var rotate = iframeDoc.querySelector( "div#rotate" );
assert.close( step1.dataset.x, 0, 1, "step-1 data-x attribute" );
assert.close( step1.dataset.y, 0, 1, "step-1 data-y attribute" );
assert.close( step1.dataset.z, 0, 1, "step-1 data-z attribute" );
assert.close( step1.dataset.rotateX, 0, 1, "step-1 data-rotate-x" );
assert.close( step1.dataset.rotateY, 0, 1, "step-1 data-rotate-y" );
assert.close( step1.dataset.rotateZ, 0, 1, "step-1 data-rotate-z" );
assert.close( step2.dataset.x, 854, 1, "step-2 data-x attribute" );
assert.close( step2.dataset.y, -125, 1, "step-2 data-y attribute" );
assert.close( step2.dataset.z, -354, 1, "step-2 data-z attribute" );
assert.close( step2.dataset.rotateX, 0, 1, "step-2 data-rotate-x attribute" );
assert.close( step2.dataset.rotateY, 45, 1, "step-2 data-rotate-y attribute" );
assert.close( step2.dataset.rotateZ, 0, 1, "step-2 data-rotate-z attribute" );
assert.close( step3.dataset.x, 1208, 1, "step-3 data-x attribute" );
assert.close( step3.dataset.y, -250, 1, "step-3 data-y attribute" );
assert.close( step3.dataset.z, -1208, 1, "step-3 data-z attribute" );
assert.close( step3.dataset.rotateX, 0, 1, "step-3 data-rotate-x attribute" );
assert.close( step3.dataset.rotateY, 90, 1, "step-3 data-rotate-y attribute" );
assert.close( step3.dataset.rotateZ, 0, 1, "step-3 data-rotate-z attribute" );
assert.close( step4.dataset.x, 854, 1, "step-4 data-x attribute" );
assert.close( step4.dataset.y, -375, 1, "step-4 data-y attribute" );
assert.close( step4.dataset.z, -2062, 1, "step-4 data-z attribute" );
assert.close( step4.dataset.rotateX, 0, 1, "step-4 data-rotate-x" );
assert.close( step4.dataset.rotateY, 135, 1, "step-4 data-rotate-y" );
assert.close( step4.dataset.rotateZ, 0, 1, "step-4 data-rotate-z" );
assert.close( step5.dataset.x, 0, 1, "step-5 data-x attribute" );
assert.close( step5.dataset.y, -500, 1, "step-5 data-y attribute" );
assert.close( step5.dataset.z, -2416, 1, "step-5 data-z attribute" );
assert.close( step5.dataset.rotateX, 0, 1, "step-5 data-rotate-x" );
assert.close( step5.dataset.rotateY, 180, 1, "step-5 data-rotate-y" );
assert.close( step5.dataset.rotateZ, 0, 1, "step-5 data-rotate-z" );
assert.close( step6.dataset.x, -854, 1, "step-6 data-x attribute" );
assert.close( step6.dataset.y, -375, 1, "step-6 data-y attribute" );
assert.close( step6.dataset.z, -2062, 1, "step-6 data-z attribute" );
assert.close( step6.dataset.rotateX, 0, 1, "step-6 data-rotate-x" );
assert.close( step6.dataset.rotateY, 225, 1, "step-6 data-rotate-y" );
assert.close( step6.dataset.rotateZ, 0, 1, "step-6 data-rotate-z" );
assert.close( step7.dataset.x, -1208, 1, "step-7 data-x attribute" );
assert.close( step7.dataset.y, -250, 1, "step-7 data-y attribute" );
assert.close( step7.dataset.z, -1208, 1, "step-7 data-z attribute" );
assert.close( step7.dataset.rotateX, 0, 1, "step-7 data-rotate-x attribute" );
assert.close( step7.dataset.rotateY, 270, 1, "step-7 data-rotate-y attribute" );
assert.close( step7.dataset.rotateZ, 0, 1, "step-7 data-rotate-z attribute" );
assert.close( step8.dataset.x, -854, 1, "step-8 data-x attribute" );
assert.close( step8.dataset.y, -125, 1, "step-8 data-y attribute" );
assert.close( step8.dataset.z, -354, 1, "step-8 data-z attribute" );
assert.close( step8.dataset.rotateX, 0, 1, "step-8 data-rotate-x attribute" );
assert.close( step8.dataset.rotateY, 315, 1, "step-8 data-rotate-y attribute" );
assert.close( step8.dataset.rotateZ, 0, 1, "step-8 data-rotate-z attribute" );
assert.equal( reset.dataset.x, 0, "reset data-x attribute" );
assert.equal( reset.dataset.y, 0, "reset data-y attribute" );
assert.equal( reset.dataset.z, 0, "reset data-z attribute" );
assert.equal( reset.dataset.rotateX, 0, "reset data-rotate-x" );
assert.equal( reset.dataset.rotateY, 0, "reset data-rotate-y" );
assert.equal( reset.dataset.rotateZ, 0, "reset data-rotate-z" );
assert.equal( reset.dataset.rotateOrder, "zyx", "reset data-rotate-order" );
assert.equal( rotate.dataset.x, 0, "rotate data-x attribute" );
assert.equal( rotate.dataset.y, 0, "rotate data-y attribute" );
assert.equal( rotate.dataset.z, 0, "rotate data-z attribute" );
assert.equal( rotate.dataset.rotateX, 0, "rotate data-rotate-x" );
assert.equal( rotate.dataset.rotateY, 0, "rotate data-rotate-y" );
assert.equal( rotate.dataset.rotateZ, 123, "rotate data-rotate-z" );
assert.equal( rotate.dataset.rotateOrder, "xyz", "rotate data-rotate-order" );
done();
console.log( "End rotation test (sync)" );
} )
} ); // LoadIframe()
} );

View File

@@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>The presentation steps used in an iframe in rel/rotation_tests.js</title>
<style type="text/css" media="screen">
.step {
position: relative;
width: 1000px;
height: 1000px;
padding: 40px 60px;
margin: 20px auto;
box-sizing: border-box;
line-height: 1.5;
background-color: white;
border-radius: 10px;
box-shadow: 0 2px 6px rgba(0, 0, 0, .1);
text-shadow: 0 2px 2px rgba(0, 0, 0, .1);
font-family: 'Open Sans', Arial, sans-serif;
font-size: 40pt;
letter-spacing: -1px;
border: solid 1px red;
}
</style>
</head>
<body class="impress-not-supported">
<div id="impress">
<div id="overview" class="step overview" data-x="0" data-y="-1000" data-z="100" data-scale="4" data-rotate-x="45">
<h1>3D Rotations Demo</h1>
</div>
<div id="step-1" class="step" data-x="0" data-y="0" data-z="0" data-goto-prev="step-8">
<p>Slide one</p>
</div>
<div id="step-2" class="step" data-rel-position="relative" data-rel-rotate-y="45" data-rel-z="-354" data-rel-x="854" data-rel-y="-125">
<p>Slide two</p>
</div>
<div id="step-3" class="step">
<p>Slide three</p>
</div>
<div id="step-4" class="step">
<p>Slide four</p>
</div>
<div id="step-5" class="step">
<p>Slide five</p>
</div>
<div id="step-6" class="step" data-rel-y="125">
<p>Slide six</p>
</div>
<div id="step-7" class="step">
<p>Slide seven</p>
</div>
<div id="step-8" class="step" data-goto-next="step-1">
<p>Slide eight</p>
</div>
<div id="reset" class="step" data-x=0 data-y=0 data-z=0 data-rotate-x=0 data-rotate-y=0 data-rotate-z=0 data-rotate-order="zyx">
<p>Reset all properties</p>
</div>
<div id="rotate" class="step" data-rotate=123>
<p>Testing the data-rotate attribute</p>
</div>
</div>
<script src="../../../js/impress.js"></script>
<!-- <script>impress().init();</script> -->
</body>
<html>