返回 slidev
Demo.vue
1 <script setup lang="ts">
2 import type { SlidevMarkdown } from '@slidev/types'
3 import Center from '@slidev/client/layouts/center.vue'
4 import Default from '@slidev/client/layouts/default.vue'
5 import { parseSync } from '@slidev/parser'
6 import Cover from '@slidev/theme-default/layouts/cover.vue'
7 import Markdown from 'markdown-it'
8 import TypeIt from 'typeit'
9 import { onMounted, ref, watch } from 'vue'
10 import DemoEditor from './DemoEditor.vue'
11 import DemoSlide from './DemoSlide.vue'
12 import SlideContainer from './SlideContainer.vue'
13
14 import '@slidev/client/styles/layouts-base.css'
15 import '@slidev/theme-default/styles/layouts.css'
16
17 const page = ref(0)
18 const paused = ref(false)
19 const completed = ref(false)
20 const code = ref('')
21 const info = ref<SlidevMarkdown>()
22 const block = ref<HTMLPreElement>()
23
24 const markdown = new Markdown()
25
26 function getLayout(id: number) {
27 const name = info.value?.slides?.[id]?.frontmatter?.layout || 'default'
28 return name === 'cover'
29 ? Cover
30 : name === 'center'
31 ? Center
32 : Default
33 }
34
35 watch([code, paused], () => {
36 if (paused.value)
37 return
38 try {
39 info.value = parseSync(code.value, '')
40 }
41 catch {
42 }
43 })
44
45 function getAttrs(id: number) {
46 return info.value?.slides?.[id]?.frontmatter
47 }
48
49 function getHTML(id: number) {
50 const content = info?.value?.slides?.[id]?.content
51 if (!content)
52 return ''
53 return markdown.render(content)
54 }
55
56 function pause() {
57 paused.value = true
58 }
59 function resume() {
60 paused.value = false
61 }
62
63 const COVER_URL = 'https://sli.dev/demo-cover.png'
64 if (typeof window !== 'undefined') {
65 const img1 = new Image()
66 img1.src = COVER_URL
67 }
68
69 function play() {
70 code.value = ''
71 block.value!.innerHTML = ''
72 completed.value = false
73 // @ts-expect-error wrong types provided by TypeIt
74 new TypeIt(block.value!, {
75 speed: 50,
76 startDelay: 900,
77 afterStep: () => {
78 code.value = JSON.parse(JSON.stringify(block.value!.innerText.replace('|', '')))
79 },
80 afterComplete: () => {
81 setTimeout(() => completed.value = true, 300)
82 },
83 })
84 .type('<br><span class="token title"># Welcome to Slidev!</span><br><br>', { delay: 400 })
85 .type('Presentation Slides for Developers', { delay: 400 })
86 .move(null, { to: 'START', speed: 0 })
87 .type('<br>')
88 .move(null, { to: 'START' })
89 .exec(pause)
90 .type('<span class="token punctuation">---<br><br>---</span>')
91 .move(-4)
92 .type('<span class="token tag">layout:</span> center')
93 .exec(resume)
94 .pause(1000)
95 .exec(pause)
96 .delete(6, { delay: 100, speed: 50 })
97 .type('cover')
98 .exec(resume)
99 .exec(pause)
100 .pause(1000)
101 .type('<br>')
102 .type('<span class="token tag">background:</span> ', { delay: 200 })
103 .type(COVER_URL, { speed: 0 })
104 .exec(resume)
105 .pause(1000)
106 .move(null, { to: 'END', speed: 0 })
107 .exec(pause)
108 .type('<br><br><span class="token punctuation">---</span><br><br>', { delay: 400 })
109 .exec(resume)
110 .exec(() => setTimeout(() => page.value = 1))
111 .type('<span class="token title"># Page 2</span><br><br>', { delay: 400 })
112 .type('- 📄 Write slides in a single Markdown file<br>', { delay: 800 })
113 .type('- 🌈 Themes, code blocks, interactive components<br>', { delay: 800 })
114 .type('- 😎 Read the docs to learn more!', { delay: 800 })
115 .exec(() => setTimeout(() => page.value = 0))
116 .go()
117 }
118
119 onMounted(play)
120 </script>
121
122 <template>
123 <div>
124 <DemoEditor>
125 <div class="text-sm opacity-50 text-center">
126 ./slides.md
127 </div>
128
129 <div v-if="completed" class="absolute text-xs right-1 top-1 icon-btn opacity-50" title="Replay" @click="play()">
130 <div class="i-carbon:reset" />
131 </div>
132
133 <div class="language-md !bg-transparent px4 py1">
134 <pre ref="block" class="text-left whitespace-pre-wrap font-mono bg-transparent" />
135 </div>
136 </DemoEditor>
137
138 <DemoSlide class="text-left">
139 <div
140 class="flex h-full dark:bg-[#181819] transition-transform transform duration-500"
141 style="width: 200%"
142 :class="page === 1 ? '-translate-x-1/2' : ''"
143 >
144 <SlideContainer class="w-full h-full">
145 <component :is="getLayout(0)" v-bind="getAttrs(0)">
146 <div v-html="getHTML(0)" />
147 </component>
148 </SlideContainer>
149 <SlideContainer class="w-full h-full">
150 <component :is="getLayout(1)" v-bind="getAttrs(1)">
151 <div v-html="getHTML(1)" />
152 </component>
153 </SlideContainer>
154 </div>
155 <div
156 class="absolute left-2 bottom-1 flex text-gray-200"
157 opacity="0 hover:100"
158 >
159 <div class="icon-btn" :class="{ disabled: page === 0 }" @click="page = 0">
160 <div class="i-carbon:chevron-left" />
161 </div>
162 <div class="icon-btn" :class="{ disabled: page === 1 }" @click="page = 1">
163 <div class="i-carbon:chevron-right" />
164 </div>
165 </div>
166 </DemoSlide>
167 </div>
168 </template>
169
170 <style>
171 .slidev-layout ul {
172 padding: 0;
173 }
174 .slidev-layout li {
175 line-height: 2.4em;
176 }
177 </style>
178
178 lines Plain Text