From 1a17ac4645a7f530dc0de5e85ad084a3ab88e6a0 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Sat, 28 Dec 2019 22:44:05 +0800 Subject: [PATCH] refactor(placeable): calc position logic --- demo/debugComponents/popoverDebug.vue | 156 +++++++++++- .../components/popover/enUS/placement.md | 10 +- demo/loaders/ComponentDemoTemplate.vue | 2 +- demo/loaders/convertMd2Demo.js | 2 +- packages/utils/dom/calcPlacementTransform.js | 238 +++++++++++------- 5 files changed, 296 insertions(+), 112 deletions(-) diff --git a/demo/debugComponents/popoverDebug.vue b/demo/debugComponents/popoverDebug.vue index 3389b20f9..b854ea33f 100644 --- a/demo/debugComponents/popoverDebug.vue +++ b/demo/debugComponents/popoverDebug.vue @@ -1,7 +1,129 @@ - - - + LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLong - - + --> + + LongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLong - - + --> + + + diff --git a/demo/documentation/components/popover/enUS/placement.md b/demo/documentation/components/popover/enUS/placement.md index 129034825..93e9c1bad 100644 --- a/demo/documentation/components/popover/enUS/placement.md +++ b/demo/documentation/components/popover/enUS/placement.md @@ -126,11 +126,11 @@ ```css .popover-grid { - display: grid; - grid-template-columns: auto auto auto auto auto; - grid-gap: 12px; - justify-content: center; - align-items: center; + display: grid; + grid-template-columns: auto auto auto auto auto; + grid-gap: 12px; + justify-content: center; + align-items: center; } .large-text { diff --git a/demo/loaders/ComponentDemoTemplate.vue b/demo/loaders/ComponentDemoTemplate.vue index 565612096..441082161 100644 --- a/demo/loaders/ComponentDemoTemplate.vue +++ b/demo/loaders/ComponentDemoTemplate.vue @@ -22,7 +22,7 @@ diff --git a/demo/loaders/convertMd2Demo.js b/demo/loaders/convertMd2Demo.js index adf19ea8b..38248d0f4 100644 --- a/demo/loaders/convertMd2Demo.js +++ b/demo/loaders/convertMd2Demo.js @@ -77,7 +77,7 @@ function genVueComponent (parts, noRunning = false) { const contentReg = // const codeReg = // const scriptReg = /\/\*\*\sSCRIPT_SLOT\s\*\// - const styleReg = /\/\*\*STYLE_SLOT\*\// + const styleReg = /\/\*\*STYLE_SLOT\*\//g const demoReg = // let src = demoBlock // console.log(src) diff --git a/packages/utils/dom/calcPlacementTransform.js b/packages/utils/dom/calcPlacementTransform.js index 2a229d74b..1c16c1509 100644 --- a/packages/utils/dom/calcPlacementTransform.js +++ b/packages/utils/dom/calcPlacementTransform.js @@ -1,96 +1,144 @@ -export default function calcPlacementTransform (placement, activatorRect, contentRect) { - let contentLeft = null - let contentTop = null - let contentRight = null - let contentBottom = null - let suggesetedTransfromOrigin - if (placement === 'top-start') { - contentTop = activatorRect.top - contentRect.height - contentLeft = activatorRect.left - suggesetedTransfromOrigin = 'bottom left' - } else if (placement === 'top') { - contentTop = activatorRect.top - contentRect.height - contentLeft = activatorRect.left + activatorRect.width / 2 - contentRect.width / 2 - suggesetedTransfromOrigin = 'bottom' - } else if (placement === 'top-end') { - contentTop = activatorRect.top - contentRect.height - contentLeft = activatorRect.left + activatorRect.width - contentRect.width - suggesetedTransfromOrigin = 'bottom right' - } else if (placement === 'left-start') { - contentTop = activatorRect.top - contentLeft = activatorRect.left - contentRect.width - suggesetedTransfromOrigin = 'top right' - } else if (placement === 'left') { - contentTop = activatorRect.top + activatorRect.height / 2 - contentRect.height / 2 - contentLeft = activatorRect.left - contentRect.width - suggesetedTransfromOrigin = 'center right' - } else if (placement === 'left-end') { - contentTop = activatorRect.top + activatorRect.height - contentRect.height - contentLeft = activatorRect.left - contentRect.width - suggesetedTransfromOrigin = 'bottom right' - } else if (placement === 'right-start') { - const toWindowBottom = window.innerHeight - activatorRect.top - contentRect.height - const toWindowRight = window.innerWidth - activatorRect.right - contentRect.width - if (toWindowBottom < 0) { - contentBottom = window.innerHeight - activatorRect.bottom - suggesetedTransfromOrigin = 'bottom' - } else { - contentTop = activatorRect.top - suggesetedTransfromOrigin = 'top' - } - if (toWindowRight < 0) { - contentRight = window.innerWidth - activatorRect.left - suggesetedTransfromOrigin += ' right' - } else { - contentLeft = activatorRect.left + activatorRect.width - suggesetedTransfromOrigin += ' left' - } - } else if (placement === 'right') { - contentTop = activatorRect.top + activatorRect.height / 2 - contentRect.height / 2 - contentLeft = activatorRect.left + activatorRect.width - suggesetedTransfromOrigin = 'center left' - } else if (placement === 'right-end') { - contentTop = activatorRect.top + activatorRect.height - contentRect.height - contentLeft = activatorRect.left + activatorRect.width - suggesetedTransfromOrigin = 'bottom left' - } else if (placement === 'bottom-start') { - const toWindowBottom = window.innerHeight - activatorRect.bottom - const toWindowRight = window.innerWidth - activatorRect.left - contentRect.width - if (contentRect.height > toWindowBottom && activatorRect.top > toWindowBottom) { - contentBottom = toWindowBottom + activatorRect.height - // contentTop = null - suggesetedTransfromOrigin = 'bottom' - } else { - contentTop = activatorRect.top + activatorRect.height - // contentBottom = null - suggesetedTransfromOrigin = 'top' - } - if (toWindowRight < 0) { - contentLeft = activatorRect.right - contentRect.width - suggesetedTransfromOrigin += ' right' - } else { - contentLeft = activatorRect.left - suggesetedTransfromOrigin += ' left' - } - } else if (placement === 'bottom-end') { - contentTop = activatorRect.top + activatorRect.height - contentLeft = activatorRect.left + activatorRect.width - contentRect.width - suggesetedTransfromOrigin = 'top right' - } else { - contentTop = activatorRect.top + activatorRect.height - contentLeft = activatorRect.left + activatorRect.width / 2 - contentRect.width / 2 - suggesetedTransfromOrigin = 'top center' - } - /** - * We could also change the position using transform. - * Such as return `transform: translateX(${contentLeft}px) translateY(${contentTop}px);` - * However, I found that the dom delay is very serious. - * So I decide to use left and top for now. - */ - return [{ - left: contentLeft && `${contentLeft}px`, - top: contentTop && `${contentTop}px`, - right: contentRight && `${contentRight}px`, - bottom: contentBottom && `${contentBottom}px` - }, suggesetedTransfromOrigin] +const oppositeDirection = { + top: 'bottom', + bottom: 'top', + left: 'right', + right: 'left' } + +const adjacentDirections = { + top: ['left', 'right'], + right: ['top', 'bottom'], + bottom: ['left', 'right'], + left: ['top', 'bottom'] +} + +const lengthToCompare = { + top: 'height', + bottom: 'height', + left: 'width', + right: 'width' +} + +function getAdjustedPlacementOfTrackingElement (placement = 'bottom-start', trackedRect, trackingRect) { + const [direction, position] = placement.split('-') + if (trackedRect[direction] >= trackingRect[lengthToCompare[direction]]) { + return placement + } else if (trackedRect[oppositeDirection[direction]] >= trackingRect[lengthToCompare[direction]]) { + if (position) return `${oppositeDirection[direction]}-${position}` + else return oppositeDirection[direction] + } else { + const [direction1, direction2] = adjacentDirections[direction] + let adjacentDirectionWithMoreSpace = direction1 + if (trackedRect[direction1] < trackedRect[direction2]) { + adjacentDirectionWithMoreSpace = direction2 + } + if (trackedRect[adjacentDirectionWithMoreSpace] < trackingRect[lengthToCompare[adjacentDirections]]) { + /** + * If no direction has required space, simply not flip tracking element to any side. + */ + return placement + } + return adjacentDirectionWithMoreSpace + } +} + +const placementToTransformOrigin = { + 'bottom-start': 'top left', + 'bottom': 'top center', + 'bottom-end': 'top right', + 'top-start': 'bottom left', + 'top': 'bottom', + 'top-end': 'bottom right', + 'right-start': 'top left', + 'right': 'center left', + 'right-end': 'bottom left', + 'left-start': 'top right', + 'left': 'center right', + 'left-end': 'bottom right' +} + +function getTransformOriginByPlacement (placement) { + return placementToTransformOrigin[placement] || null +} + +function getPosition (placement, trackedRect, trackingRect) { + const position = { + left: null, + right: null, + top: null, + bottom: null + } + switch (placement) { + case 'bottom-start': + position.top = trackedRect.top + trackedRect.height + position.left = trackedRect.left + break + case 'bottom': + position.top = trackedRect.top + trackedRect.height + position.left = trackedRect.left + trackedRect.width / 2 - trackingRect.width / 2 + break + case 'bottom-end': + position.top = trackedRect.top + trackedRect.height + position.left = trackedRect.left + trackedRect.width - trackingRect.width + break + case 'top-start': + position.top = trackedRect.top - trackingRect.height + position.left = trackedRect.left + break + case 'top': + position.top = trackedRect.top - trackingRect.height + position.left = trackedRect.left + trackedRect.width / 2 - trackingRect.width / 2 + break + case 'top-end': + position.top = trackedRect.top - trackingRect.height + position.left = trackedRect.left + trackedRect.width - trackingRect.width + break + case 'left-start': + position.top = trackedRect.top + position.left = trackedRect.left - trackingRect.width + break + case 'left': + position.top = trackedRect.top + trackedRect.height / 2 - trackingRect.height / 2 + position.left = trackedRect.left - trackingRect.width + break + case 'left-end': + position.top = trackedRect.top + trackedRect.height - trackingRect.height + position.left = trackedRect.left - trackingRect.width + break + case 'right-start': + position.top = trackedRect.top + position.left = trackedRect.left + trackedRect.width + break + case 'right': + position.top = trackedRect.top + trackedRect.height / 2 - trackingRect.height / 2 + position.left = trackedRect.left + trackedRect.width + break + case 'right-end': + position.top = trackedRect.top + trackedRect.height - trackingRect.height + position.left = trackedRect.left + trackedRect.width + break + } + if (position.left !== null) position.left = position.left + 'px' + if (position.right !== null) position.right = position.right + 'px' + if (position.top !== null) position.top = position.top + 'px' + if (position.bottom !== null) position.bottom = position.bottom + 'px' + return position +} + +function calcPlacementTransform (placement, activatorRect, contentRect) { + const trackedRect = { + left: parseInt(activatorRect.left), + top: parseInt(activatorRect.top), + bottom: parseInt(window.innerHeight - activatorRect.bottom), + right: parseInt(window.innerWidth - activatorRect.right), + width: parseInt(activatorRect.width), + height: parseInt(activatorRect.height) + } + const trackingRect = contentRect + const adjustedPlacement = getAdjustedPlacementOfTrackingElement(placement, trackedRect, trackingRect) + const suggesetedTransfromOrigin = getTransformOriginByPlacement(adjustedPlacement) + const position = getPosition(adjustedPlacement, trackedRect, trackingRect) + return [position, suggesetedTransfromOrigin] +} + +export default calcPlacementTransform