ExcalidrawView.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <template>
  2. <div ref="canvasContainer" class="excalidraw-wrapper" @click="showCanvasTop" />
  3. <div ref="canvasFloating" v-if="showFullView" class="floating-canvas" @click="closeFullView" />
  4. </template>
  5. <script setup lang="ts">
  6. import { onMounted, ref, nextTick } from 'vue'
  7. import { withBase } from 'vitepress';
  8. import { exportToCanvas } from '@excalidraw/excalidraw'
  9. import { ExportedDataState } from '@excalidraw/excalidraw/data/types'
  10. interface Props {
  11. filePath: string;
  12. }
  13. const props = defineProps<Props>()
  14. const canvasContainer = ref<HTMLCanvasElement | null>(null)
  15. const canvasFloating = ref<HTMLCanvasElement | null>(null)
  16. const showFullView = ref(false);
  17. let excalidrawJson: ExportedDataState | undefined = undefined
  18. function showCanvasTop() {
  19. showFullView.value = true;
  20. // 等待 DOM 渲染后复制内容
  21. nextTick(async () => {
  22. if (canvasFloating.value && excalidrawJson) {
  23. const canvas = await drawCanvas(excalidrawJson, canvasFloating.value.clientWidth, canvasFloating.value.clientHeight)
  24. if (canvas) {
  25. canvasFloating.value.appendChild(canvas)
  26. }
  27. }
  28. });
  29. }
  30. function closeFullView() {
  31. showFullView.value = false;
  32. }
  33. // 可选:窗口大小变化时自适应
  34. // window.addEventListener("resize", resizeCanvas);
  35. const getExcalidraw = async (path: string): Promise<ExportedDataState | undefined> => {
  36. const res = await fetch(withBase(path)).catch((err) => {
  37. console.log("excalidraw file err: ", err)
  38. })
  39. if (!res) return undefined
  40. const json = await res.json()
  41. return json
  42. }
  43. const drawCanvas = async (data: ExportedDataState, width: number, height: number): Promise<HTMLCanvasElement> => {
  44. // const maxWidthOrHeight = width > height ? height : width
  45. // 改为只限制宽度,因为渲染前父窗口高度可能为0,导致maxWidthOrHeight不生效
  46. const maxWidthOrHeight = width
  47. const canvas = await exportToCanvas({
  48. ...data,
  49. maxWidthOrHeight,// 绘制的最大高度或宽度,设置maxWidthOrHeight后getDimensions则不生效
  50. // getDimensions: (width: number, height: number) => {
  51. // return {
  52. // width,
  53. // height,
  54. // scale: 1
  55. // }
  56. // }, // 参数为原始绘制尺寸,返回尺寸和比例 {width: number; height: number; scale: number}
  57. exportPadding: 10 // 添加边距,默认10
  58. })
  59. return canvas
  60. }
  61. onMounted(async () => {
  62. console.log("excalidrawJson: ", props.filePath)
  63. excalidrawJson = await getExcalidraw(props.filePath)
  64. if (excalidrawJson && canvasContainer.value) {
  65. let excalidrawCanvas = await drawCanvas(excalidrawJson, canvasContainer.value.clientWidth, canvasContainer.value.clientHeight)
  66. if (excalidrawCanvas) {
  67. canvasContainer.value.appendChild(excalidrawCanvas)
  68. }
  69. // 使用 ResizeObserver 监听父容器宽度变化
  70. const resizeObserver = new ResizeObserver(async (entries: ResizeObserverEntry[]) => {
  71. for (const entry of entries) {
  72. const el = entry.target
  73. if (el === canvasContainer.value && excalidrawJson) {
  74. excalidrawCanvas = await drawCanvas(excalidrawJson, el.clientWidth, el.clientHeight)
  75. }
  76. }
  77. });
  78. resizeObserver.observe(canvasContainer.value!);
  79. }
  80. })
  81. </script>
  82. <style scoped>
  83. .excalidraw-wrapper {
  84. width: 100%;
  85. border: 1px solid #ddd;
  86. }
  87. .floating-canvas {
  88. position: fixed;
  89. top: 0;
  90. left: 0;
  91. width: 100vw;
  92. height: 100vh;
  93. background: rgba(0, 0, 0, 0.8);
  94. z-index: 9999;
  95. display: flex;
  96. justify-content: center;
  97. align-items: center;
  98. }
  99. .floating-canvas canvas {
  100. max-width: 90vw;
  101. max-height: 90vh;
  102. box-shadow: 0 0 12px #fff;
  103. }
  104. </style>