diff --git a/src/scroll-timeline-base.js b/src/scroll-timeline-base.js index 020ff2be..8191a542 100644 --- a/src/scroll-timeline-base.js +++ b/src/scroll-timeline-base.js @@ -564,23 +564,29 @@ export function calculateRange(phase, container, target, axis, optionsInset) { const rtl = style.direction == 'rtl' || style.writingMode == 'vertical-rl'; let viewSize = undefined; let viewPos = undefined; - let containerSize = undefined; + let sizes = { + fontSize: getComputedStyle(target).fontSize + }; if (axis == 'x' || (axis == 'inline' && horizontalWritingMode) || (axis == 'block' && !horizontalWritingMode)) { viewSize = target.offsetWidth; viewPos = left; - if (rtl) + sizes.scrollPadding = [style.scrollPaddingLeft, style.scrollPaddingRight]; + if (rtl) { viewPos += container.scrollWidth - container.clientWidth; - containerSize = container.clientWidth; + sizes.scrollPadding = [style.scrollPaddingRight, style.scrollPaddingLeft]; + } + sizes.containerSize = container.clientWidth; } else { // TODO: support sideways-lr viewSize = target.offsetHeight; viewPos = top; - containerSize = container.clientHeight; + sizes.scrollPadding = [style.scrollPaddingTop, style.scrollPaddingBottom]; + sizes.containerSize = container.clientHeight; } - const inset = parseInset(optionsInset, containerSize); + const inset = parseInset(optionsInset, sizes); // Cover: // 0% progress represents the position at which the start border edge of the @@ -589,7 +595,7 @@ export function calculateRange(phase, container, target, axis, optionsInset) { // 100% progress represents the position at which the end border edge of the // element’s principal box coincides with the start edge of its view progress // visibility range. - const coverStartOffset = viewPos - containerSize + inset.end; + const coverStartOffset = viewPos - sizes.containerSize + inset.end; const coverEndOffset = viewPos + viewSize - inset.start; // Contain: @@ -612,7 +618,7 @@ export function calculateRange(phase, container, target, axis, optionsInset) { let startOffset = undefined; let endOffset = undefined; - const targetIsTallerThanContainer = viewSize > containerSize ? true : false; + const targetIsTallerThanContainer = viewSize > sizes.containerSize ? true : false; switch(phase) { case 'cover': @@ -649,39 +655,52 @@ export function calculateRange(phase, container, target, axis, optionsInset) { return { start: startOffset, end: endOffset }; } -function parseInset(value, containerSize) { - const inset = { start: 0, end: 0 }; - - if(!value) - return inset; - - const parts = value.split(' '); - const insetParts = []; - parts.forEach(part => { - // TODO: Add support for relative lengths (e.g. em) - if(part.endsWith("%")) - insetParts.push(containerSize / 100 * parseFloat(part)); - else if(part.endsWith("px")) - insetParts.push(parseFloat(part)); - else if(part === "auto") - insetParts.push(0); - else - throw TypeError("Unsupported inset. Only % and px values are supported (for now)."); - }); - - if (insetParts.length > 2) { - throw TypeError("Invalid inset"); +function parseInset(value, sizes) { + const inset = {start: 0, end: 0}; + + if (!value) return inset; + + let parts = value; + // Parse string parts to + if (typeof value === 'string') { + parts = value.split(' ').map(part => { + if (part.endsWith('%')) { + return CSS.percent(parseFloat(part)); + } else if (part.endsWith('px')) { + return CSS.px(parseFloat(part)); + } else if (part.endsWith('em')) { + return CSS.em(parseFloat(part)); + } else if (part === 'auto') { + return part; + } else { + throw TypeError('Unsupported inset. Only %, px, em and auto values are supported (for now).'); + } + }); } - - if(insetParts.length == 1) { - inset.start = insetParts[0]; - inset.end = insetParts[0]; - } else if(insetParts.length == 2) { - inset.start = insetParts[0]; - inset.end = insetParts[1]; + if (parts.length === 0 || parts.length > 2) { + throw TypeError('Invalid inset'); } - return inset; + const startPart = parts[0]; + const endPart = parts[1] ?? parts[0]; + + const [start, end] = [startPart, endPart].map((part, i) => { + if (part === 'auto') { + return sizes.scrollPadding[i] === 'auto' ? 0 : parseFloat(sizes.scrollPadding[i]); + } + + const simplifiedUnit = simplifyCalculation(part, { + percentageReference: CSS.px(sizes.containerSize), + fontSize: CSS.px(parseFloat(sizes.fontSize)) + }); + if (simplifiedUnit instanceof CSSUnitValue && simplifiedUnit.unit === 'px') { + return simplifiedUnit.value; + } else { + throw TypeError('Unsupported inset. Only %, px, em and auto values are supported (for now).'); + } + }); + + return {start, end}; } // Calculate the fractional offset of a (phase, percent) pair relative to the @@ -773,4 +792,12 @@ export class ViewTimeline extends ScrollTimeline { return CSS.percent(100 * progress); } + get startOffset() { + return CSS.px(scrollTimelineOptions.get(this).ranges['cover'].start); + } + + get endOffset() { + return CSS.px(scrollTimelineOptions.get(this).ranges['cover'].end); + } + } diff --git a/src/simplify-calculation.js b/src/simplify-calculation.js index da76e7fb..11b5bbb8 100644 --- a/src/simplify-calculation.js +++ b/src/simplify-calculation.js @@ -1,7 +1,7 @@ import {isCanonical} from "./utils"; /** - * @typedef {{percentageReference: CSSUnitValue}} Info + * @typedef {{percentageReference: CSSUnitValue, fontSize?: CSSUnitValue}} Info */ /** @@ -81,7 +81,9 @@ export function simplifyCalculation(root, info) { root = sum.values[0]; } // TODO: handle relative lengths - + if (root instanceof CSSUnitValue && root.unit === 'em' && info.fontSize) { + root = new CSSUnitValue(root.value * info.fontSize.value, info.fontSize.unit) + } // 3. If root is a , return its numeric value. // 4. Otherwise, return root. return root; diff --git a/test/expected.txt b/test/expected.txt index 80f694b9..a7200bf3 100644 --- a/test/expected.txt +++ b/test/expected.txt @@ -933,17 +933,17 @@ FAIL /scroll-animations/view-timelines/timeline-offset-in-keyframe.html Timeline FAIL /scroll-animations/view-timelines/unattached-subject-inset.html Creating a view timeline with a subject that is not attached to the document works as expected FAIL /scroll-animations/view-timelines/view-timeline-get-current-time-range-name.html View timeline current time for named range FAIL /scroll-animations/view-timelines/view-timeline-get-set-range.html Getting and setting the animation range -FAIL /scroll-animations/view-timelines/view-timeline-inset.html View timeline with px based inset. -FAIL /scroll-animations/view-timelines/view-timeline-inset.html View timeline with percent based inset. -FAIL /scroll-animations/view-timelines/view-timeline-inset.html view timeline with inset auto. -FAIL /scroll-animations/view-timelines/view-timeline-inset.html view timeline with font relative inset. +PASS /scroll-animations/view-timelines/view-timeline-inset.html View timeline with px based inset. +PASS /scroll-animations/view-timelines/view-timeline-inset.html View timeline with percent based inset. +PASS /scroll-animations/view-timelines/view-timeline-inset.html view timeline with inset auto. +PASS /scroll-animations/view-timelines/view-timeline-inset.html view timeline with font relative inset. FAIL /scroll-animations/view-timelines/view-timeline-inset.html view timeline with viewport relative insets. -FAIL /scroll-animations/view-timelines/view-timeline-inset.html view timeline inset as string +PASS /scroll-animations/view-timelines/view-timeline-inset.html view timeline inset as string PASS /scroll-animations/view-timelines/view-timeline-inset.html view timeline with invalid inset PASS /scroll-animations/view-timelines/view-timeline-missing-subject.html ViewTimeline with missing subject PASS /scroll-animations/view-timelines/view-timeline-on-display-none-element.html element with display: none should have inactive viewtimeline PASS /scroll-animations/view-timelines/view-timeline-range-large-subject.html View timeline with range set via delays. -FAIL /scroll-animations/view-timelines/view-timeline-range.html View timeline with range as pair. +PASS /scroll-animations/view-timelines/view-timeline-range.html View timeline with range as pair. PASS /scroll-animations/view-timelines/view-timeline-range.html View timeline with range and inferred name or offset. PASS /scroll-animations/view-timelines/view-timeline-range.html View timeline with range as pair. PASS /scroll-animations/view-timelines/view-timeline-range.html View timeline with range as pair. @@ -957,4 +957,4 @@ FAIL /scroll-animations/view-timelines/view-timeline-sticky-block.html View time FAIL /scroll-animations/view-timelines/view-timeline-sticky-inline.html View timeline with sticky target, block axis. FAIL /scroll-animations/view-timelines/view-timeline-subject-size-changes.html View timeline with subject size change after the creation of the animation FAIL /scroll-animations/view-timelines/zero-intrinsic-iteration-duration.tentative.html Intrinsic iteration duration is non-negative -Passed 394 of 959 tests. +Passed 400 of 959 tests.