ZoomableContainer
В'юпорт із перетягуванням і масштабуванням у стилі Figma. Покладіть усередину будь-який елемент і керуйте ним жестами. Залишає навколо вмісту відступ у половину в'юпорту, тож можна прокручувати за його краї.
Жести
| Ввід | Дія |
|---|---|
| Прокрутка / 2 пальці | Панорамування |
| Shift + прокрутка | Горизонтальне панорамування |
| Ctrl/Cmd + прокрутка | Масштаб у точці курсора |
| Щипок на трекпаді | Масштаб у точці курсора |
| Перетягування середньою кнопкою | Панорамування будь-де |
| Space + перетягування | Панорамування будь-де |
| Перетягування по фону | Панорамування |
Живе демо — власні div
Прокручуйте для панорамування, Ctrl/Cmd+прокрутка (або щипок) — масштаб, Space+перетягування чи середня кнопка миші — захоплення.
Зображення
Canvas усередині
Дочірній <canvas> працює як будь-який інший елемент — браузер масштабує його растр. Для піксельно точного рендеру за будь-якого масштабу перемальовуйте canvas під час зміни масштабу.
Використання
Базове
vue
<template>
<orio-zoomable-container style="width: 100%; height: 600px;">
<div style="width: 2000px; height: 1500px;">
<!-- any content -->
</div>
</orio-zoomable-container>
</template>Із зображенням
vue
<template>
<orio-zoomable-container :min-scale="0.5" :max-scale="10">
<img src="/large-photo.jpg" alt="" />
</orio-zoomable-container>
</template>З canvas
vue
<template>
<orio-zoomable-container>
<canvas ref="canvasEl" width="1600" height="1200" />
</orio-zoomable-container>
</template>Програмне керування
vue
<template>
<orio-zoomable-container ref="zoomRef">
<div class="board">...</div>
</orio-zoomable-container>
<orio-button @click="zoomRef?.resetView()">Reset</orio-button>
</template>
<script setup>
import { useTemplateRef } from "vue";
const zoomRef = useTemplateRef("zoomRef");
</script>Реакція на масштаб через проп слоту
Використовуйте scoped-слот, щоб компенсувати масштаб оверлеїв (наприклад, тримати підписи сталого розміру):
vue
<template>
<orio-zoomable-container v-slot="{ scale }">
<div class="board">
<div class="pin" :style="{ transform: `scale(${1 / scale})` }">
Always same size
</div>
</div>
</orio-zoomable-container>
</template>Пропси
| Проп | Тип | За замовчуванням | Опис |
|---|---|---|---|
minScale | number | 0.1 | Нижня межа масштабу |
maxScale | number | 10 | Верхня межа масштабу |
initialScale | number | 1 | Рівень масштабу під час монтування |
zoomSpeed | number | 0.0015 | Множник дельти колеса для масштабу |
wheelPanSpeed | number | 1 | Множник дельти колеса для панорамування |
Події
| Подія | Дані | Опис |
|---|---|---|
update:scale | number | Спрацьовує під час зміни масштабу |
update:translate | (x: number, y) | Спрацьовує під час панорамування |
Слоти
| Слот | Пропси | Опис |
|---|---|---|
default | { scale, tx, ty } | Вміст для трансформації |
Відкриті методи
Доступ через ref:
| Метод | Опис |
|---|---|
setScaleAt(scale, px, py) | Масштабувати з прив'язкою до точки |
panBy(dx, dy) | Зсунути на дельту в пікселях |
centerWorld() | Відцентрувати вміст у в'юпорті |
resetView() | Скинути масштаб і відцентрувати |
scale, tx, ty | Реактивні ref поточного стану |
Примітки
- В'юпорт залишає навколо вмісту з кожного боку порожній простір у половину свого розміру по осі — панорамувати можна, доки край вмісту не дійде до центру в'юпорту.
transform: scale()прискорюється GPU; за нецілих рівнів масштабу текст може виглядати розмитим, але лишається доступним для виділення та скрінрідерів.- Дочірні елементи з
position: fixedвиходять з-під трансформації (обмеження браузера). - Координати кліку у світовому просторі:
worldX = (clientX - rect.left - tx) / scale.



