Skip to content

Add per keyframe easing #563

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 17, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions test/testcases.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var tests = [
'auto-test-iterations-basic.html',
'auto-test-iterations-fill.html',
'auto-test-keyframe-creation.html',
'auto-test-keyframe-easing.html',
'auto-test-length-units.html',
'auto-test-matrix-transforms.html',
'auto-test-non-numeric.html',
Expand Down
65 changes: 65 additions & 0 deletions test/testcases/auto-test-keyframe-easing-checks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
timing_test(function() {
at(0, function() {
assert_styles("#target",{'height':'0px'});
});
at(0.25, function() {
assert_styles("#target",{'height':'12.5px'});
});
at(0.5, function() {
assert_styles("#target",{'height':'25px'});
});
at(0.75, function() {
assert_styles("#target",{'height':'37.5px'});
});
at(1, function() {
assert_styles("#target",{'height':'50px'});
});
at(1.25, function() {
assert_styles("#target",{'height':'62.5px'});
});
at(1.5, function() {
assert_styles("#target",{'height':'75px'});
});
at(1.75, function() {
assert_styles("#target",{'height':'87.5px'});
});
at(2, function() {
assert_styles("#target",{'height':'100px'});
});
at(2.25, function() {
assert_styles("#target",{'height':'106.5px'});
});
at(2.5, function() {
assert_styles("#target",{'height':'125px'});
});
at(2.75, function() {
assert_styles("#target",{'height':'143.5px'});
});
at(3, function() {
assert_styles("#target",{'height':'150px'});
});
at(3.25, function() {
assert_styles("#target",{'height':'150px'});
});
at(3.5, function() {
assert_styles("#target",{'height':'150px'});
});
at(3.75, function() {
assert_styles("#target",{'height':'200px'});
});
at(4, function() {
assert_styles("#target",{'height':'200px'});
});
at(4.25, function() {
assert_styles("#target",{'height':'200px'});
});
at(4.5, function() {
assert_styles("#target",{'height':'225px'});
});
at(4.75, function() {
assert_styles("#target",{'height':'225px'});
});
at(5, function() {
assert_styles("#target",{'height':'250px'});
});
}, "Auto generated tests");
64 changes: 64 additions & 0 deletions test/testcases/auto-test-keyframe-easing.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<style>
#container {
position: relative;
}

#control {
position: absolute;
background-color: black;
width: 20px;
height: 0px;
}

#target {
position: absolute;
left: 20px;
background-color: rgba(0, 128, 0, 0.25);
width: 280px;
height: 0px;
}

.block {
padding-left: 30px;
border-bottom: 1px solid black;
box-sizing: border-box;
width: 300px;
height: 50px;
font-size: 20px;
}
</style>

<div id="container">
<div id="control"></div>
<div id="target"></div>
</div>
<script src="../bootstrap.js"></script>
<script>
var easings = [
'',
'linear',
'ease-in-out',
'step-middle',
'steps(4, end)',
];

var keyframeHeight = 50;
var timing = {duration: easings.length, fill: 'forwards'};

control.animate({height: easings.length * keyframeHeight + 'px'}, timing);

