Anchor Example
Use autoupdate to reposition anchored content as its anchor moves.
Code
The following example assumes that you have imported the component and set up the theme.
<div class="viewport"> <div class="anchor-element"> <p class="anchor-element-label">Anchor Element</p> </div>
<sv-anchor> <div class="anchored-element"> <p>Anchored</p> </div> </sv-anchor></div>
<script type="application/x-typescript" module> const anchorEl = document.querySelector<HTMLDivElement>(".anchor-element")!;
const anchor = document.querySelector("sv-anchor")!; anchor.anchorElement = anchorEl; anchor.anchoredElement = document.querySelector(".anchored-element"); anchor.viewportElement = document.querySelector(".viewport"); anchor.preferredPositionOrder = [ ["top", "center", "center"], ["left", "center", "center"], ["right", "center", "center"], ["bottom", "center", "center"], ]; anchor.autoupdate = true;
39 collapsed lines
// Drag implementation
let isDragging = false; let startX = 0; let startY = 0; let currentTranslateX = 0; let currentTranslateY = 0;
const onPointerDown = (event: PointerEvent) => { isDragging = true; startX = event.clientX; startY = event.clientY; anchorEl.setPointerCapture(event.pointerId); };
const onPointerMove = (event: PointerEvent) => { if (!isDragging) return; const dx = event.clientX - startX; const dy = event.clientY - startY; const newTranslateX = currentTranslateX + dx; const newTranslateY = currentTranslateY + dy; anchorEl.style.transform = `translate(${newTranslateX}px, ${newTranslateY}px)`; };
const onPointerUp = (event: PointerEvent) => { isDragging = false;
const transform = anchorEl.style.transform.match(/translate\((.+?)px, (.+?)px\)/); if (transform) { currentTranslateX = parseFloat(transform[1]); currentTranslateY = parseFloat(transform[2]); }
anchorEl.releasePointerCapture(event.pointerId); };
anchorEl.addEventListener("pointerdown", onPointerDown); anchorEl.addEventListener("pointermove", onPointerMove); anchorEl.addEventListener("pointerup", onPointerUp);</script>
<style>113 collapsed lines
.viewport { overflow: hidden; overflow-x: clip; border: solid 1px red; border-radius: 0.4em;
.anchor-element { position: relative; z-index: 50; display: flex; align-items: center; justify-content: center; inline-size: 160px; block-size: 160px; margin: auto; margin-block: 10em; color: white; text-align: center; touch-action: none; cursor: pointer; border: solid 1px #aeae18; border-radius: 1em;
&::after { position: absolute; inset-block-start: 50%; inset-inline: -10% 0; inline-size: 120%; block-size: 1px; content: ""; background-color: #aeae18; }
&::before { position: absolute; inset-block: -10% 0; inset-inline-start: 50%; inline-size: 1px; block-size: 120%; content: ""; background-color: #aeae18; } }
.anchored-element { display: flex; flex-direction: column; align-items: center; justify-content: center; inline-size: 120px; block-size: 120px; color: white; text-align: center; background-color: red; border-radius: 1em;
&::after { content: ""; filter: invert(); }
p { margin: 0; }
sv-anchor:state(--top) & { margin-block-end: 1em; background-color: #ff5733;
&::after { content: "Top"; } }
sv-anchor:state(--bottom) & { margin-block-start: 1em; background-color: #1fba3b;
&::after { content: "Bottom"; } }
sv-anchor:state(--left) & { margin-inline-end: 1em; background-color: #3357ff;
&::after { content: "Left"; } }
sv-anchor:state(--right) & { margin-inline-start: 1em; background-color: #ff33a8;
&::after { content: "Right"; } }
sv-anchor:state(--misaligned) & { display: none; } }
.anchor-element-label { z-index: 2; color: var(--sb-color-text); pointer-events: none; background-color: var(--sb-color-black); } }</style>