+2012-04-27 Dean Jackson <dino@apple.com>
+
+ Support reverse and alternate-reverse in CA animations
+ https://bugs.webkit.org/show_bug.cgi?id=78041
+
+ Reviewed by Beth Dakin.
+
+ Tests support for reverse and alternate-reverse animations on
+ CoreAnimation objects, as well as filling out some of the software
+ animator tests. There are three variables to exercise: reverse vs
+ forward direction animations, whether reversed timing functions are
+ inverted correctly, and that fill mode respects the direction of
+ animation.
+
+ Refactored the animation test helper class so we could reuse
+ property parsing and evaluation.
+
+ * animations/animation-direction-reverse-fill-mode-expected.txt: Added.
+ * animations/animation-direction-reverse-fill-mode-hardware-expected.txt: Added.
+ * animations/animation-direction-reverse-fill-mode-hardware.html: Added.
+ * animations/animation-direction-reverse-fill-mode.html: Added.
+ * animations/animation-direction-reverse-hardware-expected.txt: Added.
+ * animations/animation-direction-reverse-hardware-opacity-expected.txt: Added.
+ * animations/animation-direction-reverse-hardware-opacity.html: Added.
+ * animations/animation-direction-reverse-hardware.html: Added.
+ * animations/animation-direction-reverse-non-hardware-expected.txt: Added.
+ * animations/animation-direction-reverse-non-hardware.html: Added.
+ * animations/animation-direction-reverse-timing-functions-expected.txt: Added.
+ * animations/animation-direction-reverse-timing-functions-hardware-expected.txt: Added.
+ * animations/animation-direction-reverse-timing-functions-hardware.html: Added.
+ * animations/animation-direction-reverse-timing-functions.html: Added.
+ * animations/resources/animation-test-helpers.js:
+ (checkExpectedValue):
+ (getPropertyValue):
+ (comparePropertyValue):
+
2012-04-27 Sheriff Bot <webkit.review.bot@gmail.com>
Unreviewed, rolling out r115407.
--- /dev/null
+This test performs an animation of the left property with four different fill modes. It animates over 0.1 second with a 0.1 second delay. It takes snapshots at document load and the end of the animation.
+None
+Backwards
+Forwards
+Both
+Both iterating
+PASS - start of animation - id: a expected: 100 actual: 100
+PASS - start of animation - id: b expected: 300 actual: 300
+PASS - start of animation - id: c expected: 100 actual: 100
+PASS - start of animation - id: d expected: 300 actual: 300
+PASS - start of animation - id: e expected: 300 actual: 300
+PASS - end of animation - id: a expected: 100 actual: 100
+PASS - end of animation - id: b expected: 100 actual: 100
+PASS - end of animation - id: c expected: 300 actual: 300
+PASS - end of animation - id: d expected: 300 actual: 300
+PASS - end of animation - id: e expected: 300 actual: 300
+
--- /dev/null
+This test performs an animation of the transform property with different fill modes. It animates over 0.1 second with a 0.1 second delay. It takes snapshots at document load and the end of the animations.
+Both Iterate - Reverse
+Both Iterate - Alternate Reverse
+PASS - start of animation - id: a expected: 300 actual: matrix(1, 0, 0, 1, 300, 0)
+PASS - start of animation - id: b expected: 300 actual: matrix(1, 0, 0, 1, 300, 0)
+PASS - end of animation - id: a expected: 200 actual: matrix(1, 0, 0, 1, 200, 0)
+PASS - end of animation - id: b expected: 300 actual: matrix(1, 0, 0, 1, 300, 0)
+
--- /dev/null
+<!doctype html>
+<html>
+<head>
+ <title>Test simple fill mode on transform</title>
+ <style>
+ .box {
+ position: relative;
+ left: 10px;
+ top: 10px;
+ height: 100px;
+ width: 100px;
+ -webkit-transform: translate3d(100px, 0, 0);
+ -webkit-animation-delay: 0.1s;
+ -webkit-animation-duration: 0.1s;
+ -webkit-animation-timing-function: linear;
+ -webkit-animation-name: anim;
+ }
+ @-webkit-keyframes anim {
+ from { -webkit-transform: translate3d(200px, 0, 0); }
+ to { -webkit-transform: translate3d(300px, 0, 0); }
+ }
+ #a {
+ background-color: #f99;
+ -webkit-animation-fill-mode: both;
+ -webkit-animation-iteration-count: 2;
+ -webkit-animation-direction: reverse;
+ }
+ #b {
+ background-color: #999;
+ -webkit-animation-fill-mode: both;
+ -webkit-animation-iteration-count: 2;
+ -webkit-animation-direction: alternate-reverse;
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script>
+ const numAnims = 1;
+ var animsFinished = 0;
+ const allowance = 5;
+ const expectedValues = [
+ {id: "a", start: 300, end: 200},
+ {id: "b", start: 300, end: 300}
+ ];
+ var result = "";
+
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ function animationEnded(event) {
+ if (++animsFinished == numAnims) {
+ setTimeout(endTest, 0); // This call to setTimeout should be ok in the test environment
+ // since we're just giving style a chance to resolve.
+ }
+ };
+
+ function endTest() {
+
+ for (var i = 0; i < expectedValues.length; i++) {
+ var realValue = getPropertyValue("webkitTransform.4", expectedValues[i].id);
+ var expectedValue = expectedValues[i].end;
+ if (comparePropertyValue("webkitTransform.4", realValue, expectedValue, allowance))
+ result += "PASS";
+ else
+ result += "FAIL";
+ result += " - end of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+ }
+ document.getElementById('result').innerHTML = result;
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+ }
+
+ window.onload = function () {
+ for (var i = 0; i < expectedValues.length; i++) {
+ var realValue = getPropertyValue("webkitTransform.4", expectedValues[i].id);
+ var expectedValue = expectedValues[i].start;
+ if (comparePropertyValue("webkitTransform.4", realValue, expectedValue, allowance))
+ result += "PASS";
+ else
+ result += "FAIL";
+ result += " - start of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+ }
+ document.addEventListener("webkitAnimationEnd", animationEnded, false);
+ };
+
+ </script>
+</head>
+<body>
+This test performs an animation of the transform property with different
+fill modes. It animates over 0.1 second with a 0.1 second delay.
+It takes snapshots at document load and the end of the animations.
+<div id="a" class="box">
+ Both Iterate - Reverse
+</div>
+<div id="b" class="box">
+ Both Iterate - Alternate Reverse
+</div>
+<div id="result">
+</div>
+</body>
+</html>
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+ <title>Test animation direction reverse with fill modes</title>
+ <style>
+ .box {
+ position: relative;
+ left: 100px;
+ top: 10px;
+ height: 100px;
+ width: 100px;
+ -webkit-animation-delay: 0.1s;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-timing-function: linear;
+ -webkit-animation-name: anim;
+ -webkit-animation-direction: reverse;
+ }
+ @-webkit-keyframes anim {
+ from { left: 200px; }
+ to { left: 300px; }
+ }
+ #a {
+ background-color: blue;
+ -webkit-animation-fill-mode: none;
+ }
+ #b {
+ background-color: red;
+ -webkit-animation-fill-mode: backwards;
+ }
+ #c {
+ background-color: green;
+ -webkit-animation-fill-mode: forwards;
+ }
+ #d {
+ background-color: yellow;
+ -webkit-animation-fill-mode: both;
+ }
+ #e {
+ background-color: #999;
+ -webkit-animation-fill-mode: both;
+ -webkit-animation-iteration-count: 2;
+ -webkit-animation-direction: alternate-reverse;
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script type="text/javascript" charset="utf-8">
+ const numAnims = 5;
+ var animsFinished = 0;
+ const allowance = 5;
+ const expectedValues = [
+ {id: "a", start: 100, end: 100},
+ {id: "b", start: 300, end: 100},
+ {id: "c", start: 100, end: 300},
+ {id: "d", start: 300, end: 300},
+ {id: "e", start: 300, end: 300}
+ ];
+ var result = "";
+
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+ }
+
+ function animationEnded(event) {
+ if (++animsFinished == numAnims) {
+ setTimeout(endTest, 0); // This call to setTimeout should be ok in the test environment
+ // since we're just giving style a chance to resolve.
+ }
+ };
+
+ function endTest() {
+
+ for (var i = 0; i < expectedValues.length; i++) {
+ var realValue = getPropertyValue("left", expectedValues[i].id);
+ var expectedValue = expectedValues[i].end;
+ if (comparePropertyValue("left", realValue, expectedValue, allowance))
+ result += "PASS";
+ else
+ result += "FAIL";
+ result += " - end of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+ }
+ document.getElementById('result').innerHTML = result;
+
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+ }
+
+ window.onload = function () {
+ for (var i = 0; i < expectedValues.length; i++) {
+ var realValue = getPropertyValue("left", expectedValues[i].id);
+ var expectedValue = expectedValues[i].start;
+ if (comparePropertyValue("left", realValue, expectedValue, allowance))
+ result += "PASS";
+ else
+ result += "FAIL";
+ result += " - start of animation - id: " + expectedValues[i].id + " expected: " + expectedValue + " actual: " + realValue + "<br>";
+ }
+ document.addEventListener("webkitAnimationEnd", animationEnded, false);
+ };
+
+ </script>
+</head>
+<body>
+This test performs an animation of the left property with four different
+fill modes. It animates over 0.1 second with a 0.1 second delay.
+It takes snapshots at document load and the end of the animation.
+<div id="a" class="box">
+ None
+</div>
+<div id="b" class="box">
+ Backwards
+</div>
+<div id="c" class="box">
+ Forwards
+</div>
+<div id="d" class="box">
+ Both
+</div>
+<div id="e" class="box">
+ Both iterating
+</div>
+<div id="result">
+</div>
+</body>
+</html>
--- /dev/null
+2 keyframes: normal
+2 keyframes: alternate
+2 keyframes: reverse
+2 keyframes: alternate-reverse
+4 keyframes: normal
+4 keyframes: alternate
+4 keyframes: reverse
+4 keyframes: alternate-reverse
+PASS - "webkitTransform" property for "box1" element at 0.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box2" element at 0.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box3" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box4" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box1" element at 2.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box2" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box3" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box4" element at 2.2s saw something close to: 1,0,0,1,20,0
+PASS - "webkitTransform" property for "box5" element at 0.2s saw something close to: 1,0,0,1,40,0
+PASS - "webkitTransform" property for "box6" element at 0.2s saw something close to: 1,0,0,1,40,0
+PASS - "webkitTransform" property for "box7" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box8" element at 0.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box5" element at 2.2s saw something close to: 1,0,0,1,40,0
+PASS - "webkitTransform" property for "box6" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box7" element at 2.2s saw something close to: 1,0,0,1,180,0
+PASS - "webkitTransform" property for "box8" element at 2.2s saw something close to: 1,0,0,1,40,0
+
--- /dev/null
+2 keyframes: normal
+2 keyframes: alternate
+2 keyframes: reverse
+2 keyframes: alternate-reverse
+4 keyframes: normal
+4 keyframes: alternate
+4 keyframes: reverse
+4 keyframes: alternate-reverse
+PASS - "opacity" property for "box1" element at 0.2s saw something close to: 0.2
+PASS - "opacity" property for "box2" element at 0.2s saw something close to: 0.2
+PASS - "opacity" property for "box3" element at 0.2s saw something close to: 0.8
+PASS - "opacity" property for "box4" element at 0.2s saw something close to: 0.8
+PASS - "opacity" property for "box1" element at 1.2s saw something close to: 0.2
+PASS - "opacity" property for "box2" element at 1.2s saw something close to: 0.8
+PASS - "opacity" property for "box3" element at 1.2s saw something close to: 0.8
+PASS - "opacity" property for "box4" element at 1.2s saw something close to: 0.2
+PASS - "opacity" property for "box5" element at 0.2s saw something close to: 0.4
+PASS - "opacity" property for "box6" element at 0.2s saw something close to: 0.4
+PASS - "opacity" property for "box7" element at 0.2s saw something close to: 0.7
+PASS - "opacity" property for "box8" element at 0.2s saw something close to: 0.7
+PASS - "opacity" property for "box5" element at 1.2s saw something close to: 0.4
+PASS - "opacity" property for "box6" element at 1.2s saw something close to: 0.7
+PASS - "opacity" property for "box7" element at 1.2s saw something close to: 0.7
+PASS - "opacity" property for "box8" element at 1.2s saw something close to: 0.4
+
--- /dev/null
+<!doctype html>
+<html>
+<head>
+ <title>Test of -webkit-animation-direction on composited elements (opacity)</title>
+ <style>
+ body {
+ margin: 0;
+ }
+
+ .box {
+ position: relative;
+ left: 20px;
+ top: 10px;
+ height: 50px;
+ width: 250px;
+ margin-bottom: 10px;
+ -webkit-animation-duration: 1s;
+ -webkit-animation-timing-function: linear;
+ -webkit-animation-iteration-count: 8;
+ }
+
+ .fade1 {
+ -webkit-animation-name: fade1;
+ background-color: blue;
+ color: white;
+ }
+
+ .fade2 {
+ -webkit-animation-name: fade2;
+ background-color: orange;
+ }
+
+ .normal {
+ -webkit-animation-direction: normal;
+ }
+
+ .alternate {
+ -webkit-animation-direction: alternate;
+ }
+
+ .reverse {
+ -webkit-animation-direction: reverse;
+ }
+
+ .alternate-reverse {
+ -webkit-animation-direction: alternate-reverse;
+ }
+
+ @-webkit-keyframes fade1 {
+ from { opacity: 0; }
+ to { opacity: 1.0; }
+ }
+
+ @-webkit-keyframes fade2 {
+ 0% { opacity: 0.0; }
+ 40% { opacity: 0.8; }
+ 60% { opacity: 0.4; }
+ 100% { opacity: 1.0; }
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script>
+ const expectedValues = [
+ // [animation-name, time, element-id, property, expected-value, tolerance]
+ ["fade1", 0.2, "box1", "opacity", 0.2, 0.15],
+ ["fade1", 0.2, "box2", "opacity", 0.2, 0.15],
+ ["fade1", 0.2, "box3", "opacity", 0.8, 0.15],
+ ["fade1", 0.2, "box4", "opacity", 0.8, 0.15],
+ ["fade1", 1.2, "box1", "opacity", 0.2, 0.15],
+ ["fade1", 1.2, "box2", "opacity", 0.8, 0.15],
+ ["fade1", 1.2, "box3", "opacity", 0.8, 0.15],
+ ["fade1", 1.2, "box4", "opacity", 0.2, 0.15],
+ ["fade2", 0.2, "box5", "opacity", 0.4, 0.15],
+ ["fade2", 0.2, "box6", "opacity", 0.4, 0.15],
+ ["fade2", 0.2, "box7", "opacity", 0.7, 0.15],
+ ["fade2", 0.2, "box8", "opacity", 0.7, 0.15],
+ ["fade2", 1.2, "box5", "opacity", 0.4, 0.15],
+ ["fade2", 1.2, "box6", "opacity", 0.7, 0.15],
+ ["fade2", 1.2, "box7", "opacity", 0.7, 0.15],
+ ["fade2", 1.2, "box8", "opacity", 0.4, 0.15],
+ ];
+
+ runAnimationTest(expectedValues);
+
+ </script>
+</head>
+<body>
+<div id="box1" class="box fade1 normal">2 keyframes: normal</div>
+<div id="box2" class="box fade1 alternate">2 keyframes: alternate</div>
+<div id="box3" class="box fade1 reverse">2 keyframes: reverse</div>
+<div id="box4" class="box fade1 alternate-reverse">2 keyframes: alternate-reverse</div>
+<div id="box5" class="box fade2 normal">4 keyframes: normal</div>
+<div id="box6" class="box fade2 alternate">4 keyframes: alternate</div>
+<div id="box7" class="box fade2 reverse">4 keyframes: reverse</div>
+<div id="box8" class="box fade2 alternate-reverse">4 keyframes: alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
--- /dev/null
+<!doctype html>
+<html>
+<head>
+ <title>Test of -webkit-animation-direction on composited elements</title>
+ <style>
+ body {
+ margin: 0;
+ }
+
+ .box {
+ position: relative;
+ left: 20px;
+ top: 10px;
+ height: 50px;
+ width: 250px;
+ margin-bottom: 10px;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-timing-function: linear;
+ -webkit-animation-iteration-count: 8;
+ }
+
+ .move1 {
+ -webkit-animation-name: move1;
+ background-color: blue;
+ color: white;
+ }
+
+ .move2 {
+ -webkit-animation-name: move2;
+ background-color: orange;
+ }
+
+ .normal {
+ -webkit-animation-direction: normal;
+ }
+
+ .alternate {
+ -webkit-animation-direction: alternate;
+ }
+
+ .reverse {
+ -webkit-animation-direction: reverse;
+ }
+
+ .alternate-reverse {
+ -webkit-animation-direction: alternate-reverse;
+ }
+
+ @-webkit-keyframes move1 {
+ from { -webkit-transform: translateX(0px); }
+ to { -webkit-transform: translateX(200px); }
+ }
+
+ @-webkit-keyframes move2 {
+ 0% { -webkit-transform: translateX(0px); }
+ 40% { -webkit-transform: translateX(160px); }
+ 60% { -webkit-transform: translateX(120px); }
+ 100% { -webkit-transform: translateX(200px); }
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script>
+ const expectedValues = [
+ // [animation-name, time, element-id, property, expected-value, tolerance]
+ ["move1", 0.2, "box1", "webkitTransform", [1,0,0,1, 20,0], 20],
+ ["move1", 0.2, "box2", "webkitTransform", [1,0,0,1, 20,0], 20],
+ ["move1", 0.2, "box3", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move1", 0.2, "box4", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move1", 2.2, "box1", "webkitTransform", [1,0,0,1, 20,0], 20],
+ ["move1", 2.2, "box2", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move1", 2.2, "box3", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move1", 2.2, "box4", "webkitTransform", [1,0,0,1, 20,0], 20],
+ ["move2", 0.2, "box5", "webkitTransform", [1,0,0,1, 40,0], 20],
+ ["move2", 0.2, "box6", "webkitTransform", [1,0,0,1, 40,0], 20],
+ ["move2", 0.2, "box7", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move2", 0.2, "box8", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move2", 2.2, "box5", "webkitTransform", [1,0,0,1, 40,0], 20],
+ ["move2", 2.2, "box6", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move2", 2.2, "box7", "webkitTransform", [1,0,0,1, 180,0], 20],
+ ["move2", 2.2, "box8", "webkitTransform", [1,0,0,1, 40,0], 20],
+ ];
+
+ runAnimationTest(expectedValues);
+
+ </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">2 keyframes: normal</div>
+<div id="box2" class="box move1 alternate">2 keyframes: alternate</div>
+<div id="box3" class="box move1 reverse">2 keyframes: reverse</div>
+<div id="box4" class="box move1 alternate-reverse">2 keyframes: alternate-reverse</div>
+<div id="box5" class="box move2 normal">4 keyframes: normal</div>
+<div id="box6" class="box move2 alternate">4 keyframes: alternate</div>
+<div id="box7" class="box move2 reverse">4 keyframes: reverse</div>
+<div id="box8" class="box move2 alternate-reverse">4 keyframes: alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
--- /dev/null
+2 keyframes: normal
+2 keyframes: alternate
+2 keyframes: reverse
+2 keyframes: alternate-reverse
+4 keyframes: normal
+4 keyframes: alternate
+4 keyframes: reverse
+4 keyframes: alternate-reverse
+PASS - "left" property for "box1" element at 0.2s saw something close to: 20
+PASS - "left" property for "box2" element at 0.2s saw something close to: 20
+PASS - "left" property for "box3" element at 0.2s saw something close to: 180
+PASS - "left" property for "box4" element at 0.2s saw something close to: 180
+PASS - "left" property for "box1" element at 2.2s saw something close to: 20
+PASS - "left" property for "box2" element at 2.2s saw something close to: 180
+PASS - "left" property for "box3" element at 2.2s saw something close to: 180
+PASS - "left" property for "box4" element at 2.2s saw something close to: 20
+PASS - "left" property for "box5" element at 0.2s saw something close to: 40
+PASS - "left" property for "box6" element at 0.2s saw something close to: 40
+PASS - "left" property for "box7" element at 0.2s saw something close to: 180
+PASS - "left" property for "box8" element at 0.2s saw something close to: 180
+PASS - "left" property for "box5" element at 2.2s saw something close to: 40
+PASS - "left" property for "box6" element at 2.2s saw something close to: 180
+PASS - "left" property for "box7" element at 2.2s saw something close to: 180
+PASS - "left" property for "box8" element at 2.2s saw something close to: 40
+
--- /dev/null
+<!doctype html>
+<html>
+<head>
+ <title>Test of -webkit-animation-direction reverse on non-composited elements</title>
+ <style>
+ body {
+ margin: 0;
+ }
+
+ .box {
+ position: relative;
+ left: 20px;
+ top: 10px;
+ height: 50px;
+ width: 250px;
+ margin-bottom: 10px;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-timing-function: linear;
+ -webkit-animation-iteration-count: 8;
+ }
+
+ .move1 {
+ -webkit-animation-name: move1;
+ background-color: blue;
+ color: white;
+ }
+
+ .move2 {
+ -webkit-animation-name: move2;
+ background-color: orange;
+ }
+
+ .normal {
+ -webkit-animation-direction: normal;
+ }
+
+ .alternate {
+ -webkit-animation-direction: alternate;
+ }
+
+ .reverse {
+ -webkit-animation-direction: reverse;
+ }
+
+ .alternate-reverse {
+ -webkit-animation-direction: alternate-reverse;
+ }
+
+ @-webkit-keyframes move1 {
+ from { left: 0px; }
+ to { left: 200px; }
+ }
+
+ @-webkit-keyframes move2 {
+ 0% { left: 0px; }
+ 40% { left: 160px; }
+ 60% { left: 120px; }
+ 100% { left: 200px; }
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script>
+ const expectedValues = [
+ // [animation-name, time, element-id, property, expected-value, tolerance]
+ ["move1", 0.2, "box1", "left", 20, 20],
+ ["move1", 0.2, "box2", "left", 20, 20],
+ ["move1", 0.2, "box3", "left", 180, 20],
+ ["move1", 0.2, "box4", "left", 180, 20],
+ ["move1", 2.2, "box1", "left", 20, 20],
+ ["move1", 2.2, "box2", "left", 180, 20],
+ ["move1", 2.2, "box3", "left", 180, 20],
+ ["move1", 2.2, "box4", "left", 20, 20],
+ ["move2", 0.2, "box5", "left", 40, 20],
+ ["move2", 0.2, "box6", "left", 40, 20],
+ ["move2", 0.2, "box7", "left", 180, 20],
+ ["move2", 0.2, "box8", "left", 180, 20],
+ ["move2", 2.2, "box5", "left", 40, 20],
+ ["move2", 2.2, "box6", "left", 180, 20],
+ ["move2", 2.2, "box7", "left", 180, 20],
+ ["move2", 2.2, "box8", "left", 40, 20],
+ ];
+
+ runAnimationTest(expectedValues);
+
+ </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">2 keyframes: normal</div>
+<div id="box2" class="box move1 alternate">2 keyframes: alternate</div>
+<div id="box3" class="box move1 reverse">2 keyframes: reverse</div>
+<div id="box4" class="box move1 alternate-reverse">2 keyframes: alternate-reverse</div>
+<div id="box5" class="box move2 normal">4 keyframes: normal</div>
+<div id="box6" class="box move2 alternate">4 keyframes: alternate</div>
+<div id="box7" class="box move2 reverse">4 keyframes: reverse</div>
+<div id="box8" class="box move2 alternate-reverse">4 keyframes: alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
--- /dev/null
+normal
+alternate
+reverse
+alternate-reverse
+PASS - "left" property for "box1" element at 0.2s saw something close to: 18
+PASS - "left" property for "box2" element at 0.2s saw something close to: 18
+PASS - "left" property for "box3" element at 0.2s saw something close to: 198
+PASS - "left" property for "box4" element at 0.2s saw something close to: 198
+PASS - "left" property for "box1" element at 2.2s saw something close to: 18
+PASS - "left" property for "box2" element at 2.2s saw something close to: 198
+PASS - "left" property for "box3" element at 2.2s saw something close to: 198
+PASS - "left" property for "box4" element at 2.2s saw something close to: 18
+
--- /dev/null
+normal
+alternate
+reverse
+alternate-reverse
+PASS - "webkitTransform" property for "box1" element at 0.2s saw something close to: 1,0,0,1,18,0
+PASS - "webkitTransform" property for "box2" element at 0.2s saw something close to: 1,0,0,1,18,0
+PASS - "webkitTransform" property for "box3" element at 0.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box4" element at 0.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box1" element at 2.2s saw something close to: 1,0,0,1,18,0
+PASS - "webkitTransform" property for "box2" element at 2.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box3" element at 2.2s saw something close to: 1,0,0,1,198,0
+PASS - "webkitTransform" property for "box4" element at 2.2s saw something close to: 1,0,0,1,18,0
+
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+ <title>Test of -webkit-animation-direction timing functions on composited elements</title>
+ <style>
+ body {
+ margin: 0;
+ }
+
+ .box {
+ position: relative;
+ left: 20px;
+ top: 10px;
+ height: 50px;
+ width: 250px;
+ margin-bottom: 10px;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-timing-function: ease; /* ease is good for testing because it is not symmetric */
+ -webkit-animation-iteration-count: 4;
+ }
+
+ .move1 {
+ -webkit-animation-name: move1;
+ background-color: blue;
+ color: white;
+ }
+
+ .normal {
+ -webkit-animation-direction: normal;
+ }
+
+ .alternate {
+ -webkit-animation-direction: alternate;
+ }
+
+ .reverse {
+ -webkit-animation-direction: reverse;
+ }
+
+ .alternate-reverse {
+ -webkit-animation-direction: alternate-reverse;
+ }
+
+ @-webkit-keyframes move1 {
+ from { -webkit-transform: translateX(0px); }
+ to { -webkit-transform: translateX(200px); }
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script>
+ const expectedValues = [
+ // [animation-name, time, element-id, property, expected-value, tolerance]
+ ["move1", 0.2, "box1", "webkitTransform", [1,0,0,1, 18, 0], 10],
+ ["move1", 0.2, "box2", "webkitTransform", [1,0,0,1, 18, 0], 10],
+ ["move1", 0.2, "box3", "webkitTransform", [1,0,0,1, 198, 0], 10],
+ ["move1", 0.2, "box4", "webkitTransform", [1,0,0,1, 198, 0], 10],
+ ["move1", 2.2, "box1", "webkitTransform", [1,0,0,1, 18, 0], 10],
+ ["move1", 2.2, "box2", "webkitTransform", [1,0,0,1, 198, 0], 10],
+ ["move1", 2.2, "box3", "webkitTransform", [1,0,0,1, 198, 0], 10],
+ ["move1", 2.2, "box4", "webkitTransform", [1,0,0,1, 18, 0], 10],
+ ];
+
+ runAnimationTest(expectedValues);
+
+ </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">normal</div>
+<div id="box2" class="box move1 alternate">alternate</div>
+<div id="box3" class="box move1 reverse">reverse</div>
+<div id="box4" class="box move1 alternate-reverse">alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
--- /dev/null
+<!doctype html>
+<html lang="en">
+<head>
+ <title>Test of -webkit-animation-direction timing functions</title>
+ <style>
+ body {
+ margin: 0;
+ }
+
+ .box {
+ position: relative;
+ left: 20px;
+ top: 10px;
+ height: 50px;
+ width: 250px;
+ margin-bottom: 10px;
+ -webkit-animation-duration: 2s;
+ -webkit-animation-timing-function: ease; /* ease is good for testing because it is not symmetric */
+ -webkit-animation-iteration-count: 4;
+ }
+
+ .move1 {
+ -webkit-animation-name: move1;
+ background-color: blue;
+ color: white;
+ }
+
+ .normal {
+ -webkit-animation-direction: normal;
+ }
+
+ .alternate {
+ -webkit-animation-direction: alternate;
+ }
+
+ .reverse {
+ -webkit-animation-direction: reverse;
+ }
+
+ .alternate-reverse {
+ -webkit-animation-direction: alternate-reverse;
+ }
+
+ @-webkit-keyframes move1 {
+ from { left: 0px; }
+ to { left: 200px; }
+ }
+ </style>
+ <script src="resources/animation-test-helpers.js"></script>
+ <script>
+ const expectedValues = [
+ // [animation-name, time, element-id, property, expected-value, tolerance]
+ ["move1", 0.2, "box1", "left", 18, 10],
+ ["move1", 0.2, "box2", "left", 18, 10],
+ ["move1", 0.2, "box3", "left", 198, 10],
+ ["move1", 0.2, "box4", "left", 198, 10],
+ ["move1", 2.2, "box1", "left", 18, 10],
+ ["move1", 2.2, "box2", "left", 198, 10],
+ ["move1", 2.2, "box3", "left", 198, 10],
+ ["move1", 2.2, "box4", "left", 18, 10],
+ ];
+
+ runAnimationTest(expectedValues);
+
+ </script>
+</head>
+<body>
+<div id="box1" class="box move1 normal">normal</div>
+<div id="box2" class="box move1 alternate">alternate</div>
+<div id="box3" class="box move1 reverse">reverse</div>
+<div id="box4" class="box move1 alternate-reverse">alternate-reverse</div>
+<div id="result"></div>
+</div>
+</body>
+</html>
}
var computedValue, computedValue2;
- var pass = true;
- if (!property.indexOf("webkitTransform")) {
- var element;
- if (iframeId)
- element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+ if (compareElements) {
+ computedValue = getPropertyValue(property, elementId, iframeId);
+ computedValue2 = getPropertyValue(property, elementId2, iframeId);
+
+ if (comparePropertyValue(property, computedValue, computedValue2, tolerance))
+ result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 +
+ "\" elements at " + time + "s are close enough to each other" + "<br>";
else
- element = document.getElementById(elementId);
-
- computedValue = window.getComputedStyle(element).webkitTransform;
- if (compareElements) {
- computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitTransform;
- var m1 = matrixStringToArray(computedValue);
- var m2 = matrixStringToArray(computedValue2);
-
- // for now we assume that both matrices are either both 2D or both 3D
- var count = (computedValue.substring(0, 7) == "matrix3d") ? 16 : 6;
- for (var i = 0; i < count; ++i) {
- pass = isCloseEnough(parseFloat(m1[i]), m2[i], tolerance);
- if (!pass)
- break;
- }
- } else {
- if (typeof expectedValue == "string")
- pass = (computedValue == expectedValue);
- else if (typeof expectedValue == "number") {
- var m = matrixStringToArray(computedValue);
- pass = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
- } else {
- var m = matrixStringToArray(computedValue);
- for (i = 0; i < expectedValue.length; ++i) {
- pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
- if (!pass)
- break;
- }
- }
- }
- } else if (property == "webkitFilter") {
- var element;
+ result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 +
+ "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 +
+ "\" which are not close enough to each other" + "<br>";
+ } else {
+ var elementName;
if (iframeId)
- element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+ elementName = iframeId + '.' + elementId;
else
- element = document.getElementById(elementId);
-
- computedValue = window.getComputedStyle(element).webkitFilter;
- var filterParameters = getFilterParameters(computedValue);
+ elementName = elementId;
- if (compareElements) {
- computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitFilter;
- var filter2Parameters = getFilterParameters(computedValue2);
- pass = filterParametersMatch(filterParameters, filter2Parameters, tolerance);
- } else {
- pass = filterParametersMatch(filterParameters, getFilterParameters(expectedValue), tolerance);
- }
- } else if (property == "lineHeight") {
- var element;
- if (iframeId)
- element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+ computedValue = getPropertyValue(property, elementId, iframeId);
+
+ if (comparePropertyValue(property, computedValue, expectedValue, tolerance))
+ result += "PASS - \"" + property + "\" property for \"" + elementName + "\" element at " + time +
+ "s saw something close to: " + expectedValue + "<br>";
else
- element = document.getElementById(elementId);
-
+ result += "FAIL - \"" + property + "\" property for \"" + elementName + "\" element at " + time +
+ "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
+ }
+}
+
+
+function getPropertyValue(property, elementId, iframeId)
+{
+ var computedValue;
+ var element;
+ if (iframeId)
+ element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
+ else
+ element = document.getElementById(elementId);
+
+ if (property == "lineHeight")
computedValue = parseInt(window.getComputedStyle(element).lineHeight);
- if (compareElements) {
- computedValue2 = parseInt(window.getComputedStyle(document.getElementById(elementId2)).lineHeight);
- pass = isCloseEnough(computedValue, computedValue2, tolerance);
+ else if (property == "backgroundImage"
+ || property == "borderImageSource"
+ || property == "listStyleImage"
+ || property == "webkitMaskImage"
+ || property == "webkitMaskBoxImage"
+ || property == "webkitFilter"
+ || !property.indexOf("webkitTransform")) {
+ computedValue = window.getComputedStyle(element)[property.split(".")[0]];
+ } else {
+ var computedStyle = window.getComputedStyle(element).getPropertyCSSValue(property);
+ computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+ }
+
+ return computedValue;
+}
+
+function comparePropertyValue(property, computedValue, expectedValue, tolerance)
+{
+ var result = true;
+
+ if (!property.indexOf("webkitTransform")) {
+ if (typeof expectedValue == "string")
+ result = (computedValue == expectedValue);
+ else if (typeof expectedValue == "number") {
+ var m = matrixStringToArray(computedValue);
+ result = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
+ } else {
+ var m = matrixStringToArray(computedValue);
+ for (i = 0; i < expectedValue.length; ++i) {
+ result = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
+ if (!result)
+ break;
+ }
}
- else
- pass = isCloseEnough(computedValue, expectedValue, tolerance);
+ } else if (property == "webkitFilter") {
+ var filterParameters = getFilterParameters(computedValue);
+ var filter2Parameters = getFilterParameters(expectedValue);
+ result = filterParametersMatch(filterParameters, filter2Parameters, tolerance);
} else if (property == "backgroundImage"
|| property == "borderImageSource"
|| property == "listStyleImage"
|| property == "webkitMaskImage"
|| property == "webkitMaskBoxImage") {
- var element;
- if (iframeId)
- element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
- else
- element = document.getElementById(elementId);
-
- computedValue = window.getComputedStyle(element)[property];
- computedCrossFade = parseCrossFade(computedValue);
+ var computedCrossFade = parseCrossFade(computedValue);
if (!computedCrossFade) {
- pass = false;
+ result = false;
} else {
- if (compareElements) {
- computedValue2 = window.getComputedStyle(document.getElementById(elementId2))[property];
- computedCrossFade2 = parseCrossFade(computedValue2);
- if (computedCrossFade2) {
- pass = (isCloseEnough(computedCrossFade.percent, computedCrossFade2.percent, tolerance) &&
- computedCrossFade.from == computedCrossFade2.from &&
- computedCrossFade.to == computedCrossFade2.to);
- }
- else {
- pass = false;
- }
+ if (typeof expectedValue == "string") {
+ var computedCrossFade2 = parseCrossFade(expectedValue);
+ result = isCloseEnough(computedCrossFade.percent, computedCrossFade2.percent, tolerance) && computedCrossFade.from == computedCrossFade2.from && computedCrossFade.to == computedCrossFade2.to;
} else {
- pass = isCloseEnough(computedCrossFade.percent, expectedValue, tolerance);
+ result = isCloseEnough(computedCrossFade.percent, expectedValue, tolerance)
}
}
} else {
- var element;
- if (iframeId)
- element = document.getElementById(iframeId).contentDocument.getElementById(elementId);
- else
- element = document.getElementById(elementId);
-
- var computedStyle = window.getComputedStyle(element).getPropertyCSSValue(property);
- computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
- if (compareElements) {
- var computedStyle2 = window.getComputedStyle(document.getElementById(elementId2)).getPropertyCSSValue(property);
- computedValue2 = computedStyle2.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
- pass = isCloseEnough(computedValue, computedValue2, tolerance);
- }
- else
- pass = isCloseEnough(computedValue, expectedValue, tolerance);
- }
-
- if (compareElements) {
- if (pass)
- result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 +
- "\" elements at " + time + "s are close enough to each other" + "<br>";
- else
- result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 +
- "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 +
- "\" which are not close enough to each other" + "<br>";
- } else {
- var elementName;
- if (iframeId)
- elementName = iframeId + '.' + elementId;
- else
- elementName = elementId;
- if (pass)
- result += "PASS - \"" + property + "\" property for \"" + elementName + "\" element at " + time +
- "s saw something close to: " + expectedValue + "<br>";
- else
- result += "FAIL - \"" + property + "\" property for \"" + elementName + "\" element at " + time +
- "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
+ result = isCloseEnough(computedValue, expectedValue, tolerance);
}
+ return result;
}
function endTest()
+2012-04-27 Dean Jackson <dino@apple.com>
+
+ Support reverse and alternate-reverse in CA animations
+ https://bugs.webkit.org/show_bug.cgi?id=78041
+
+ Reviewed by Beth Dakin.
+
+ CoreAnimation does not natively support reverse and alternate-reverse
+ animation directions so we need to flip the animation values (keyframe
+ keys and timing functions) that we send to GraphicsLayerCA. Unfortunately
+ this code adds a lot of conditionals because it isn't as simple as
+ reversing the order of keys. You also now have a different alignment of
+ timing functions to the reversed list.
+
+ New tests to cover the two new directions, making sure the timing
+ functions are correctly inverted, and exercising fill modes.
+
+ Tests: animations/animation-direction-reverse-fill-mode-hardware.html
+ animations/animation-direction-reverse-fill-mode.html
+ animations/animation-direction-reverse-hardware-opacity.html
+ animations/animation-direction-reverse-hardware.html
+ animations/animation-direction-reverse-non-hardware.html
+ animations/animation-direction-reverse-timing-functions-hardware.html
+ animations/animation-direction-reverse-timing-functions.html
+
+ * platform/graphics/ca/GraphicsLayerCA.cpp:
+ Handle the previously unsupported animation directions, reversing
+ the list of values and keytimes that would be used to create
+ the CA Animation.
+ (WebCore::GraphicsLayerCA::addAnimation):
+ Do not create an animation if on Windows and using a reverse
+ direction.
+ (WebCore::GraphicsLayerCA::createFilterAnimationsFromKeyframes):
+ (WebCore::GraphicsLayerCA::setupAnimation):
+ (WebCore::GraphicsLayerCA::setAnimationEndpoints):
+ (WebCore::GraphicsLayerCA::setAnimationKeyframes):
+ (WebCore::GraphicsLayerCA::setTransformAnimationEndpoints):
+ (WebCore::GraphicsLayerCA::setTransformAnimationKeyframes):
+ (WebCore::GraphicsLayerCA::setFilterAnimationEndpoints):
+ (WebCore::GraphicsLayerCA::setFilterAnimationKeyframes):
+ * platform/graphics/ca/PlatformCAAnimation.h:
+ (PlatformCAAnimation): Pass through a flag that tells the CA Animation
+ that it should invert the timing functions.
+ * platform/graphics/ca/mac/PlatformCAAnimationMac.mm:
+ (toCAMediaTimingFunction): Add a parameter that will invert the timing
+ function coefficients if necessary.
+ (PlatformCAAnimation::setTimingFunction):
+ (PlatformCAAnimation::setTimingFunctions):
+ * platform/graphics/ca/win/PlatformCAAnimationWin.cpp:
+ (toCACFTimingFunction):
+ New unused parameter.
+
2012-04-27 Sheriff Bot <webkit.review.bot@gmail.com>
Unreviewed, rolling out r115407.
AnimationDirectionAlternateReverse
};
AnimationDirection direction() const { return static_cast<AnimationDirection>(m_direction); }
+ bool directionIsForwards() const { return m_direction == AnimationDirectionNormal || m_direction == AnimationDirectionAlternate; }
unsigned fillMode() const { return m_fillMode; }
if (animationHasStepsTimingFunction(valueList, anim))
return false;
+#if PLATFORM(WIN)
+ // CoreAnimation on Windows does not handle a reverse direction. Fall
+ // back to software animation in that case.
+ // https://bugs.webkit.org/show_bug.cgi?id=85121
+ if (!anim->directionIsForwards())
+ return false;
+#endif
+
bool createdAnimations = false;
if (valueList.property() == AnimatedPropertyWebkitTransform)
createdAnimations = createTransformAnimationsFromKeyframes(valueList, anim, animationName, timeOffset, boxSize);
const FilterOperations* operations = static_cast<const FilterAnimationValue*>(valueList.at(listIndex))->value();
int numAnimations = operations->size();
- // FIXME: We can't currently hardware animate shadows. There is an open question about removing shadows from filters
- // entirely, in which case this issue is moot.
+ // FIXME: We can't currently hardware animate shadows.
for (int i = 0; i < numAnimations; ++i) {
if (operations->at(i)->getOperationType() == FilterOperation::DROP_SHADOW)
return false;
float repeatCount = anim->iterationCount();
if (repeatCount == Animation::IterationCountInfinite)
repeatCount = numeric_limits<float>::max();
- else if (anim->direction() == Animation::AnimationDirectionAlternate)
+ else if (anim->direction() == Animation::AnimationDirectionAlternate || anim->direction() == Animation::AnimationDirectionAlternateReverse)
repeatCount /= 2;
PlatformCAAnimation::FillModeType fillMode = PlatformCAAnimation::NoFillMode;
propertyAnim->setDuration(duration);
propertyAnim->setRepeatCount(repeatCount);
- propertyAnim->setAutoreverses(anim->direction());
+ propertyAnim->setAutoreverses(anim->direction() == Animation::AnimationDirectionAlternate || anim->direction() == Animation::AnimationDirectionAlternateReverse);
propertyAnim->setRemovedOnCompletion(false);
propertyAnim->setAdditive(additive);
propertyAnim->setFillMode(fillMode);
return CubicBezierTimingFunction::defaultTimingFunction();
}
-bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* basicAnim)
+bool GraphicsLayerCA::setAnimationEndpoints(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* basicAnim)
{
+ bool forwards = animation->directionIsForwards();
+
+ unsigned fromIndex = !forwards;
+ unsigned toIndex = forwards;
+
switch (valueList.property()) {
case AnimatedPropertyOpacity: {
- basicAnim->setFromValue(static_cast<const FloatAnimationValue*>(valueList.at(0))->value());
- basicAnim->setToValue(static_cast<const FloatAnimationValue*>(valueList.at(1))->value());
+ basicAnim->setFromValue(static_cast<const FloatAnimationValue*>(valueList.at(fromIndex))->value());
+ basicAnim->setToValue(static_cast<const FloatAnimationValue*>(valueList.at(toIndex))->value());
break;
}
default:
}
// This codepath is used for 2-keyframe animations, so we still need to look in the start
- // for a timing function.
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim);
+ // for a timing function. Even in the reversing animation case, the first keyframe provides the timing function.
+ const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), animation);
if (timingFunction)
- basicAnim->setTimingFunction(timingFunction);
+ basicAnim->setTimingFunction(timingFunction, !forwards);
return true;
}
-bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* keyframeAnim)
+bool GraphicsLayerCA::setAnimationKeyframes(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* keyframeAnim)
{
Vector<float> keyTimes;
Vector<float> values;
Vector<const TimingFunction*> timingFunctions;
+
+ bool forwards = animation->directionIsForwards();
for (unsigned i = 0; i < valueList.size(); ++i) {
- const AnimationValue* curValue = valueList.at(i);
- keyTimes.append(curValue->keyTime());
+ unsigned index = forwards ? i : (valueList.size() - i - 1);
+ const AnimationValue* curValue = valueList.at(index);
+ keyTimes.append(forwards ? curValue->keyTime() : (1 - curValue->keyTime()));
switch (valueList.property()) {
case AnimatedPropertyOpacity: {
break;
}
- timingFunctions.append(timingFunctionForAnimationValue(curValue, anim));
+ if (i < (valueList.size() - 1))
+ timingFunctions.append(timingFunctionForAnimationValue(forwards ? curValue : valueList.at(index - 1), animation));
}
- // We toss the last tfArray value because it has to one shorter than the others.
- timingFunctions.removeLast();
-
keyframeAnim->setKeyTimes(keyTimes);
keyframeAnim->setValues(values);
- keyframeAnim->setTimingFunctions(timingFunctions);
+ keyframeAnim->setTimingFunctions(timingFunctions, !forwards);
return true;
}
-bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* anim, PlatformCAAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize)
+bool GraphicsLayerCA::setTransformAnimationEndpoints(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* basicAnim, int functionIndex, TransformOperation::OperationType transformOpType, bool isMatrixAnimation, const IntSize& boxSize)
{
ASSERT(valueList.size() == 2);
- const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(0));
- const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(1));
+
+ bool forwards = animation->directionIsForwards();
+
+ unsigned fromIndex = !forwards;
+ unsigned toIndex = forwards;
+
+ const TransformAnimationValue* startValue = static_cast<const TransformAnimationValue*>(valueList.at(fromIndex));
+ const TransformAnimationValue* endValue = static_cast<const TransformAnimationValue*>(valueList.at(toIndex));
if (isMatrixAnimation) {
TransformationMatrix fromTransform, toTransform;
}
// This codepath is used for 2-keyframe animations, so we still need to look in the start
- // for a timing function.
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), anim);
- basicAnim->setTimingFunction(timingFunction);
+ // for a timing function. Even in the reversing animation case, the first keyframe provides the timing function.
+ const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), animation);
+ basicAnim->setTimingFunction(timingFunction, !forwards);
PlatformCAAnimation::ValueFunctionType valueFunction = getValueFunctionNameForTransformOperation(transformOpType);
if (valueFunction != PlatformCAAnimation::NoValueFunction)
Vector<TransformationMatrix> transformationMatrixValues;
Vector<const TimingFunction*> timingFunctions;
+ bool forwards = animation->directionIsForwards();
+
for (unsigned i = 0; i < valueList.size(); ++i) {
- const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(i));
- keyTimes.append(curValue->keyTime());
+ unsigned index = forwards ? i : (valueList.size() - i - 1);
+ const TransformAnimationValue* curValue = static_cast<const TransformAnimationValue*>(valueList.at(index));
+ keyTimes.append(forwards ? curValue->keyTime() : (1 - curValue->keyTime()));
if (isMatrixAnimation) {
TransformationMatrix transform;
}
}
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation);
- timingFunctions.append(timingFunction);
+ if (i < (valueList.size() - 1))
+ timingFunctions.append(timingFunctionForAnimationValue(forwards ? curValue : valueList.at(index - 1), animation));
}
- // We toss the last tfArray value because it has to one shorter than the others.
- timingFunctions.removeLast();
-
keyframeAnim->setKeyTimes(keyTimes);
if (isTransformTypeNumber(transformOpType))
else
keyframeAnim->setValues(transformationMatrixValues);
- keyframeAnim->setTimingFunctions(timingFunctions);
+ keyframeAnim->setTimingFunctions(timingFunctions, !forwards);
PlatformCAAnimation::ValueFunctionType valueFunction = getValueFunctionNameForTransformOperation(transformOpType);
if (valueFunction != PlatformCAAnimation::NoValueFunction)
bool GraphicsLayerCA::setFilterAnimationEndpoints(const KeyframeValueList& valueList, const Animation* animation, PlatformCAAnimation* basicAnim, int functionIndex, int internalFilterPropertyIndex)
{
ASSERT(valueList.size() == 2);
-
- const FilterAnimationValue* fromValue = static_cast<const FilterAnimationValue*>(valueList.at(0));
- const FilterAnimationValue* toValue = static_cast<const FilterAnimationValue*>(valueList.at(1));
-
+
+ bool forwards = animation->directionIsForwards();
+
+ unsigned fromIndex = !forwards;
+ unsigned toIndex = forwards;
+
+ const FilterAnimationValue* fromValue = static_cast<const FilterAnimationValue*>(valueList.at(fromIndex));
+ const FilterAnimationValue* toValue = static_cast<const FilterAnimationValue*>(valueList.at(toIndex));
+
const FilterOperation* fromOperation = fromValue->value()->at(functionIndex);
const FilterOperation* toOperation = toValue->value()->at(functionIndex);
-
+
RefPtr<DefaultFilterOperation> defaultFromOperation;
RefPtr<DefaultFilterOperation> defaultToOperation;
-
+
ASSERT(fromOperation || toOperation);
-
+
if (!fromOperation) {
defaultFromOperation = DefaultFilterOperation::create(toOperation->getOperationType());
fromOperation = defaultFromOperation.get();
}
-
+
if (!toOperation) {
defaultToOperation = DefaultFilterOperation::create(fromOperation->getOperationType());
toOperation = defaultToOperation.get();
}
-
+
basicAnim->setFromValue(fromOperation, internalFilterPropertyIndex);
basicAnim->setToValue(toOperation, internalFilterPropertyIndex);
-
- // This codepath is used for 2-keyframe animations, so we still need to look in the start for a timing function.
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(valueList.at(0), animation);
- basicAnim->setTimingFunction(timingFunction);
+
+ // This codepath is used for 2-keyframe animations, so we still need to look in the start
+ // for a timing function. Even in the reversing animation case, the first keyframe provides the timing function.
+ basicAnim->setTimingFunction(timingFunctionForAnimationValue(valueList.at(0), animation), !forwards);
return true;
}
Vector<const TimingFunction*> timingFunctions;
RefPtr<DefaultFilterOperation> defaultOperation;
+ bool forwards = animation->directionIsForwards();
+
for (unsigned i = 0; i < valueList.size(); ++i) {
- const FilterAnimationValue* curValue = static_cast<const FilterAnimationValue*>(valueList.at(i));
- keyTimes.append(curValue->keyTime());
+ unsigned index = forwards ? i : (valueList.size() - i - 1);
+ const FilterAnimationValue* curValue = static_cast<const FilterAnimationValue*>(valueList.at(index));
+ keyTimes.append(forwards ? curValue->keyTime() : (1 - curValue->keyTime()));
if (curValue->value()->operations().size() > static_cast<size_t>(functionIndex))
values.append(curValue->value()->operations()[functionIndex]);
values.append(defaultOperation);
}
- const TimingFunction* timingFunction = timingFunctionForAnimationValue(curValue, animation);
- timingFunctions.append(timingFunction);
+ if (i < (valueList.size() - 1))
+ timingFunctions.append(timingFunctionForAnimationValue(forwards ? curValue : valueList.at(index - 1), animation));
}
- // We toss the last timing function because it has to be one shorter than the others.
- timingFunctions.removeLast();
-
keyframeAnim->setKeyTimes(keyTimes);
keyframeAnim->setValues(values, internalFilterPropertyIndex);
- keyframeAnim->setTimingFunctions(timingFunctions);
+ keyframeAnim->setTimingFunctions(timingFunctions, !forwards);
return true;
}
FillModeType fillMode() const;
void setFillMode(FillModeType);
- void setTimingFunction(const TimingFunction*);
+ void setTimingFunction(const TimingFunction*, bool reverse = false);
void copyTimingFunctionFrom(const PlatformCAAnimation*);
bool isRemovedOnCompletion() const;
void setKeyTimes(const Vector<float>&);
void copyKeyTimesFrom(const PlatformCAAnimation*);
- void setTimingFunctions(const Vector<const TimingFunction*>&);
+ void setTimingFunctions(const Vector<const TimingFunction*>&, bool reverse = false);
void copyTimingFunctionsFrom(const PlatformCAAnimation*);
#if ENABLE(CSS_FILTERS)
}
#endif
-static CAMediaTimingFunction* toCAMediaTimingFunction(const TimingFunction* timingFunction)
+static CAMediaTimingFunction* toCAMediaTimingFunction(const TimingFunction* timingFunction, bool reverse)
{
ASSERT(timingFunction);
if (timingFunction->isCubicBezierTimingFunction()) {
const CubicBezierTimingFunction* ctf = static_cast<const CubicBezierTimingFunction*>(timingFunction);
- return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(ctf->x1()) :static_cast<float>(ctf->y1())
- :static_cast<float>(ctf->x2()) :static_cast<float>(ctf->y2())];
+ float x1 = static_cast<float>(ctf->x1());
+ float y1 = static_cast<float>(ctf->y1());
+ float x2 = static_cast<float>(ctf->x2());
+ float y2 = static_cast<float>(ctf->y2());
+ return [CAMediaTimingFunction functionWithControlPoints:(reverse ? 1 - x2 : x1)
+ :(reverse ? 1 - y2 : y1)
+ :(reverse ? 1 - x1 : x2)
+ :(reverse ? 1 - y1 : y2)];
}
return [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[m_animation.get() setFillMode:toCAFillModeType(value)];
}
-void PlatformCAAnimation::setTimingFunction(const TimingFunction* value)
+void PlatformCAAnimation::setTimingFunction(const TimingFunction* value, bool reverse)
{
- [m_animation.get() setTimingFunction:toCAMediaTimingFunction(value)];
+ [m_animation.get() setTimingFunction:toCAMediaTimingFunction(value, reverse)];
}
void PlatformCAAnimation::copyTimingFunctionFrom(const PlatformCAAnimation* value)
[static_cast<CAKeyframeAnimation*>(m_animation.get()) setKeyTimes:[other keyTimes]];
}
-void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value)
+void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
{
NSMutableArray* array = [NSMutableArray array];
for (size_t i = 0; i < value.size(); ++i)
- [array addObject:toCAMediaTimingFunction(value[i])];
+ [array addObject:toCAMediaTimingFunction(value[i], reverse)];
[static_cast<CAKeyframeAnimation*>(m_animation.get()) setTimingFunctions:array];
}
CACFAnimationSetFillMode(m_animation.get(), toCACFFillModeType(value));
}
-void PlatformCAAnimation::setTimingFunction(const TimingFunction* value)
+void PlatformCAAnimation::setTimingFunction(const TimingFunction* value, bool reverse)
{
+ UNUSED_PARAM(reverse);
CACFAnimationSetTimingFunction(m_animation.get(), toCACFTimingFunction(value).get());
}
CACFAnimationSetKeyTimes(m_animation.get(), CACFAnimationGetKeyTimes(value->platformAnimation()));
}
-void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value)
+void PlatformCAAnimation::setTimingFunctions(const Vector<const TimingFunction*>& value, bool reverse)
{
+ UNUSED_PARAM(reverse);
if (animationType() != Keyframe)
return;