mirror of
https://github.com/servo/servo.git
synced 2025-10-14 23:40:26 +01:00
215 lines
No EOL
6.7 KiB
HTML
215 lines
No EOL
6.7 KiB
HTML
<!DOCTYPE html>
|
|
<meta charset=utf-8>
|
|
<title>Test clamping logic of element-based scroll offset for scroll timeline.</title>
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/web-animations/testcommon.js"></script>
|
|
<script src="testcommon.js"></script>
|
|
|
|
<style>
|
|
.scroller {
|
|
overflow: scroll;
|
|
height: 500px;
|
|
width: 500px;
|
|
will-change: transform;
|
|
}
|
|
|
|
/* Disable scrollbars to simplify the calculations in the test. */
|
|
.scroller {
|
|
scrollbar-width: 0;
|
|
}
|
|
/*
|
|
Chrome does not support scrollbar-width so we use this non-standard property
|
|
until it does.
|
|
*/
|
|
.scroller::-webkit-scrollbar {
|
|
display: none;
|
|
}
|
|
|
|
.contents {
|
|
height: 1200px;
|
|
width: 1200px;
|
|
position: relative;
|
|
}
|
|
|
|
.vertical #target {
|
|
background: blue;
|
|
border-top: 0px solid pink;
|
|
border-bottom: 0px solid pink;
|
|
box-sizing: border-box;
|
|
position: absolute;
|
|
width: 100%;
|
|
top: var(--start-position);
|
|
height: calc(var(--end-position) - var(--start-position));
|
|
}
|
|
|
|
.horizontal #target {
|
|
background: blue;
|
|
border-left: 0px solid pink;
|
|
border-right: 0px solid pink;
|
|
box-sizing: border-box;
|
|
position: absolute;
|
|
height: 100%;
|
|
left: var(--start-position);
|
|
width: calc(var(--end-position) - var(--start-position));
|
|
}
|
|
</style>
|
|
<div id="log"></div>
|
|
<script>
|
|
'use strict';
|
|
|
|
function createScrollerWithTarget(test, config) {
|
|
const orientationClass = config.orientation;
|
|
const positions = `
|
|
--start-position: ${config.startElementPosition};
|
|
--end-position: ${config.endElementPosition};`
|
|
|
|
var scroller = createDiv(test);
|
|
scroller.innerHTML =
|
|
`<div class='contents' style="${positions}">
|
|
<div id='target'></div>
|
|
</div>`;
|
|
scroller.classList.add('scroller');
|
|
scroller.classList.add(orientationClass);
|
|
|
|
return scroller;
|
|
}
|
|
|
|
async function createScrollAnimationTest(description, config) {
|
|
promise_test(async t => {
|
|
const scroller = createScrollerWithTarget(t, config);
|
|
t.add_cleanup(() => scroller.remove());
|
|
|
|
const target = scroller.querySelector("#target");
|
|
|
|
const timeline = createScrollTimeline(t, {
|
|
scrollSource: scroller,
|
|
orientation: config.orientation,
|
|
timeRange: 1000,
|
|
fill: 'both',
|
|
startScrollOffset: {target: target, edge: 'end', ...config.start},
|
|
endScrollOffset: {target: target, edge:'start', ...config.end }
|
|
});
|
|
|
|
// Wait for new animation frame which allows the timeline to compute new
|
|
// current time.
|
|
await waitForNextFrame();
|
|
|
|
const animation = createScrollLinkedAnimation(t, timeline);
|
|
const timeRange = animation.timeline.timeRange;
|
|
|
|
// Verify initial start and current times in Idle state.
|
|
assert_equals(animation.currentTime, null,
|
|
"The current time is null in Idle state.");
|
|
assert_equals(animation.startTime, null,
|
|
"The start time is null in Idle state.");
|
|
|
|
animation.play();
|
|
assert_true(animation.pending, "Animation is in pending state.");
|
|
// Verify initial start and current times in Pending state.
|
|
assert_times_equal(animation.currentTime, 0,
|
|
"The current time is zero in Pending state.");
|
|
assert_equals(animation.startTime, 0,
|
|
"The start time is zero in Pending state.");
|
|
|
|
await animation.ready;
|
|
// Verify initial start and current times in Playing state.
|
|
assert_times_equal(animation.currentTime, 0,
|
|
"The current time is zero in Playing state.");
|
|
assert_times_equal(animation.startTime, 0,
|
|
"The start time is zero in Playing state.");
|
|
|
|
// Now do some scrolling and make sure that the Animation current time is
|
|
// correct.
|
|
if (config.orientation == 'vertical') {
|
|
scroller.scrollTo({top: config.scrollTo});
|
|
assert_equals(scroller.scrollTop, config.scrollTo);
|
|
} else {
|
|
scroller.scrollTo({left: config.scrollTo});
|
|
assert_equals(scroller.scrollLeft, config.scrollTo);
|
|
}
|
|
|
|
await waitForNextFrame();
|
|
|
|
assert_times_equal(animation.timeline.currentTime, config.expectedCurrentTime,
|
|
"The timeline current time corresponds to the scroll position of the scroller.");
|
|
assert_times_equal(animation.currentTime, config.expectedCurrentTime,
|
|
"The animation current time corresponds to the scroll position of the scroller.");
|
|
assert_times_equal(
|
|
animation.effect.getComputedTiming().localTime,
|
|
config.expectedCurrentTime,
|
|
'Effect local time corresponds to the scroll position of the scroller.');
|
|
}, description);
|
|
}
|
|
|
|
// We have no scrollbar and the scroller is symmetric on x & y axis so this
|
|
// static value is axis and platform agnostic.
|
|
const scroll_max = 700;
|
|
|
|
// For this test we setup a single target, and scroll timeline in a way that
|
|
// our animation runs from when target enters the scroll port until it fully
|
|
// exits it. Then we create various edgecase scenarios to see the clamping
|
|
// logic.
|
|
//
|
|
// Scroller has 500px heights with 1200px content which translates to
|
|
// 0 < scroll < 700px
|
|
//
|
|
// +----------+ ^
|
|
// | | |
|
|
// | Scroller | |
|
|
// | | | scrollRange
|
|
// | | |
|
|
// +----------+ | +--+
|
|
// |TT| | |TT|
|
|
// +--+ v +----------+
|
|
// | |
|
|
// | Scroller |
|
|
// | |
|
|
// | |
|
|
// +----------+
|
|
//
|
|
// For each test the expected timeline start/end is in the comment to help
|
|
// with the verification.
|
|
const tests = {
|
|
// offsets: [0, 600]
|
|
"no clamping is expected": {
|
|
startElementPosition: '500px',
|
|
endElementPosition: '600px',
|
|
scrollTo: 300,
|
|
expectedCurrentTime: 500,
|
|
},
|
|
// offsets: [0, 600]
|
|
"start is visible at zero offset and should get clamped": {
|
|
startElementPosition: '400px',
|
|
endElementPosition: '600px',
|
|
scrollTo: 300,
|
|
expectedCurrentTime: 500,
|
|
},
|
|
|
|
// offsets: [0, scroll_max]
|
|
"end is not reachable and should be clamped": {
|
|
startElementPosition: '500px',
|
|
endElementPosition: '800px',
|
|
scrollTo: scroll_max / 2,
|
|
expectedCurrentTime: 500,
|
|
},
|
|
|
|
// offsets: [0, scroll_max]
|
|
"both start and end are clamped": {
|
|
startElementPosition: '400px',
|
|
endElementPosition: '800px',
|
|
scrollTo: scroll_max / 2,
|
|
expectedCurrentTime: 500,
|
|
},
|
|
};
|
|
|
|
for (let orientation of ['vertical', 'horizontal']) {
|
|
for (let testName in tests) {
|
|
const description = `Animation start and current times are correct given
|
|
element-based offsets for orienation ${orientation} and ${testName}.`;
|
|
const config = tests[testName];
|
|
config.orientation = orientation;
|
|
createScrollAnimationTest(description, config);
|
|
}
|
|
}
|
|
</script> |