blob: bd07895a3be89153f1d381f108c7fafc59a9a480 [file] [log] [blame]
<!DOCTYPE html>
<meta charset="UTF-8">
<script src="../resources/testharness.js"></script>
<script src="../resources/testharnessreport.js"></script>
<div id="target"></div>
<script>
// These tests target post multiplication interpolation behaviour of the transform property.
// https://drafts.csswg.org/css-transforms-1/#interpolation-of-transforms
function assertPostMultiplication(start, end) {
test(() => {
assert_true(interpolationUsesPostMultiplication(start, end));
}, 'Animating transform from ' + start + ' to ' + end + ' should use post multiplication');
}
function assertNoPostMultiplication(start, end) {
test(() => {
assert_false(interpolationUsesPostMultiplication(start, end));
}, 'Animating transform from ' + start + ' to ' + end + ' should not use post multiplication');
}
function interpolationUsesPostMultiplication(start, end) {
// This value suffix will cause post multiplication to interpolate differently than pairwise
// interpolation. Rotating by 370 degrees is equivalent to rotating by 10 degrees in the matrix
// representation.
var testStart = start + ' rotate(370deg)';
var testEnd = end + ' rotate(0deg)';
var animation = target.animate({transform: [toMatrix(testStart), toMatrix(testEnd)]}, 1);
animation.currentTime = 0.5;
var postMultipliedInterpolation = getComputedStyle(target).transform;
animation.cancel();
animation = target.animate({transform: [testStart, testEnd]}, 1);
animation.currentTime = 0.5;
var interpolation = getComputedStyle(target).transform;
animation.cancel();
return matricesApproxEqual(interpolation, postMultipliedInterpolation, 0.01);
}
function toMatrix(transform) {
target.style.transform = transform;
var matrix = getComputedStyle(target).transform;
target.style.transform = '';
return matrix;
}
function matricesApproxEqual(actualMatrix, expectedMatrix, epsilon) {
var extractmatrix = function(matrixStr) {
var list = [];
var regex = /[+\-]?[0-9]+[.]?[0-9]*(e[+/-][0-9]+)?/g;
var match = undefined;
do {
match = regex.exec(matrixStr);
if (match) {
list.push(parseFloat(match[0]));
}
} while (match);
return list;
}
var actualNumbers = extractmatrix(actualMatrix);
var expectedNumbers = extractmatrix(expectedMatrix);
if (actualNumbers.length !== expectedNumbers.length) {
return false;
}
for (var i = 0; i < actualNumbers.length; i++) {
if (Math.abs(actualNumbers[i] - expectedNumbers[i]) > epsilon) {
return false;
}
}
return true;
}
// Verify that the transform prefix generates different interpolations based on matrix and pairwise
// interpolation.
assertNoPostMultiplication('', '');
// Transforms are not of the same type: fallback to 4x4 matrix.
assertPostMultiplication('translateX(10px)', 'matrix(1, 1, 0, 1, 1, 1)');
assertPostMultiplication('translateX(10px)', 'rotate(90deg)');
assertPostMultiplication('skewX(45deg)', 'translateX(10px)');
assertPostMultiplication(
'matrix(1, 0, 0, 1, 0, 0)',
'matrix3d(2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1)');
// TODO(kevers): Investigate if skewX(a) and skewY(b) should map to skew(a, 0) and skew(0, b),
// respectively. If correct, then pairwise interpolation should be used.
assertPostMultiplication('skewX(15deg)', 'skewY(15deg)');
assertPostMultiplication('skew(15deg, 0deg)', 'skewY(15deg)');
// Transform pairs can be expressed in the form of a common primitive.
assertNoPostMultiplication('translate(10px, 20px)', 'translateX(100px)');
assertNoPostMultiplication('translateX(10px)', 'translateY(100px)');
assertNoPostMultiplication('translateX(10px)', 'translateZ(100px)');
assertNoPostMultiplication('translateZ(10px)', 'translate3d(100px, 20px, 30px)');
assertNoPostMultiplication('scaleX(2)', 'scaleY(1)');
assertNoPostMultiplication('scaleX(2)', 'scaleZ(1)');
assertNoPostMultiplication('scaleX(2)', 'scale(1, 2)');
assertNoPostMultiplication('scaleX(2)', 'scale3d(1, 2, 3)');
assertNoPostMultiplication('skewY(10deg)', 'skewY(15deg)');
assertNoPostMultiplication('skew(10deg, 0)', 'skew(10deg, 0)');
assertNoPostMultiplication('rotate(10deg)', 'rotate(90deg)');
assertNoPostMultiplication('rotateY(10deg)', 'rotateY(90deg)');
assertNoPostMultiplication('rotateX(90deg)', 'rotateY(90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotate3d(1, 2, 1, 90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotateZ(90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotate3d(0, 0, 1, 90deg)');
assertNoPostMultiplication('rotate(10deg)', 'rotate3d(0, 0, 2, 90deg)');
assertNoPostMultiplication('rotateX(10deg)', 'rotate3d(1, 0, 0, 100deg)');
assertNoPostMultiplication('perspective(10px)', 'perspective(100px)');
assertNoPostMultiplication('matrix(1, 0, 0, 1, 0, 0)', 'matrix(2, 0, 0, 2, 0, 0)');
</script>