'use strict';
function assert_px_equals(observed, expected, description) {
assert_equals(observed.unit, 'px',
`Unexpected unit type for '${description}'`);
assert_approx_equals(observed.value, expected, 0.0001,
`Unexpected value for ${description}`);
}
function CreateViewTimelineOpacityAnimation(test, target, options) {
const timeline_options = {
subject: target,
axis: 'block'
};
if (options && 'timeline' in options) {
for (let key in options.timeline) {
timeline_options[key] = options.timeline[key];
}
}
const animation_options = {
timeline: new ViewTimeline(timeline_options)
};
if (options && 'animation' in options) {
for (let key in options.animation) {
animation_options[key] = options.animation[key];
}
}
const anim =
target.animate({ opacity: [0.3, 0.7] }, animation_options);
test.add_cleanup(() => {
anim.cancel();
});
return anim;
}
// Verify that range specified in the options aligns with the active range of
// the animation.
//
// Sample call:
// await runTimelineBoundsTest(t, {
// timeline: { inset: [ CSS.percent(0), CSS.percent(20)] },
// timing: { fill: 'both' }
// startOffset: 600,
// endOffset: 900
// });
async function runTimelineBoundsTest(t, options, message) {
const scrollOffsetProp = options.axis == 'block' ? 'scrollTop' : 'scrollLeft';
container[scrollOffsetProp] = 0;
await waitForNextFrame();
const anim =
options.anim ||
CreateViewTimelineOpacityAnimation(t, target, options);
if (options.timing)
anim.effect.updateTiming(options.timing);
const timeline = anim.timeline;
await anim.ready;
// Advance to the start offset, which triggers entry to the active phase.
container[scrollOffsetProp] = options.startOffset;
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '0.3',
`Effect at the start of the active phase: ${message}`);
// Advance to the midpoint of the animation.
container[scrollOffsetProp] = (options.startOffset + options.endOffset) / 2;
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity,'0.5',
`Effect at the midpoint of the active range: ${message}`);
// Advance to the end of the animation.
container[scrollOffsetProp] = options.endOffset;
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '0.7',
`Effect is in the active phase at effect end time: ${message}`);
// Return the animation so that we can continue testing with the same object.
return anim;
}
// Sets the start and end range for a view timeline and ensures that the
// range aligns with expected values.
//
// Sample call:
// await runTimelineRangeTest(t, {
// rangeStart: { rangeName: 'cover', offset: CSS.percent(0) } ,
// rangeEnd: { rangeName: 'cover', offset: CSS.percent(100) },
// startOffset: 600,
// endOffset: 900
// });
async function runTimelineRangeTest(t, options) {
const rangeToString = range => {
const parts = [];
if (range.rangeName)
parts.push(range.rangeName);
if (range.offset)
parts.push(`${range.offset.value}%`);
return parts.join(' ');
};
const range =
`${rangeToString(options.rangeStart)} to ` +
`${rangeToString(options.rangeEnd)}`;
options.timeline = {
axis: options.axis || 'inline'
};
options.animation = {
rangeStart: options.rangeStart,
rangeEnd: options.rangeEnd,
};
options.timing = {
// Set fill to accommodate floating point precision errors at the
// endpoints.
fill: 'both'
};
return runTimelineBoundsTest(t, options, range);
}
// Sets the Inset for a view timeline and ensures that the range aligns with
// expected values.
//
// Sample call:
// await runTimelineInsetTest(t, {
// inset: [ CSS.px(20), CSS.px(40) ]
// startOffset: 600,
// endOffset: 900
// });
async function runTimelineInsetTest(t, options) {
options.timeline = {
axis: 'inline',
inset: options.inset
};
options.timing = {
// Set fill to accommodate floating point precision errors at the
// endpoints.
fill: 'both'
}
const length = options.inset.length;
const range =
(options.inset instanceof Array) ? options.inset.join(' ')
: options.inset;
return runTimelineBoundsTest(t, options, range);
}