UNPKG

3.63 kBJavaScriptView Raw
1import { isSafari, isFirefox } from './BrowserDetector';
2import MonotonicInterpolant from './MonotonicInterpolant';
3const ELEMENT_NODE = 1;
4export function getNodeClientOffset(node) {
5 const el = node.nodeType === ELEMENT_NODE ? node : node.parentElement;
6 if (!el) {
7 return null;
8 }
9 const { top, left } = el.getBoundingClientRect();
10 return { x: left, y: top };
11}
12export function getEventClientOffset(e) {
13 return {
14 x: e.clientX,
15 y: e.clientY,
16 };
17}
18function isImageNode(node) {
19 return (node.nodeName === 'IMG' &&
20 (isFirefox() || !document.documentElement.contains(node)));
21}
22function getDragPreviewSize(isImage, dragPreview, sourceWidth, sourceHeight) {
23 let dragPreviewWidth = isImage ? dragPreview.width : sourceWidth;
24 let dragPreviewHeight = isImage ? dragPreview.height : sourceHeight;
25 // Work around @2x coordinate discrepancies in browsers
26 if (isSafari() && isImage) {
27 dragPreviewHeight /= window.devicePixelRatio;
28 dragPreviewWidth /= window.devicePixelRatio;
29 }
30 return { dragPreviewWidth, dragPreviewHeight };
31}
32export function getDragPreviewOffset(sourceNode, dragPreview, clientOffset, anchorPoint, offsetPoint) {
33 // The browsers will use the image intrinsic size under different conditions.
34 // Firefox only cares if it's an image, but WebKit also wants it to be detached.
35 const isImage = isImageNode(dragPreview);
36 const dragPreviewNode = isImage ? sourceNode : dragPreview;
37 const dragPreviewNodeOffsetFromClient = getNodeClientOffset(dragPreviewNode);
38 const offsetFromDragPreview = {
39 x: clientOffset.x - dragPreviewNodeOffsetFromClient.x,
40 y: clientOffset.y - dragPreviewNodeOffsetFromClient.y,
41 };
42 const { offsetWidth: sourceWidth, offsetHeight: sourceHeight } = sourceNode;
43 const { anchorX, anchorY } = anchorPoint;
44 const { dragPreviewWidth, dragPreviewHeight } = getDragPreviewSize(isImage, dragPreview, sourceWidth, sourceHeight);
45 const calculateYOffset = () => {
46 const interpolantY = new MonotonicInterpolant([0, 0.5, 1], [
47 // Dock to the top
48 offsetFromDragPreview.y,
49 // Align at the center
50 (offsetFromDragPreview.y / sourceHeight) * dragPreviewHeight,
51 // Dock to the bottom
52 offsetFromDragPreview.y + dragPreviewHeight - sourceHeight,
53 ]);
54 let y = interpolantY.interpolate(anchorY);
55 // Work around Safari 8 positioning bug
56 if (isSafari() && isImage) {
57 // We'll have to wait for @3x to see if this is entirely correct
58 y += (window.devicePixelRatio - 1) * dragPreviewHeight;
59 }
60 return y;
61 };
62 const calculateXOffset = () => {
63 // Interpolate coordinates depending on anchor point
64 // If you know a simpler way to do this, let me know
65 const interpolantX = new MonotonicInterpolant([0, 0.5, 1], [
66 // Dock to the left
67 offsetFromDragPreview.x,
68 // Align at the center
69 (offsetFromDragPreview.x / sourceWidth) * dragPreviewWidth,
70 // Dock to the right
71 offsetFromDragPreview.x + dragPreviewWidth - sourceWidth,
72 ]);
73 return interpolantX.interpolate(anchorX);
74 };
75 // Force offsets if specified in the options.
76 const { offsetX, offsetY } = offsetPoint;
77 const isManualOffsetX = offsetX === 0 || offsetX;
78 const isManualOffsetY = offsetY === 0 || offsetY;
79 return {
80 x: isManualOffsetX ? offsetX : calculateXOffset(),
81 y: isManualOffsetY ? offsetY : calculateYOffset(),
82 };
83}