From 55568be0e3acda0a80962caed14962085cb93c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Cchrisshank=E2=80=9D?= Date: Tue, 26 Nov 2024 12:27:30 -0800 Subject: [PATCH] add xanadu link --- demo/xanadu.html | 272 ++++++++++++++++++++++++++++++++++++++++++++ src/arrows/utils.ts | 26 ++--- src/folk-xanadu.ts | 101 ++++++++++++++++ 3 files changed, 382 insertions(+), 17 deletions(-) create mode 100644 demo/xanadu.html create mode 100644 src/folk-xanadu.ts diff --git a/demo/xanadu.html b/demo/xanadu.html new file mode 100644 index 0000000..ff01e4b --- /dev/null +++ b/demo/xanadu.html @@ -0,0 +1,272 @@ + + + + + + Perfect Arrow - Xanadu + + + + + + + +
+

Note 1

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+
+
+ + +
+

Note 2

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore + magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo + consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla + pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id + est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing + + elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis + nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat + cupidatat non proident, sunt in culpa qui officia deserunt mollit + anim id est laborum. +

+ +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. + Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

+
+
+ + + + + + + + diff --git a/src/arrows/utils.ts b/src/arrows/utils.ts index dd17e8c..1ebeea4 100644 --- a/src/arrows/utils.ts +++ b/src/arrows/utils.ts @@ -94,18 +94,8 @@ function getPointsOnBezierCurveWithSplitting( const red = lerp(r1, r2, t); - getPointsOnBezierCurveWithSplitting( - [p1, q1, r1, red], - 0, - tolerance, - outPoints - ); - getPointsOnBezierCurveWithSplitting( - [red, r2, q3, p4], - 0, - tolerance, - outPoints - ); + getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints); + getPointsOnBezierCurveWithSplitting([red, r2, q3, p4], 0, tolerance, outPoints); } return outPoints; } @@ -152,11 +142,7 @@ export function simplifyPoints( return outPoints; } -export function pointsOnBezierCurves( - points: readonly Point[], - tolerance: number = 0.15, - distance?: number -): Point[] { +export function pointsOnBezierCurves(points: readonly Point[], tolerance: number = 0.15, distance?: number): Point[] { const newPoints: Point[] = []; const numSegments = (points.length - 1) / 3; for (let i = 0; i < numSegments; i++) { @@ -189,3 +175,9 @@ export function getSvgPathFromStroke(stroke: number[][]): string { d.push('Z'); return d.join(' '); } + +export function verticesToPolygon(vertices: Vertex[]): string { + if (vertices.length === 0) return ''; + + return `polygon(${vertices.map((vertex) => `${vertex.x}px ${vertex.y}px`).join(', ')})`; +} diff --git a/src/folk-xanadu.ts b/src/folk-xanadu.ts new file mode 100644 index 0000000..f91bd7b --- /dev/null +++ b/src/folk-xanadu.ts @@ -0,0 +1,101 @@ +import { AbstractArrow } from './arrows/abstract-arrow.js'; +import { Vertex, verticesToPolygon } from './arrows/utils.js'; + +export class FolkXanadu extends AbstractArrow { + static tagName = 'folk-xanadu'; + + render(sourceRect: DOMRectReadOnly, targetRect: DOMRectReadOnly): void { + if (this.sourceElement === null || this.targetElement === null) { + this.style.clipPath = ''; + return; + } + + // If the right side of the target is to the left of the right side of the source then swap them + if (sourceRect.x + sourceRect.width > targetRect.x + targetRect.width) { + const temp = sourceRect; + sourceRect = targetRect; + targetRect = temp; + } + + let sourceVertices = computeInlineVertices(Array.from(this.sourceElement.getClientRects())); + const targetVertices = computeInlineVertices(Array.from(this.targetElement.getClientRects())); + + if (sourceVertices.length === 0 || targetVertices.length === 0) { + this.style.clipPath = ''; + return; + } + + // To trace the link we need to rotate the vertices of the source to start on the bottom right corner. + const maxRightCoordinate = Math.max.apply( + null, + sourceVertices.map((vertex) => vertex.x) + ); + const maxBottomCoordinate = Math.max.apply( + null, + sourceVertices.filter((vertex) => vertex.x === maxRightCoordinate).map((vertex) => vertex.y) + ); + + const index = sourceVertices.findIndex( + (vertex) => vertex.x === maxRightCoordinate && vertex.y === maxBottomCoordinate + ); + + sourceVertices = sourceVertices.slice(index).concat(sourceVertices.slice(0, index)); + + this.style.clipPath = verticesToPolygon(sourceVertices.concat(targetVertices)); + } +} + +// The order that vertices are returned is significant +function computeInlineVertices(rects: DOMRect[]): Vertex[] { + rects = rects.map((rect) => + DOMRectReadOnly.fromRect({ + height: Math.round(rect.height), + width: Math.round(rect.width), + x: Math.round(rect.x), + y: Math.round(rect.y), + }) + ); + + if (rects.length === 0) return []; + else if (rects.length === 1) { + const rect = rects[0]; + return [ + { x: rect.left, y: rect.top }, + { x: rect.right, y: rect.top }, + { x: rect.right, y: rect.bottom }, + { x: rect.left, y: rect.bottom }, + ]; + } + + const vertices: Vertex[] = []; + + if (rects[1].left < rects[0].left) { + vertices.push({ x: rects[1].left, y: rects[1].top }, { x: rects[0].left, y: rects[0].bottom }); + } + + vertices.push({ x: rects[0].left, y: rects[0].top }, { x: rects[0].right, y: rects[0].top }); + + const maxRightCoordinate = Math.max.apply( + null, + rects.map((rect) => rect.right) + ); + const maxBottomCoordinate = Math.max.apply( + null, + rects.filter((rect) => rect.right === maxRightCoordinate).map((rect) => rect.bottom) + ); + + vertices.push({ + x: maxRightCoordinate, + y: maxBottomCoordinate, + }); + + const lastRect = rects.at(-1)!; + + if (lastRect.bottom > maxBottomCoordinate) { + vertices.push({ x: lastRect.right, y: lastRect.top }, { x: lastRect.right, y: lastRect.bottom }); + } + + vertices.push({ x: lastRect.left, y: lastRect.bottom }); + + return vertices; +}