返回 slidev
AutoFitText.vue
根目录 / packages / client / builtin / AutoFitText.vue
1 <!--
2 Think of this component as the TextBox that you will see
3 in PowerPoint or Keynote. It will automatically resize
4 the font size based on its content to fit them in,
5 powered by fitty (https://github.com/rikschennink/fitty).
6
7 Usage:
8
9 <AutoFitText modelValue="text"/>
10
11 or
12
13 <AutoFitText :max="100" :min="30" v-model="text"/>
14 -->
15
16 <script setup lang="ts">
17 import type { FittyInstance } from 'fitty'
18 import { useResizeObserver, useVModel } from '@vueuse/core'
19 import fitty from 'fitty'
20 import { onMounted, onUnmounted, ref, watch } from 'vue'
21
22 const props = defineProps({
23 modelValue: {
24 default: '',
25 },
26 max: {
27 default: 100,
28 },
29 min: {
30 default: 30,
31 },
32 multiLine: {
33 type: Boolean,
34 default: true,
35 },
36 })
37
38 const emit = defineEmits(['update:modelValue'])
39
40 const value = useVModel(props, 'modelValue', emit)
41
42 const container = ref<HTMLDivElement>()
43 const inner = ref<HTMLDivElement>()
44
45 let instance: FittyInstance | undefined
46
47 function init() {
48 instance?.unsubscribe()
49 instance = undefined
50 if (!inner.value)
51 return
52 instance = fitty(inner.value, {
53 minSize: props.min,
54 maxSize: props.max,
55 multiLine: props.multiLine,
56 })
57 }
58
59 onMounted(() => {
60 init()
61 // Refit once web fonts are loaded, as glyph metrics may change
62 document.fonts?.ready.then(() => instance?.fit())
63 })
64
65 onUnmounted(() => {
66 instance?.unsubscribe()
67 instance = undefined
68 })
69
70 watch(() => [props.min, props.max, props.multiLine], init)
71
72 // Fitty observes content mutations and window resizes on its own,
73 // but not resizes of the container itself
74 useResizeObserver(container, () => {
75 if (!container.value || container.value.clientWidth <= 0)
76 return
77 // If fitty was initialized while the element was hidden (e.g. on a
78 // preloaded slide), its internal state is invalid and refitting can
79 // never recover, so reinitialize it once the container is visible.
80 // A successful fit always leaves an inline font-size on the element.
81 if (inner.value && !inner.value.style.fontSize)
82 init()
83 else
84 instance?.fit()
85 })
86 </script>
87
88 <template>
89 <div ref="container" class="slidev-auto-fit-text">
90 <div ref="inner" class="slidev-auto-fit-text-inner">
91 <slot>
92 {{ value }}
93 </slot>
94 </div>
95 </div>
96 </template>
97
98 <style scoped>
99 .slidev-auto-fit-text {
100 overflow: auto;
101 }
102
103 .slidev-auto-fit-text-inner {
104 display: inline-block;
105 white-space: nowrap;
106 }
107 </style>
108
108 lines Plain Text