var keyframes = [];
easings.forEach(function (easing, i) {
var textBlock = document.createElement('div');
textBlock.textContent = easing.length ? easing : '<default>';
textBlock.classList.add('block');
container.appendChild(textBlock);
var keyframe = {height: i * keyframeHeight + 'px'};
if (easing.length) {
keyframe.easing = easing;
}
keyframes.push(keyframe);
});
keyframes.push({height: easings.length * keyframeHeight + 'px'});
target.animate(keyframes, timing);
</script>
30 changes: 21 additions & 9 deletions web-animations.js
Original file line number Diff line number Diff line change
Expand Up @@ -2006,7 +2006,8 @@ var expandShorthand = function(property, value, result) {
var normalizeKeyframeDictionary = function(properties) {
var result = {
offset: null,
composite: null
composite: null,
easing: presetTimingFunctions.linear
};
var animationProperties = [];
for (var property in properties) {
Expand All @@ -2020,6 +2021,8 @@ var normalizeKeyframeDictionary = function(properties) {
properties.composite === 'replace') {
result.composite = properties.composite;
}
} else if (property === 'easing') {
result.easing = TimingFunction.createFromString(properties.easing);
} else {
// TODO: Check whether this is a supported property.
animationProperties.push(property);
Expand Down Expand Up @@ -2186,6 +2189,9 @@ KeyframeEffect.prototype = createObject(AnimationEffect.prototype, {
}
var intervalDistance = (timeFraction - startKeyframe.offset) /
(endKeyframe.offset - startKeyframe.offset);
if (startKeyframe.easing) {
intervalDistance = startKeyframe.easing.scaleTime(intervalDistance);
}
return new BlendedCompositableValue(
new AddReplaceCompositableValue(startKeyframe.rawValue(),
this._compositeForKeyframe(startKeyframe)),
Expand All @@ -2207,8 +2213,8 @@ KeyframeEffect.prototype = createObject(AnimationEffect.prototype, {
}
var frame = distributedFrames[i];
this._cachedPropertySpecificKeyframes[property].push(
new PropertySpecificKeyframe(frame.offset,
frame.composite, property, frame.cssValues[property]));
new PropertySpecificKeyframe(frame.offset, frame.composite,
frame.easing, property, frame.cssValues[property]));
}
}

Expand All @@ -2221,12 +2227,12 @@ KeyframeEffect.prototype = createObject(AnimationEffect.prototype, {
// Add synthetic keyframes at offsets of 0 and 1 if required.
if (frames[0].offset !== 0.0) {
var keyframe = new PropertySpecificKeyframe(0.0, 'add',
property, cssNeutralValue);
presetTimingFunctions.linear, property, cssNeutralValue);
frames.unshift(keyframe);
}
if (frames[frames.length - 1].offset !== 1.0) {
var keyframe = new PropertySpecificKeyframe(1.0, 'add',
property, cssNeutralValue);
presetTimingFunctions.linear, property, cssNeutralValue);
frames.push(keyframe);
}
ASSERT_ENABLED && assert(
Expand Down Expand Up @@ -2372,7 +2378,7 @@ KeyframeEffect.prototype = createObject(AnimationEffect.prototype, {
*
* @constructor
*/
var KeyframeInternal = function(offset, composite) {
var KeyframeInternal = function(offset, composite, easing) {
ASSERT_ENABLED && assert(
typeof offset === 'number' || offset === null,
'Invalid offset value');
Expand All @@ -2381,6 +2387,7 @@ var KeyframeInternal = function(offset, composite) {
'Invalid composite value');
this.offset = offset;
this.composite = composite;
this.easing = easing;
this.cssValues = {};
};

Expand All @@ -2405,9 +2412,12 @@ KeyframeInternal.createFromNormalizedProperties = function(properties) {
ASSERT_ENABLED && assert(
isDefinedAndNotNull(properties) && typeof properties === 'object',
'Properties must be an object');
var keyframe = new KeyframeInternal(properties.offset, properties.composite);
var keyframe = new KeyframeInternal(properties.offset, properties.composite,
properties.easing);
for (var candidate in properties) {
if (candidate !== 'offset' && candidate !== 'composite') {
if (candidate !== 'offset' &&
candidate !== 'composite' &&
candidate !== 'easing') {
keyframe.addPropertyValuePair(candidate, properties[candidate]);
}
}
Expand All @@ -2417,9 +2427,11 @@ KeyframeInternal.createFromNormalizedProperties = function(properties) {


/** @constructor */
var PropertySpecificKeyframe = function(offset, composite, property, cssValue) {
var PropertySpecificKeyframe = function(offset, composite, easing, property,
cssValue) {
this.offset = offset;
this.composite = composite;
this.easing = easing;
this.property = property;
this.cssValue = cssValue;
// Calculated lazily
Expand Down