add xanadu link
This commit is contained in:
parent
f4f51de53e
commit
55568be0e3
|
|
@ -0,0 +1,272 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Perfect Arrow - Xanadu</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet" />
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
/* font-weight: <weight>; */
|
||||||
|
font-style: normal;
|
||||||
|
font-variation-settings: 'slnt' 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
margin: 0;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
fc-geometry {
|
||||||
|
background-color: #fefefe;
|
||||||
|
width: 400px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
article {
|
||||||
|
height: 100%;
|
||||||
|
padding: 2rem;
|
||||||
|
overflow: scroll;
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
folk-xanadu {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0 0 0 0;
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: rgba(185 233 219 / 75%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<fc-geometry name="1" x="20">
|
||||||
|
<article>
|
||||||
|
<h2>Note 1</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
<span link="2"
|
||||||
|
>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.</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p link="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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</fc-geometry>
|
||||||
|
|
||||||
|
<fc-geometry name="2" x="500">
|
||||||
|
<article>
|
||||||
|
<h2>Note 2</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing
|
||||||
|
<span link="1">
|
||||||
|
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</span
|
||||||
|
>
|
||||||
|
anim id est laborum.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<span link="2"
|
||||||
|
>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
|
||||||
|
dolore magna aliqua.</span
|
||||||
|
>
|
||||||
|
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.
|
||||||
|
</p>
|
||||||
|
</article>
|
||||||
|
</fc-geometry>
|
||||||
|
|
||||||
|
<folk-xanadu source="fc-geometry[name='1'] [link='1']" target="fc-geometry[name='2'] [link='1']"></folk-xanadu>
|
||||||
|
|
||||||
|
<folk-xanadu source="fc-geometry[name='1'] [link='2']" target="fc-geometry[name='2'] [link='2']"></folk-xanadu>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import { FolkXanadu } from '../src/folk-xanadu.ts';
|
||||||
|
import { FolkGeometry } from '../src/canvas/fc-geometry.ts';
|
||||||
|
|
||||||
|
FolkGeometry.register();
|
||||||
|
FolkXanadu.register();
|
||||||
|
|
||||||
|
// class AnimatedXanaduLink extends FolkXanadu {
|
||||||
|
// sourceAnimation = null;
|
||||||
|
// targetAnimation = null;
|
||||||
|
|
||||||
|
// observeSource() {
|
||||||
|
// super.observeSource();
|
||||||
|
|
||||||
|
// this.sourceAnimation = this.animate(
|
||||||
|
// [
|
||||||
|
// { opacity: 0.3, offset: 0 },
|
||||||
|
// { opacity: 1, offset: 0.1 },
|
||||||
|
// { opacity: 1, offset: 0.9 },
|
||||||
|
// { opacity: 0.3, offset: 1 },
|
||||||
|
// ],
|
||||||
|
// {
|
||||||
|
// timeline: new ViewTimeline({
|
||||||
|
// subject: this.sourceElement,
|
||||||
|
// }),
|
||||||
|
// rangeStart: 'entry 0%',
|
||||||
|
// rangeEnd: 'exit 100%',
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// unobserveSource() {
|
||||||
|
// super.unobserveSource();
|
||||||
|
|
||||||
|
// this.sourceAnimation?.cancel();
|
||||||
|
// this.sourceAnimation = null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// observeTarget() {
|
||||||
|
// super.observeTarget();
|
||||||
|
|
||||||
|
// this.targetAnimation = this.animate(
|
||||||
|
// [
|
||||||
|
// { opacity: 0.3, offset: 0 },
|
||||||
|
// { opacity: 1, offset: 0.1 },
|
||||||
|
// { opacity: 1, offset: 0.9 },
|
||||||
|
// { opacity: 0.3, offset: 1 },
|
||||||
|
// ],
|
||||||
|
// {
|
||||||
|
// timeline: new ViewTimeline({
|
||||||
|
// subject: this.targetElement,
|
||||||
|
// }),
|
||||||
|
// rangeStart: 'entry 0%',
|
||||||
|
// rangeEnd: 'exit 100%',
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// unobserveTarget() {
|
||||||
|
// super.unobserveTarget();
|
||||||
|
|
||||||
|
// this.targetAnimation?.cancel();
|
||||||
|
// this.targetAnimation = null;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// AnimatedXanaduLink.register();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -94,18 +94,8 @@ function getPointsOnBezierCurveWithSplitting(
|
||||||
|
|
||||||
const red = lerp(r1, r2, t);
|
const red = lerp(r1, r2, t);
|
||||||
|
|
||||||
getPointsOnBezierCurveWithSplitting(
|
getPointsOnBezierCurveWithSplitting([p1, q1, r1, red], 0, tolerance, outPoints);
|
||||||
[p1, q1, r1, red],
|
getPointsOnBezierCurveWithSplitting([red, r2, q3, p4], 0, tolerance, outPoints);
|
||||||
0,
|
|
||||||
tolerance,
|
|
||||||
outPoints
|
|
||||||
);
|
|
||||||
getPointsOnBezierCurveWithSplitting(
|
|
||||||
[red, r2, q3, p4],
|
|
||||||
0,
|
|
||||||
tolerance,
|
|
||||||
outPoints
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return outPoints;
|
return outPoints;
|
||||||
}
|
}
|
||||||
|
|
@ -152,11 +142,7 @@ export function simplifyPoints(
|
||||||
return outPoints;
|
return outPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pointsOnBezierCurves(
|
export function pointsOnBezierCurves(points: readonly Point[], tolerance: number = 0.15, distance?: number): Point[] {
|
||||||
points: readonly Point[],
|
|
||||||
tolerance: number = 0.15,
|
|
||||||
distance?: number
|
|
||||||
): Point[] {
|
|
||||||
const newPoints: Point[] = [];
|
const newPoints: Point[] = [];
|
||||||
const numSegments = (points.length - 1) / 3;
|
const numSegments = (points.length - 1) / 3;
|
||||||
for (let i = 0; i < numSegments; i++) {
|
for (let i = 0; i < numSegments; i++) {
|
||||||
|
|
@ -189,3 +175,9 @@ export function getSvgPathFromStroke(stroke: number[][]): string {
|
||||||
d.push('Z');
|
d.push('Z');
|
||||||
return d.join(' ');
|
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(', ')})`;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue