From 3c15cc7583ec40a6db9e9e7920a72d5d97c2411a Mon Sep 17 00:00:00 2001 From: ameerabuf Date: Tue, 20 Aug 2024 14:35:14 +0300 Subject: [PATCH] naively supporting cqw and cqh --- dist/index.cjs | 54 ++++++++-- docs/reference/index.html | 203 +++++++++++++++++++++++++++++++++++++- src/view.js | 54 ++++++++-- test/view.spec.js | 22 +++++ 4 files changed, 308 insertions(+), 25 deletions(-) diff --git a/dist/index.cjs b/dist/index.cjs index 9d2ecc7..a916ebb 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -89,10 +89,11 @@ function debounce (fn, interval) { * parses offsetString of the format calc( + ) * @param {string|undefined} offsetString * @param {AbsoluteOffsetContext} absoluteOffsetContext + * @param {HTMLElement} container */ -function parseOffsetCalc(offsetString, absoluteOffsetContext) { - const match = offsetString.match(/^calc\s*\(\s*(-?\d+((px)|(vh)|(vw)))\s*\+\s*(-?\d+((px)|(vh)|(vw)))\s*\)\s*$/); - return transformAbsoluteOffsetToNumber(match[1], absoluteOffsetContext) + transformAbsoluteOffsetToNumber(match[6], absoluteOffsetContext); +function parseOffsetCalc(offsetString, absoluteOffsetContext, container) { + const match = offsetString.match(/^calc\s*\(\s*(-?\d+((px)|(vh)|(vw)|(cqh)|(cqw)))\s*\+\s*(-?\d+((px)|(vh)|(vw)|(cqh)|(cqw)))\s*\)\s*$/); + return transformAbsoluteOffsetToNumber(match[1], absoluteOffsetContext, container) + transformAbsoluteOffsetToNumber(match[8], absoluteOffsetContext, container); } /** @@ -100,9 +101,10 @@ function parseOffsetCalc(offsetString, absoluteOffsetContext) { * * @param {string|undefined} offsetString * @param {AbsoluteOffsetContext} absoluteOffsetContext + * @param {HTMLElement} container * @return {number} */ -function transformAbsoluteOffsetToNumber (offsetString, absoluteOffsetContext) { +function transformAbsoluteOffsetToNumber (offsetString, absoluteOffsetContext, container) { return offsetString ? /^-?\d+px$/.test(offsetString) ? parseInt(offsetString) @@ -110,9 +112,13 @@ function transformAbsoluteOffsetToNumber (offsetString, absoluteOffsetContext) { ? parseInt(offsetString) * absoluteOffsetContext.viewportHeight / 100 : /^-?\d+vw$/.test(offsetString) ? parseInt(offsetString) * absoluteOffsetContext.viewportWidth / 100 - : /^calc\s*\(\s*-?\d+((px)|(vh)|(vw))\s*\+\s*-?\d+((px)|(vh)|(vw))\s*\)\s*$/.test(offsetString) - ? parseOffsetCalc(offsetString, absoluteOffsetContext) - : parseInt(offsetString) || 0 + : /^-?\d+cqh$/.test(offsetString) + ? parseInt(offsetString) * container.offsetHeight / 100 + : /^-?\d+cqw$/.test(offsetString) + ? parseInt(offsetString) * container.offsetWidth / 100 + : /^calc\s*\(\s*-?\d+((px)|(vh)|(vw)|(cqh)|(cqw))\s*\+\s*-?\d+((px)|(vh)|(vw)|(cqh)|(cqw))\s*\)\s*$/.test(offsetString) + ? parseOffsetCalc(offsetString, absoluteOffsetContext, container) + : parseInt(offsetString) || 0 : 0; } @@ -246,10 +252,11 @@ function computeStickinessIntoFullRange(offsetTree, absoluteStartOffset, absolut * @param {number} viewportSize * @param {boolean} isHorizontal * @param {AbsoluteOffsetContext} absoluteOffsetContext + * @param {HTMLElement} container * @param {Array<{element: HTMLElement, offset: number, sticky: {start?: number, end?: number}}>} offsetTree * @return {ScrollScene} */ -function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, absoluteOffsetContext, offsetTree) { +function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, absoluteOffsetContext, container, offsetTree) { const { start, end, duration } = scene; let startOffset = start; @@ -275,7 +282,7 @@ function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, if (startRange || start?.name) { startRange = startRange || start; - const startAdd = transformAbsoluteOffsetToNumber(startRange.add, absoluteOffsetContext); + const startAdd = transformAbsoluteOffsetToNumber(startRange.add, absoluteOffsetContext, container); const absoluteStartOffset = transformRangeToPosition({...startRange, offset: 0}, viewportSize, rect); const absoluteEndOffset = transformRangeToPosition({...startRange, offset: 100}, viewportSize, rect); // we take 0% to 100% of the named range for start, and we compute the position by adding the sticky addition for the given start offset @@ -287,7 +294,7 @@ function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, if (endRange || end?.name) { endRange = endRange || end; - const endAdd = transformAbsoluteOffsetToNumber(endRange.add, absoluteOffsetContext); + const endAdd = transformAbsoluteOffsetToNumber(endRange.add, absoluteOffsetContext, container); const absoluteStartOffset = transformRangeToPosition({...endRange, offset: 0}, viewportSize, rect); const absoluteEndOffset = transformRangeToPosition({...endRange, offset: 100}, viewportSize, rect); // we take 0% to 100% of the named range for end, and we compute the position by adding the sticky addition for the given end offset @@ -303,6 +310,16 @@ function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, return {...scene, start: startOffset, end: endOffset, startRange, endRange, duration: overrideDuration || duration }; } +/** + * Check whether the position of an element is sticky. + * + * @param {CSSStyleDeclaration} style + * @return {boolean} + */ +function getIsContainer (style) { + return style.containerType ? style.containerType !== 'normal' : false; +} + /** * Check whether the position of an element is sticky. * @@ -396,6 +413,8 @@ function getTransformedScene (scene, root, viewportSize, isHorizontal, absoluteO const elementStickiness = isElementSticky ? getStickyData(elementStyle, isHorizontal) : undefined; let parent = element.offsetParent; + let foundContainer = getIsContainer(elementStyle); + let container = foundContainer ? element : null; let elementLayoutStart = 0; let isFixed = elementStyle.position === 'fixed'; const elementOffset = getRectStart(element, isHorizontal, isElementSticky); @@ -418,11 +437,16 @@ function getTransformedScene (scene, root, viewportSize, isHorizontal, absoluteO if (parent === root) { offsetTree.push({element: parent, offset: 0}); // if we're at the root don't add its own offset + if (!foundContainer) { + container = parent; + foundContainer = true; + } break; } const nodeStyle = window.getComputedStyle(parent); const isSticky = getIsSticky(nodeStyle); + const isContainer = getIsContainer(nodeStyle); const sticky = isSticky ? getStickyData(nodeStyle, isHorizontal) : undefined; // get the base offset of the source element - before adding sticky intervals @@ -433,10 +457,19 @@ function getTransformedScene (scene, root, viewportSize, isHorizontal, absoluteO elementLayoutStart += offset; } + if (!foundContainer && isContainer) { + container = parent; + foundContainer = true; + } + offsetTree.push({element: parent, offset, sticky}); parent = parent.offsetParent; if (!parent) { + if (!foundContainer) { + container = element; + foundContainer = true; + } // only if offsetParent is null do we know that the fixed element is actually fixed to the viewport and we need to set duration to 0 isFixed = nodeStyle.position === 'fixed'; } @@ -450,6 +483,7 @@ function getTransformedScene (scene, root, viewportSize, isHorizontal, absoluteO viewportSize, isHorizontal, absoluteOffsetContext, + container, offsetTree ); diff --git a/docs/reference/index.html b/docs/reference/index.html index 7b82862..1d65c4b 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -2,7 +2,7 @@ - fizban 0.6.5 | Documentation + fizban 0.6.7 | Documentation @@ -15,7 +15,7 @@

fizban

-
0.6.5
+
0.6.7
fizban +
  • + parseOffsetCalc + + + +
  • + +
  • @@ -280,6 +290,16 @@

    fizban

  • +
  • + getIsContainer + + + +
  • + +
  • @@ -1418,7 +1438,7 @@

    Type: - ("entry" | "contain" | "exit" | "cover") + ("entry" | "contain" | "exit" | "cover" | "entry-crossing" | "exit-crossing")

    @@ -2062,6 +2082,85 @@

    + + + + + + + + + + + + + + +
    + + +
    + +

    + parseOffsetCalc +

    + + +
    + + +

    parses offsetString of the format calc( + )

    + +
    + + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + offsetString ((string | undefined)) + +
    + +
    + +
    +
    + absoluteOffsetContext (AbsoluteOffsetContext) + +
    + +
    + +
    +
    + container (HTMLElement) + +
    + +
    + +
    + + + + + + + + + + @@ -2091,7 +2190,7 @@

    Convert an absolute offset as string to number of pixels

    -
    transformAbsoluteOffsetToNumber(offsetString: (string | undefined), absoluteOffsetContext: AbsoluteOffsetContext): number
    +
    transformAbsoluteOffsetToNumber(offsetString: (string | undefined), absoluteOffsetContext: AbsoluteOffsetContext, container: HTMLElement): number
    @@ -2122,6 +2221,14 @@

  • +
    +
    + container (HTMLElement) + +
    + +
    +
    @@ -2255,7 +2362,7 @@

    Convert scene data in ranges into offsets in pixels.

    -
    transformSceneRangesToOffsets(scene: ScrollScene, rect: {start: number, end: number}, viewportSize: number, isHorizontal: boolean, absoluteOffsetContext: AbsoluteOffsetContext): ScrollScene
    +
    transformSceneRangesToOffsets(scene: ScrollScene, rect: {start: number, end: number}, viewportSize: number, isHorizontal: boolean, absoluteOffsetContext: AbsoluteOffsetContext, container: HTMLElement, offsetTree: any): ScrollScene
    @@ -2310,6 +2417,22 @@

    +
    +
    + container (HTMLElement) + +
    + +
    + +
    +
    + offsetTree (any) + +
    + +
    + @@ -2337,6 +2460,76 @@

    + + + + + +
    + + +
    + +

    + getIsContainer +

    + + +
    + + +

    Check whether the position of an element is sticky.

    + +
    getIsContainer(style: CSSStyleDeclaration): boolean
    + + + + + + + + + + + +
    Parameters
    +
    + +
    +
    + style (CSSStyleDeclaration) + +
    + +
    + +
    + + + + + + +
    Returns
    + boolean: + + + + + + + + + + + + + + + + + +
    diff --git a/src/view.js b/src/view.js index 92ba515..e45b75e 100644 --- a/src/view.js +++ b/src/view.js @@ -2,10 +2,11 @@ * parses offsetString of the format calc( + ) * @param {string|undefined} offsetString * @param {AbsoluteOffsetContext} absoluteOffsetContext + * @param {HTMLElement} container */ -function parseOffsetCalc(offsetString, absoluteOffsetContext) { - const match = offsetString.match(/^calc\s*\(\s*(-?\d+((px)|(vh)|(vw)))\s*\+\s*(-?\d+((px)|(vh)|(vw)))\s*\)\s*$/); - return transformAbsoluteOffsetToNumber(match[1], absoluteOffsetContext) + transformAbsoluteOffsetToNumber(match[6], absoluteOffsetContext); +function parseOffsetCalc(offsetString, absoluteOffsetContext, container) { + const match = offsetString.match(/^calc\s*\(\s*(-?\d+((px)|(vh)|(vw)|(cqh)|(cqw)))\s*\+\s*(-?\d+((px)|(vh)|(vw)|(cqh)|(cqw)))\s*\)\s*$/); + return transformAbsoluteOffsetToNumber(match[1], absoluteOffsetContext, container) + transformAbsoluteOffsetToNumber(match[8], absoluteOffsetContext, container); } /** @@ -13,9 +14,10 @@ function parseOffsetCalc(offsetString, absoluteOffsetContext) { * * @param {string|undefined} offsetString * @param {AbsoluteOffsetContext} absoluteOffsetContext + * @param {HTMLElement} container * @return {number} */ -function transformAbsoluteOffsetToNumber (offsetString, absoluteOffsetContext) { +function transformAbsoluteOffsetToNumber (offsetString, absoluteOffsetContext, container) { return offsetString ? /^-?\d+px$/.test(offsetString) ? parseInt(offsetString) @@ -23,9 +25,13 @@ function transformAbsoluteOffsetToNumber (offsetString, absoluteOffsetContext) { ? parseInt(offsetString) * absoluteOffsetContext.viewportHeight / 100 : /^-?\d+vw$/.test(offsetString) ? parseInt(offsetString) * absoluteOffsetContext.viewportWidth / 100 - : /^calc\s*\(\s*-?\d+((px)|(vh)|(vw))\s*\+\s*-?\d+((px)|(vh)|(vw))\s*\)\s*$/.test(offsetString) - ? parseOffsetCalc(offsetString, absoluteOffsetContext) - : parseInt(offsetString) || 0 + : /^-?\d+cqh$/.test(offsetString) + ? parseInt(offsetString) * container.offsetHeight / 100 + : /^-?\d+cqw$/.test(offsetString) + ? parseInt(offsetString) * container.offsetWidth / 100 + : /^calc\s*\(\s*-?\d+((px)|(vh)|(vw)|(cqh)|(cqw))\s*\+\s*-?\d+((px)|(vh)|(vw)|(cqh)|(cqw))\s*\)\s*$/.test(offsetString) + ? parseOffsetCalc(offsetString, absoluteOffsetContext, container) + : parseInt(offsetString) || 0 : 0; } @@ -159,10 +165,11 @@ function computeStickinessIntoFullRange(offsetTree, absoluteStartOffset, absolut * @param {number} viewportSize * @param {boolean} isHorizontal * @param {AbsoluteOffsetContext} absoluteOffsetContext + * @param {HTMLElement} container * @param {Array<{element: HTMLElement, offset: number, sticky: {start?: number, end?: number}}>} offsetTree * @return {ScrollScene} */ -function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, absoluteOffsetContext, offsetTree) { +function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, absoluteOffsetContext, container, offsetTree) { const { start, end, duration } = scene; let startOffset = start; @@ -188,7 +195,7 @@ function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, if (startRange || start?.name) { startRange = startRange || start; - const startAdd = transformAbsoluteOffsetToNumber(startRange.add, absoluteOffsetContext); + const startAdd = transformAbsoluteOffsetToNumber(startRange.add, absoluteOffsetContext, container); const absoluteStartOffset = transformRangeToPosition({...startRange, offset: 0}, viewportSize, rect); const absoluteEndOffset = transformRangeToPosition({...startRange, offset: 100}, viewportSize, rect); // we take 0% to 100% of the named range for start, and we compute the position by adding the sticky addition for the given start offset @@ -200,7 +207,7 @@ function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, if (endRange || end?.name) { endRange = endRange || end; - const endAdd = transformAbsoluteOffsetToNumber(endRange.add, absoluteOffsetContext); + const endAdd = transformAbsoluteOffsetToNumber(endRange.add, absoluteOffsetContext, container); const absoluteStartOffset = transformRangeToPosition({...endRange, offset: 0}, viewportSize, rect); const absoluteEndOffset = transformRangeToPosition({...endRange, offset: 100}, viewportSize, rect); // we take 0% to 100% of the named range for end, and we compute the position by adding the sticky addition for the given end offset @@ -216,6 +223,16 @@ function transformSceneRangesToOffsets (scene, rect, viewportSize, isHorizontal, return {...scene, start: startOffset, end: endOffset, startRange, endRange, duration: overrideDuration || duration }; } +/** + * Check whether the position of an element is sticky. + * + * @param {CSSStyleDeclaration} style + * @return {boolean} + */ +function getIsContainer (style) { + return style.containerType ? style.containerType !== 'normal' : false; +} + /** * Check whether the position of an element is sticky. * @@ -309,6 +326,8 @@ export function getTransformedScene (scene, root, viewportSize, isHorizontal, ab const elementStickiness = isElementSticky ? getStickyData(elementStyle, isHorizontal) : undefined; let parent = element.offsetParent; + let foundContainer = getIsContainer(elementStyle); + let container = foundContainer ? element : null; let elementLayoutStart = 0; let isFixed = elementStyle.position === 'fixed'; const elementOffset = getRectStart(element, isHorizontal, isElementSticky); @@ -331,11 +350,16 @@ export function getTransformedScene (scene, root, viewportSize, isHorizontal, ab if (parent === root) { offsetTree.push({element: parent, offset: 0}); // if we're at the root don't add its own offset + if (!foundContainer) { + container = parent; + foundContainer = true; + } break; } const nodeStyle = window.getComputedStyle(parent); const isSticky = getIsSticky(nodeStyle); + const isContainer = getIsContainer(nodeStyle); const sticky = isSticky ? getStickyData(nodeStyle, isHorizontal) : undefined; // get the base offset of the source element - before adding sticky intervals @@ -346,10 +370,19 @@ export function getTransformedScene (scene, root, viewportSize, isHorizontal, ab elementLayoutStart += offset; } + if (!foundContainer && isContainer) { + container = parent + foundContainer = true; + } + offsetTree.push({element: parent, offset, sticky}); parent = parent.offsetParent; if (!parent) { + if (!foundContainer) { + container = element; + foundContainer = true; + } // only if offsetParent is null do we know that the fixed element is actually fixed to the viewport and we need to set duration to 0 isFixed = nodeStyle.position === 'fixed'; } @@ -363,6 +396,7 @@ export function getTransformedScene (scene, root, viewportSize, isHorizontal, ab viewportSize, isHorizontal, absoluteOffsetContext, + container, offsetTree ); diff --git a/test/view.spec.js b/test/view.spec.js index a21f292..acc277f 100644 --- a/test/view.spec.js +++ b/test/view.spec.js @@ -287,3 +287,25 @@ test('start :: entry-crossing 40% :: end :: exit-crossing 70%', t => { t.is(result.start, 10); t.is(result.end, 105); }); + +test('start :: entry 40% :: start add calc(10cqh + 25px) :: end cover 100% :: end add calc(100cqw + 100px)', t => { + const input = getScene({ + viewSource: { + offsetParent: { + offsetWidth: 100, + offsetHeight: 200, + style: { + containerType: 'size' + } + }, + offsetHeight: 150, + }, + start: {name: 'entry', offset: 40, add: 'calc(10cqh + 25px)'}, // add = 20px + 25px = 45px + end: {name: 'cover', offset: 100, add: 'calc(100cqw + 100px)'}, // add = 100px + 100px = 200px + }); + + const result = getTransformedScene(input, null, SMALLER_VIEWPORT_SIZE, IS_HORIZONTAL, smallAbsoluteOffsetContext); + + t.is(result.start, 15); // -30 + 45 = 15 + t.is(result.end, 350); // 150 + 200 = 350 +});