mcpField
S
slidev-components

slidev-components

github.com/yoanbernabeu文档处理41207 次安装39 周前更新
代码仓库

关于 slidev-components

slidev-components 是面向 Slidev 演示文稿作者的技能,介绍内置 Vue 组件及自定义交互组件的创建。内容涉及动画、动态内容、导航状态和可复用组件。

主要功能

  1. 01使用内置 Slidev 组件
  2. 02创建自定义 Vue 组件
  3. 03点击与顺序显示动画
  4. 04访问导航和幻灯片上下文
  5. 05设置全局顶部与底部组件

适用场景

  1. 01为幻灯片添加交互元素
  2. 02创建可复用的演示文稿组件
  3. 03按点击逐步显示内容
  4. 04根据演示模式控制内容呈现

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yoanbernabeu/slidev-skills --skill slidev-components

安装后在 Claude Code 中输入/slidev-components确认已加载。

详细介绍

Slidev 组件

本技能介绍如何在 Slidev 中使用 Vue 组件,包括所有内置组件,以及如何为演示文稿创建自定义交互元素。

适用场景

  • 为幻灯片添加交互元素
  • 使用 Slidev 内置组件
  • 创建自定义 Vue 组件
  • 构建可复用的演示元素
  • 添加动态内容

使用组件

可以直接在 Markdown 中使用组件:

# My Slide

<MyComponent />

<Counter :start="5" />

内置组件

Arrow

在两个点之间绘制箭头。

<Arrow x1="10" y1="20" x2="100" y2="200" />

<Arrow
  x1="10"
  y1="20"
  x2="100"
  y2="200"
  color="#f00"
  width="3"
/>

属性:

  • x1、y1:起点坐标
  • x2、y2:终点坐标
  • color:箭头颜色
  • width:线宽
VDragArrow

可拖动的箭头(适用于演示)。

<VDragArrow />
AutoFitText

自动调整字体大小以适应容器。

<AutoFitText :max="200" :min="50" modelValue="My Text" />

属性:

  • max:最大字体大小
  • min:最小字体大小
  • modelValue:文本内容
LightOrDark

根据主题渲染不同内容。

<LightOrDark>
  <template #light>
    <img src="/logo-dark.png" />
  </template>
  <template #dark>
    <img src="/logo-light.png" />
  </template>
</LightOrDark>
Link

用于跳转到其他幻灯片的导航链接。

<Link to="42">Go to slide 42</Link>

<Link to="/intro">Go to intro</Link>
SlideCurrentNo / SlidesTotal

显示幻灯片页码。

Slide <SlideCurrentNo /> of <SlidesTotal />
Toc(目录)

生成目录。

<Toc />

<Toc maxDepth="2" />

<Toc mode="onlyCurrentTree" />

属性:

  • maxDepth:最大标题层级
  • mode:显示模式(all、onlyCurrentTree、onlySiblings)
Transform

应用 CSS 变换。

<Transform :scale="1.5">
  <div>Scaled content</div>
</Transform>

<Transform :scale="0.8" :rotate="10">
  Rotated and scaled
</Transform>

属性:

  • scale:缩放比例
  • rotate:旋转角度
Tweet

嵌入推文。

<Tweet id="1234567890" />

<Tweet id="1234567890" scale="0.8" />
Youtube

嵌入 YouTube 视频。

<Youtube id="dQw4w9WgXcQ" />

<Youtube id="dQw4w9WgXcQ" width="560" height="315" />

属性:

  • id:YouTube 视频 ID
  • width、height:尺寸
SlidevVideo

嵌入视频文件。

<SlidevVideo v-click autoplay controls>
  <source src="/video.mp4" type="video/mp4" />
</SlidevVideo>

属性:

  • autoplay:进入幻灯片时自动播放
  • controls:显示视频控件
  • loop:循环播放视频
RenderWhen

根据上下文进行条件渲染。

<RenderWhen context="slide">
  Only visible in slide view
</RenderWhen>

<RenderWhen context="presenter">
  Only visible in presenter view
</RenderWhen>

上下文选项:slide、presenter、previewNext、print

VDrag

使元素可拖动。

<VDrag>
  <div class="p-4 bg-blue-500 text-white">
    Drag me!
  </div>
</VDrag>

<VDrag :initialX="100" :initialY="50">
  Positioned draggable
</VDrag>

动画组件

VClick

点击后显示内容。

<v-click>

Revealed on first click

</v-click>

<v-click at="2">

Revealed on second click

</v-click>
VClicks

依次显示子元素。

<v-clicks>

- First item
- Second item
- Third item

</v-clicks>

属性:

  • depth:嵌套列表的深度
  • every:每次点击显示的项目数
VAfter

与前一个元素同时显示。

<v-click>First</v-click>
<v-after>Appears with first</v-after>
VSwitch

根据点击切换内容。

<v-switch>
  <template #1>Step 1 content</template>
  <template #2>Step 2 content</template>
  <template #3>Step 3 content</template>
</v-switch>

创建自定义组件

基础组件

