Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
2cc744d5
Commit
2cc744d5
authored
Apr 26, 2021
by
Robert Knight
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add utilities for scrolling an element to a target offset
parent
3b9a71b3
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
145 additions
and
0 deletions
+145
-0
scroll.js
src/annotator/util/scroll.js
+74
-0
scroll-test.js
src/annotator/util/test/scroll-test.js
+71
-0
No files found.
src/annotator/util/scroll.js
0 → 100644
View file @
2cc744d5
/**
* Return a promise that resolves on the next animation frame.
*/
function
nextAnimationFrame
()
{
return
new
Promise
(
resolve
=>
{
requestAnimationFrame
(
resolve
);
});
}
/**
* Linearly interpolate between two values.
*
* @param {number} a
* @param {number} b
* @param {number} fraction - Value in [0, 1]
*/
function
interpolate
(
a
,
b
,
fraction
)
{
return
a
+
fraction
*
(
b
-
a
);
}
/**
* Return the offset of `element` from the top of a positioned ancestor `parent`.
*
* @param {HTMLElement} element
* @param {HTMLElement} parent - Positioned ancestor of `element`
* @return {number}
*/
export
function
offsetRelativeTo
(
element
,
parent
)
{
let
offset
=
0
;
while
(
element
!==
parent
&&
parent
.
contains
(
element
))
{
offset
+=
element
.
offsetTop
;
element
=
/** @type {HTMLElement} */
(
element
.
offsetParent
);
}
return
offset
;
}
/**
* Scroll `element` until its `scrollTop` offset reaches a target value.
*
* @param {Element} element - Container element to scroll
* @param {number} offset - Target value for the scroll offset
* @param {object} options
* @param {number} [options.maxDuration]
* @return {Promise<void>} - A promise that resolves once the scroll animation
* is complete
*/
export
async
function
scrollElement
(
element
,
offset
,
{
maxDuration
=
500
}
=
{}
)
{
const
initialOffset
=
element
.
scrollTop
;
const
targetOffset
=
offset
;
const
scrollStart
=
Date
.
now
();
// Choose a scroll duration proportional to the scroll distance, but capped
// to avoid it being too slow.
const
pixelsPerMs
=
3
;
const
scrollDuration
=
Math
.
min
(
Math
.
abs
(
targetOffset
-
initialOffset
)
/
pixelsPerMs
,
maxDuration
);
let
scrollFraction
=
0.0
;
while
(
scrollFraction
<
1.0
)
{
await
nextAnimationFrame
();
scrollFraction
=
Math
.
min
(
1.0
,
(
Date
.
now
()
-
scrollStart
)
/
scrollDuration
);
element
.
scrollTop
=
interpolate
(
initialOffset
,
targetOffset
,
scrollFraction
);
}
}
src/annotator/util/test/scroll-test.js
0 → 100644
View file @
2cc744d5
import
{
offsetRelativeTo
,
scrollElement
}
from
'../scroll'
;
describe
(
'annotator/util/scroll'
,
()
=>
{
let
containers
;
beforeEach
(()
=>
{
sinon
.
stub
(
window
,
'requestAnimationFrame'
);
window
.
requestAnimationFrame
.
yields
();
containers
=
[];
});
afterEach
(()
=>
{
containers
.
forEach
(
c
=>
c
.
remove
());
window
.
requestAnimationFrame
.
restore
();
});
function
createContainer
()
{
const
el
=
document
.
createElement
(
'div'
);
containers
.
push
(
el
);
document
.
body
.
append
(
el
);
return
el
;
}
describe
(
'offsetRelativeTo'
,
()
=>
{
it
(
'returns the offset of an element relative to the given ancestor'
,
()
=>
{
const
parent
=
createContainer
();
parent
.
style
.
position
=
'relative'
;
const
child
=
document
.
createElement
(
'div'
);
child
.
style
.
position
=
'absolute'
;
child
.
style
.
top
=
'100px'
;
parent
.
append
(
child
);
const
grandchild
=
document
.
createElement
(
'div'
);
grandchild
.
style
.
position
=
'absolute'
;
grandchild
.
style
.
top
=
'150px'
;
child
.
append
(
grandchild
);
assert
.
equal
(
offsetRelativeTo
(
child
,
parent
),
100
);
assert
.
equal
(
offsetRelativeTo
(
grandchild
,
parent
),
250
);
});
it
(
'returns 0 if the parent is not an ancestor of the element'
,
()
=>
{
const
parent
=
document
.
createElement
(
'div'
);
const
child
=
document
.
createElement
(
'div'
);
child
.
style
.
position
=
'absolute'
;
child
.
style
.
top
=
'100px'
;
assert
.
equal
(
offsetRelativeTo
(
child
,
parent
),
0
);
});
});
describe
(
'scrollElement'
,
()
=>
{
it
(
"animates the element's `scrollTop` offset to the target position"
,
async
()
=>
{
const
container
=
createContainer
();
container
.
style
.
overflow
=
'scroll'
;
container
.
style
.
width
=
'200px'
;
container
.
style
.
height
=
'500px'
;
container
.
style
.
position
=
'relative'
;
const
child
=
document
.
createElement
(
'div'
);
child
.
style
.
height
=
'3000px'
;
container
.
append
(
child
);
await
scrollElement
(
container
,
2000
,
{
maxDuration
:
5
});
assert
.
equal
(
container
.
scrollTop
,
2000
);
container
.
remove
();
});
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment