diff --git a/app/src/assets/scss/business/_av.scss b/app/src/assets/scss/business/_av.scss index c640ee12d..2d5ac225f 100644 --- a/app/src/assets/scss/business/_av.scss +++ b/app/src/assets/scss/business/_av.scss @@ -140,12 +140,10 @@ &--header, &--footer { background-color: var(--av-background); - position: relative; - z-index: 2; } &--footer { - display: inline-flex; + display: flex; border-top: 1px solid var(--b3-theme-surface-lighter); color: var(--b3-theme-on-surface); position: relative; diff --git a/app/src/protyle/render/av/render.ts b/app/src/protyle/render/av/render.ts index 0ec4d539e..763470b93 100644 --- a/app/src/protyle/render/av/render.ts +++ b/app/src/protyle/render/av/render.ts @@ -7,7 +7,6 @@ import {unicode2Emoji} from "../../../emoji"; import {focusBlock} from "../../util/selection"; import {isMac} from "../../util/compatibility"; import {hasClosestByClassName} from "../../util/hasClosest"; -import {avScroll} from "./scroll"; export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) => { let avElements: Element[] = []; @@ -42,6 +41,8 @@ export const avRender = (element: Element, protyle: IProtyle, cb?: () => void) = e.firstElementChild.innerHTML = html; } const left = e.querySelector(".av__scroll")?.scrollLeft || 0; + const headerTransform = (e.querySelector(".av__row--header") as HTMLElement)?.style.transform; + const footerTransform = (e.querySelector(".av__row--footer") as HTMLElement)?.style.transform; let selectCellId = ""; const selectCellElement = e.querySelector(".av__cell--select") as HTMLElement; if (selectCellElement) { @@ -221,7 +222,7 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}`;
-
+
${tableHTML}
@@ -239,7 +240,12 @@ ${cell.color ? `color:${cell.color};` : ""}">${text}
`; if (left) { e.querySelector(".av__scroll").scrollLeft = left; } - avScroll(protyle.contentElement, e); + if (headerTransform) { + (e.querySelector(".av__row--header") as HTMLElement).style.transform = headerTransform; + } + if (footerTransform) { + (e.querySelector(".av__row--footer") as HTMLElement).style.transform = footerTransform; + } if (selectCellId) { const newCellElement = e.querySelector(`.av__row[data-id="${selectCellId.split(Constants.ZWSP)[0]}"] .av__cell[data-col-id="${selectCellId.split(Constants.ZWSP)[1]}"]`); if (newCellElement) { diff --git a/app/src/protyle/render/av/scroll.ts b/app/src/protyle/render/av/scroll.ts deleted file mode 100644 index ca8a6e04b..000000000 --- a/app/src/protyle/render/av/scroll.ts +++ /dev/null @@ -1,20 +0,0 @@ -import {stickyScrollY} from "../../scroll/stickyScroll"; - -export const avScroll = ( - contentElement: HTMLElement, - nodeElement: HTMLElement, -) => { - const bodyElement = nodeElement.querySelector(".av__body") as HTMLElement; - - if (bodyElement) { - const headerElement = bodyElement.querySelector(".av__row--header") as HTMLElement; - const footerElement = bodyElement.querySelector(".av__row--footer") as HTMLElement; - - stickyScrollY( - contentElement, - bodyElement, - headerElement ? [{element: headerElement}] : [], - footerElement ? [{element: footerElement}] : [], - ); - } -} diff --git a/app/src/protyle/scroll/event.ts b/app/src/protyle/scroll/event.ts index 7cd449cd9..fb33c0836 100644 --- a/app/src/protyle/scroll/event.ts +++ b/app/src/protyle/scroll/event.ts @@ -4,7 +4,6 @@ import {fetchPost} from "../../util/fetch"; import {onGet} from "../util/onGet"; import {isMobile} from "../../util/functions"; import {hasClosestBlock, hasClosestByClassName} from "../util/hasClosest"; -import {avScroll} from "../render/av/scroll"; let getIndexTimeout: number; export const scrollEvent = (protyle: IProtyle, element: HTMLElement) => { @@ -22,7 +21,32 @@ export const scrollEvent = (protyle: IProtyle, element: HTMLElement) => { } protyle.wysiwyg.element.querySelectorAll(".av").forEach((item: HTMLElement) => { - avScroll(element, item); + if (item.dataset.render !== "true") { + return; + } + const scrollRect = item.querySelector(".av__scroll").getBoundingClientRect(); + const headerElement = item.querySelector(".av__row--header") as HTMLElement; + if (headerElement) { + const distance = Math.floor(elementRect.top - scrollRect.top); + if (distance > 0 && distance < scrollRect.height) { + headerElement.style.transform = `translateY(${distance}px)`; + } else { + headerElement.style.transform = ""; + } + } + const footerElement = item.querySelector(".av__row--footer") as HTMLElement; + if (footerElement) { + if (footerElement.querySelector(".av__calc--ashow")) { + const distance = Math.floor(elementRect.bottom - footerElement.parentElement.getBoundingClientRect().bottom); + if (distance < 0 && -distance < scrollRect.height) { + footerElement.style.transform = `translateY(${distance}px)`; + } else { + footerElement.style.transform = ""; + } + } else { + footerElement.style.transform = ""; + } + } }); if (!protyle.element.classList.contains("block__edit") && !isMobile()) { diff --git a/app/src/protyle/scroll/stickyScroll.ts b/app/src/protyle/scroll/stickyScroll.ts deleted file mode 100644 index 3acc628b1..000000000 --- a/app/src/protyle/scroll/stickyScroll.ts +++ /dev/null @@ -1,276 +0,0 @@ -export interface IStickyPositionY { - top: number; - bottom: number; -} - -export interface IStickyElementY { - element: HTMLElement; - offset?: number; -} - -export interface IStickyContextY extends IStickyElementY { - rect: DOMRect; - base: number; - origin: IStickyPositionY; - current: IStickyPositionY; - target: IStickyPositionY; - style: IStickyPositionY; -} - -export const stickyScrollY = ( - view: HTMLElement, // 视口元素 - container: HTMLElement, // 容器元素 - topElements: IStickyElementY[] = [], // 顶部粘性元素 - bottomElements: IStickyElementY[] = [], // 底部粘性元素 -) => { - if (topElements.length === 0 && bottomElements.length === 0) { - return; - } - - const viewRect = view.getBoundingClientRect(); - const containerRect = container.getBoundingClientRect(); - - /** - * ┏---------------┓ - * | view | - * ┗---------------┛ → viewRect.bottom - * ┌-----------┐ --→ containerRect.top - * | container | - * └-----------┘ - * ====== OR ====== - * ┌-----------┐ - * | container | - * └-----------┘ --→ containerRect.bottom - * ┏---------------┓ → viewRect.top - * | view | - * ┗---------------┛ - */ - if (viewRect.bottom <= containerRect.top || containerRect.bottom <= viewRect.top) { - return; - } - - const topContext: IStickyContextY[] = topElements.map(item => { - const rect = item.element.getBoundingClientRect(); - const base = Number.parseFloat(item.element.style.top) || 0; - item.offset ??= 0; - - return { - ...item, - rect, - base, - origin: { - top: rect.top - base, - bottom: rect.bottom - base, - }, - current: { - top: rect.top, - bottom: rect.bottom, - }, - target: { - top: null, - bottom: null, - }, - style: { - top: null, - bottom: null, - } - }; - }); - const bottomContext: IStickyContextY[] = bottomElements.map(item => { - const rect = item.element.getBoundingClientRect(); - const base = Number.parseFloat(item.element.style.bottom) || 0; - item.offset ??= 0; - - return { - ...item, - rect, - base, - origin: { - top: rect.top + base, - bottom: rect.bottom + base, - }, - current: { - top: rect.top, - bottom: rect.bottom, - }, - target: { - top: null, - bottom: null, - }, - style: { - top: null, - bottom: null, - } - }; - }); - - let handleTop = false; - let handleBottom = false; - - switch (true) { - /** - * ┏---------------┓ → viewRect.top - * | | - * | view | - * | ┌-----------┐ | → containerRect.top - * ┗-╊-----------╊-┛ → viewRect.bottom - * | container | - * └-----------┘ --→ containerRect.bottom - */ - case viewRect.top <= containerRect.top - && containerRect.top <= viewRect.bottom - && viewRect.top <= viewRect.bottom: - handleBottom = true; - break; - - /** - * ┏---------------┓ → viewRect.top - * | view | - * | ┌-----------┐ | → containerRect.top - * | | container | | - * | └-----------┘ | → containerRect.bottom - * ┗---------------┛ → viewRect.bottom - */ - case viewRect.top <= containerRect.top - && containerRect.bottom <= viewRect.bottom: - break; - - - /** - * ┌-----------┐ --→ containerRect.top - * ┏-╊-----------╊-┓ → viewRect.top - * | | | |}→ view - * ┗-╊-----------╊-┛ → viewRect.bottom - * | container | - * └-----------┘ --→ containerRect.bottom - */ - case containerRect.top <= viewRect.top - && viewRect.bottom <= containerRect.bottom: - handleTop = true; - handleBottom = true; - break; - - /** - * ┌-----------┐ --→ containerRect.top - * | container | - * ┏-╊-----------╊-┓ → viewRect.top - * | └-----------┘ | → containerRect.bottom - * | view | - * ┗-|-----------|-┛ → viewRect.bottom - */ - case containerRect.top <= viewRect.top - && viewRect.top <= containerRect.bottom - && containerRect.bottom <= viewRect.bottom: - handleTop = true; - break; - - default: - break; - } - - if (handleTop) { - if (topContext.length > 0) { - topContext.reduceRight((next, current) => { - switch (true) { - /** - * ┌-----------┐ --→ containerRect.top - * ┏-╊-----------╊-┓ → viewRect.top - * | | ┌┄┄┄┄┄┄┄┐ | | → current.target.top - current.offset - * | | ├╌╌╌╌╌╌╌┤ | | → current.origin.top - * | | └╌╌╌╌╌╌╌┘ | | → current.origin.bottom - * | | ┌╌╌╌╌╌╌╌┐ | | → next.origin.top - * | | └╌╌╌╌╌╌╌┘ | | → next.origin.bottom - * ┗-╊-----------╊-┛ → viewRect.bottom - * └-----------┘ --→ containerRect.bottom - */ - case viewRect.top <= (current.origin.top - current.offset): - current.target.top = current.origin.top; - current.target.bottom = current.origin.bottom; - current.style.top = null; - break; - - /** - * ┌-----------┐ --→ containerRect.top - * | ┌╌╌╌╌╌╌╌┐ | --→ current.origin.top - * | └╌╌╌╌╌╌╌┘ | --→ current.origin.bottom - * ┏-╊-----------╊-┓ → viewRect.top - * | | ┌-------┐ | | → current.target.top - * | | └-------┘ | | → current.target.bottom - * ┗-╊-----------╊-┛ → viewRect.bottom - * | container | - * └-----------┘ --→ containerRect.bottom - */ - default: - current.target.top = viewRect.top + current.offset; - current.target.bottom = current.target.top + current.rect.height; - const nextTop = next - ? Math.min(next.target.top, next.origin.top, containerRect.bottom) - : containerRect.bottom; - if (nextTop < current.target.bottom) { - const diff = nextTop - current.target.bottom; - current.target.top += diff; - current.target.bottom += diff; - } - current.style.top = current.base + (current.target.top - current.current.top); - break; - } - return current; - }, null); - } - } - if (handleBottom) { - if (bottomContext.length > 0) { - bottomContext.reduce((last, current) => { - switch (true) { - /** - * ┌-----------┐ --→ containerRect.top - * ┏-╊-----------╊-┓ → viewRect.top - * | | ┌╌╌╌╌╌╌╌┐ | | → last.origin.top - * | | └╌╌╌╌╌╌╌┘ | | → last.origin.bottom - * | | ┌╌╌╌╌╌╌╌┐ | | → current.origin.top - * | | ├╌╌╌╌╌╌╌┤ | | → current.origin.bottom - * | | └┄┄┄┄┄┄┄┘ | | → current.target.bottom + current.offset - * ┗-╊-----------╊-┛ → viewRect.bottom - * └-----------┘ --→ containerRect.bottom - */ - case (current.origin.bottom + current.offset) <= viewRect.bottom: - current.target.top = current.origin.top; - current.target.bottom = current.origin.bottom; - current.style.bottom = null; - break; - - /** - * ┌-----------┐ --→ containerRect.top - * ┏-╊-----------╊-┓ → viewRect.top - * | | ┌-------┐ | | → current.target.top - * | | └-------┘ | | → current.target.bottom - * ┗-╊-----------╊-┛ → viewRect.bottom - * | ┌╌╌╌╌╌╌╌┐ | --→ current.origin.top - * | └╌╌╌╌╌╌╌┘ | --→ current.origin.bottom - * | container | - * └-----------┘ --→ containerRect.bottom - */ - default: - current.target.bottom = viewRect.bottom - current.offset; - current.target.top = current.target.bottom - current.rect.height; - const lastBottom = last - ? Math.max(last.target.bottom, last.origin.bottom, containerRect.top) - : containerRect.top; - if (current.target.top < lastBottom) { - const diff = lastBottom - current.target.top; - current.target.top += diff; - current.target.bottom += diff; - } - current.style.bottom = current.base - (current.target.bottom - current.current.bottom); - break; - } - return current; - }, null); - } - } - - [...topContext, ...bottomContext].forEach(item => { - item.element.style.top = item.style.top ? `${item.style.top}px` : null; - item.element.style.bottom = item.style.bottom ? `${item.style.bottom}px` : null; - }); -} diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 11fe36fdb..32cfbca26 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -77,7 +77,6 @@ import {activeBlur, hideKeyboardToolbar} from "../../mobile/util/keyboardToolbar import {commonClick} from "./commonClick"; import {avClick, avContextmenu, updateAVName} from "../render/av/action"; import {updateHeader} from "../render/av/row"; -import {avScroll} from "../render/av/scroll"; export class WYSIWYG { public lastHTMLs: { [key: string]: string } = {}; @@ -390,7 +389,6 @@ export class WYSIWYG { scrollElement.querySelectorAll(".av__row, .av__row--footer").forEach(item => { (item.querySelector(`[data-col-id="${dragColId}"]`) as HTMLElement).style.width = newWidth; }); - avScroll(protyle.contentElement, nodeElement); } };