| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- <template>
- <div ref="canvasContainer" class="excalidraw-wrapper" @click="showCanvasTop" />
- <div ref="canvasFloating" v-if="showFullView" class="floating-canvas" @mousedown="onMouseDown" @wheel.prevent="onWheel" @click="onCloseFloating" />
- </template>
- <script setup lang="ts">
- import { onMounted, ref, nextTick, onUnmounted } from 'vue'
- import { withBase } from 'vitepress';
- import { exportToCanvas } from '@excalidraw/excalidraw'
- import { ExportedDataState } from '@excalidraw/excalidraw/data/types'
- interface Props {
- filePath: string;
- }
- const props = defineProps<Props>()
- const canvasContainer = ref<HTMLCanvasElement | null>(null)
- const canvasFloating = ref<HTMLCanvasElement | null>(null)
- const showFullView = ref(false);
- let excalidrawJson: ExportedDataState | undefined = undefined
- let topCanvas: HTMLCanvasElement | undefined = undefined
- let scale = 1;
- let origin = { x: 0, y: 0 };
- let lastMouse = { x: 0, y: 0 };
- let dragging = false;
- let hasMoved = false; // 标记是否拖动过
- const dblclickCanvasHandler = () => {
- origin = { x: 0, y: 0 };
- scale = 1;
- updateTransform();
- };
- function showCanvasTop() {
- showFullView.value = true;
- // 等待 DOM 渲染后复制内容
- nextTick(async () => {
- if (canvasFloating.value && excalidrawJson) {
- const canvas = await drawCanvas(
- excalidrawJson,
- canvasFloating.value.clientWidth,
- canvasFloating.value.clientHeight
- )
- if (canvas) {
- topCanvas = canvas
- canvas.style.transform = `translate(0px, 0px) scale(1)`;
- origin = { x: 0, y: 0 };
- scale = 1;
- canvasFloating.value.appendChild(canvas)
- // 双击重置
- canvas.addEventListener('dblclick', dblclickCanvasHandler);
- }
- }
- });
- }
- function closeFullView() {
- showFullView.value = false;
- if (topCanvas) {
- topCanvas.removeEventListener('dblclick', dblclickCanvasHandler);
- }
- if (canvasFloating.value) {
- canvasFloating.value.innerHTML = ''; // 清空 canvas
- }
- }
- function onCloseFloating() {
- if (hasMoved) {
- hasMoved = false;
- return; // 用户拖动过,不关闭
- }
- closeFullView()
- }
- function onMouseDown(e: MouseEvent) {
- // 如果按的是 canvas,就拖动
- if (topCanvas && topCanvas.contains(e.target as Node)) {
- dragging = true;
- hasMoved = false; // 重置状态
- lastMouse = { x: e.clientX, y: e.clientY };
- const onMouseMove = (e: MouseEvent) => {
- if (!dragging) return;
- const dx = e.clientX - lastMouse.x;
- const dy = e.clientY - lastMouse.y;
- // 如果移动距离大于阈值,认为已经拖动过
- if (Math.abs(dx) > 2 || Math.abs(dy) > 2) {
- hasMoved = true;
- }
- origin.x += dx;
- origin.y += dy;
- lastMouse = { x: e.clientX, y: e.clientY };
- updateTransform();
- };
- const onMouseUp = () => {
- dragging = false;
- window.removeEventListener('mousemove', onMouseMove);
- window.removeEventListener('mouseup', onMouseUp);
- };
- // 添加鼠标事件监控
- window.addEventListener('mousemove', onMouseMove);
- window.addEventListener('mouseup', onMouseUp);
- }
- }
- function updateTransform() {
- if (topCanvas) {
- topCanvas.style.transform = `translate(${origin.x}px, ${origin.y}px) scale(${scale})`;
- }
- }
- function onWheel(e: WheelEvent) {
- const delta = -e.deltaY;
- const factor = delta > 0 ? 1.1 : 0.9;
- scale *= factor;
- scale = Math.min(Math.max(0.2, scale), 10); // 限制缩放范围
- updateTransform();
- }
- const getExcalidraw = async (path: string): Promise<ExportedDataState | undefined> => {
- const res = await fetch(withBase(path)).catch((err) => {
- console.log("excalidraw file err: ", err)
- })
- if (!res) return undefined
- const json = await res.json()
- return json
- }
- const drawCanvas = async (data: ExportedDataState, width: number, height: number): Promise<HTMLCanvasElement> => {
- // const maxWidthOrHeight = width > height ? height : width
- // 改为只限制宽度,因为渲染前父窗口高度可能为0,导致maxWidthOrHeight不生效
- const maxWidthOrHeight = width
- const canvas = await exportToCanvas({
- ...data,
- maxWidthOrHeight,// 绘制的最大高度或宽度,设置maxWidthOrHeight后getDimensions则不生效
- // getDimensions: (width: number, height: number) => {
- // return {
- // width,
- // height,
- // scale: 1
- // }
- // }, // 参数为原始绘制尺寸,返回尺寸和比例 {width: number; height: number; scale: number}
- exportPadding: 10 // 添加边距,默认10
- })
- return canvas
- }
- function onKeyDown(e: KeyboardEvent) {
- if (e.key === 'Escape') {
- closeFullView()
- }
- }
- onMounted(async () => {
- console.log("excalidrawJson: ", props.filePath)
- excalidrawJson = await getExcalidraw(props.filePath)
- if (excalidrawJson && canvasContainer.value) {
- let excalidrawCanvas = await drawCanvas(excalidrawJson, canvasContainer.value.clientWidth, canvasContainer.value.clientHeight)
- if (excalidrawCanvas) {
- canvasContainer.value.appendChild(excalidrawCanvas)
- }
- // 使用 ResizeObserver 监听父容器宽度变化
- const resizeObserver = new ResizeObserver(async (entries: ResizeObserverEntry[]) => {
- for (const entry of entries) {
- const el = entry.target
- if (el === canvasContainer.value && excalidrawJson) {
- excalidrawCanvas = await drawCanvas(excalidrawJson, el.clientWidth, el.clientHeight)
- }
- }
- });
- resizeObserver.observe(canvasContainer.value!);
- }
- window.addEventListener('keydown', onKeyDown);
- })
- onUnmounted(() => {
- window.removeEventListener('keydown', onKeyDown);
- })
- </script>
- <style scoped>
- .excalidraw-wrapper {
- width: 100%;
- border: 1px solid #ddd;
- }
- .floating-canvas {
- position: fixed;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100vh;
- background: rgba(0, 0, 0, 0.8);
- z-index: 9999;
- display: flex;
- justify-content: center;
- align-items: center;
- overflow: hidden;
- cursor: grab;
- }
- .floating-canvas:active {
- cursor: grabbing;
- }
- .floating-canvas canvas {
- /* max-width: 90vw; */
- max-width: none;
- /* max-height: 90vh; */
- max-height: none;
- box-shadow: 0 0 12px #fff;
- transition: transform 0.05s;
- }
- </style>
|