创建 components/Counter.vue:

<script setup>
import { ref } from 'vue'

const props = defineProps({
  start: {
    type: Number,
    default: 0
  }
})

const count = ref(props.start)
</script>

<template>
  <div class="counter">
    <button @click="count--">-</button>
    <span class="count">{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<style scoped>
.counter {
  display: flex;
  align-items: center;
  gap: 1rem;
}
button {
  padding: 0.5rem 1rem;
  font-size: 1.5rem;
  cursor: pointer;
}
.count {
  font-size: 2rem;
  min-width: 3rem;
  text-align: center;
}
</style>

用法:

# Interactive Counter

<Counter :start="10" />
带插槽的组件

<script setup>
defineProps({
  title: String,
  color: {
    type: String,
    default: 'blue'
  }
})
</script>

<template>
  <div :class="`card card-${color}`">
    <h3 v-if="title">{{ title }}</h3>
    <slot />
  </div>
</template>

<style scoped>
.card {
  padding: 1.5rem;
  border-radius: 0.5rem;
  margin: 1rem 0;
}
.card-blue { background: #3b82f6; color: white; }
.card-green { background: #22c55e; color: white; }
.card-red { background: #ef4444; color: white; }
</style>

用法:

<Card title="Important" color="red">
  This is a red card with important content.
</Card>
带 Slidev 上下文的组件

<script setup>
import { computed } from 'vue'
import { useNav } from '@slidev/client'

const { currentSlideNo, total } = useNav()

const progress = computed(() =>
  (currentSlideNo.value / total.value) * 100
)
</script>

<template>
  <div class="progress-bar">
    <div
      class="progress"
      :style="{ width: `${progress}%` }"
    />
  </div>
</template>

<style scoped>
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 4px;
  background: #e5e7eb;
}
.progress {
  height: 100%;
  background: #3b82f6;
  transition: width 0.3s;
}
</style>
代码演示组件

<script setup>
import { ref, computed } from 'vue'

const props = defineProps({
  code: String,
  language: {
    type: String,
    default: 'javascript'
  }
})

const output = ref('')
const error = ref('')

const run = () => {
  try {
    output.value = eval(props.code)
    error.value = ''
  } catch (e) {
    error.value = e.message
    output.value = ''
  }
}
</script>

<template>
  <div class="code-demo">
    <pre><code>{{ code }}</code></pre>
    <button @click="run">Run</button>
    <div v-if="output" class="output">{{ output }}</div>
    <div v-if="error" class="error">{{ error }}</div>
  </div>
</template>

组合式函数

useNav

访问导航状态:

<script setup>
import { useNav } from '@slidev/client'

const {
  currentSlideNo,  // Current slide number
  total,           // Total slides
  next,            // Go to next
  prev,            // Go to previous
  go               // Go to specific slide
} = useNav()
</script>
useSlideContext

访问幻灯片上下文:

<script setup>
import { useSlideContext } from '@slidev/client'

const {
  $slidev,         // Global context
  $clicks,         // Current click count
  $page            // Current page number
} = useSlideContext()
</script>

全局组件

global-top.vue

显示在所有幻灯片上方:


<template>
  <div class="absolute top-4 right-4">
    <img src="/logo.png" class="h-8" />
  </div>
</template>
global-bottom.vue

显示在所有幻灯片下方:


<template>
  <footer class="absolute bottom-4 left-4 text-sm opacity-50">
    © 2025 My Company
  </footer>
</template>

组件模式

进度指示器
<div class="fixed bottom-4 right-4 text-sm">
  <SlideCurrentNo /> / <SlidesTotal />
</div>
社交链接

<template>
  <div class="flex gap-4">
    <a href="https://twitter.com/..." target="_blank">
      <carbon-logo-twitter class="text-2xl" />
    </a>
    <a href="https://github.com/..." target="_blank">
      <carbon-logo-github class="text-2xl" />
    </a>
  </div>
</template>
二维码

<script setup>
import { ref, onMounted } from 'vue'
import QRCodeLib from 'qrcode'

const props = defineProps({
  url: String,
  size: { type: Number, default: 200 }
})

const qrDataUrl = ref('')

onMounted(async () => {
  qrDataUrl.value = await QRCodeLib.toDataURL(props.url, {
    width: props.size
  })
})
</script>

<template>
  <img :src="qrDataUrl" :width="size" :height="size" />
</template>

最佳实践

  1. 保持组件简单:专注于单一职责
  2. 使用属性:让组件可配置
  3. 使用局部样式:避免污染全局样式
  4. 记录用法:添加注释说明如何使用
  5. 测试交互性:确认组件在演示者模式下正常工作

输出格式

创建组件时,请提供:

COMPONENT: [name]
PURPOSE: [what it does]

FILE: components/[Name].vue
---
<script setup>
[script content]
</script>

<template>
[template content]
</template>

<style scoped>
[styles]
</style>
---

USAGE IN SLIDES:
```markdown
<[Name] prop="value" />

PROPS:

  • [propName]: [type] - [description]

评论

支持 Markdown