Slidevを使ってユニークなプレゼン資料を作る
概要
Slidevを使うとこんなプレゼン資料も簡単に作れます。
実際に観て触れるリンク先はこちら
なぜ作ったのか
社内LT会を主催するに当たり、僅かな時間で興味のなさそうな視聴者にどれだけインパクトを与えられるかを考え、動きのあるプレゼンテーション資料を作成してみようと思ったのがきっかけです。興味のなさそうな方が対象だったため資料内容自体はかなーりライトになっています。
Slidevとは
MarkdownやHTML、CSS、Vue.js等を使ってプレゼンテーション資料が簡単に作れるツールです。詳しい内容はドキュメントをお読みください。Slidevの日本語のドキュメントはこちら。
日本語翻訳してくださった@ikumatdkr
さんに感謝です。
作り方
この🐪が大量に降ってくるスライドを題材に説明します。
これはCSSアニメーションとVueコンポーネントを利用して作っています。
Slidevのプロジェクト初期化(yarn create slidev
)を行った前提でこちらの作り方を説明していきます。
🐪1つ分のコンポーネント
まず、🐪1つ分のコンポーネントを作成します。
途中に552
というマジックナンバーが出てきますが、こちらはSlidevのスライド部分の高さです。Slidevでは980x552px
でサイズが固定されており、この値だけを考えれば画面上の配置を制御できます。なお、この資料でチーム開発はしないためマジックナンバーのまま使用しています。
<template>
<div :class="$style.text">
<slot></slot>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
speed: number
x: number
y: number
rotate: number
scale: number
}>()
const initRotate = -(Math.round(Math.random() * 360))
const animationDuration = computed(() => `${552 / props.speed}s`)
const pixel = computed(() => ({
x: `${props.x}px`,
y: `${props.y}px`
}))
const transform = computed(() => ({
begin: `translateY(${props.y}px) scaleX(-1) rotate(${initRotate}deg)`,
end: `translateY(${Math.abs(props.y) + 552 + 30}px) scaleX(-1) rotate(${props.rotate}deg)`
}))
</script>
<style module>
.text {
position: absolute;
overflow: none;
top: v-bind("pixel.y");
left: v-bind("pixel.x");
animation: fall linear;
animation-duration: v-bind(animationDuration);
}
@keyframes fall {
0% {
transform: v-bind("transform.begin");
}
100% {
transform: v-bind("transform.end");
}
}
</style>
top: v-bind("pixel.y")
のようにvue3
ではCSS内でJavaScriptの値が使用できてスタイル指定がスッキリしていいですよね。ドキュメントはこちらから確認できます。
ランダムに🐪を生成するコンポーネント
続いて先ほど作った単体のコンポーネントをランダムな位置に大量に生成するコンポーネントを作成します。
props
で指定されたインターバルで🐪を生成します。その際、y
を画面より上側、x
は画面より少し広い範囲でランダムに生成することでランダム性を実現しています。
<template>
<fall
v-for="fallText in fallTexts"
:key="fallText.id"
:x="fallText.x"
:y="fallText.y"
:rotate="fallText.rotate"
:scale="fallText.scale"
:speed="props.speed"
v-bind="$attrs"
>
<slot></slot>
</fall>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
const props = defineProps<{
speed: number
interval: number
max: number
}>()
interface FallText {
id: string
x: number
y: number
rotate: number
scale: number
}
const fallTexts = ref<FallText[]>([])
const generateInterval = () => setInterval(() => {
fallTexts.value.push({
id: new Date().getTime().toString(),
x: Math.round((Math.random() * 980)) - 50,
y: -Math.round((Math.random() * 100)) - 100,
rotate: Math.round((Math.random() * 360)),
scale: -1,
})
}, props.interval)
setInterval(() => {
if (fallTexts.value.length > props.max) {
for (; fallTexts.value.length > props.max;) {
fallTexts.value.shift()
}
}
}, 10)
const interval = ref(generateInterval())
watch(() => props.interval, () => {
clearInterval(interval.value)
interval.value = generateInterval()
})
</script>
markdown
markdownでは先ほど作成したコンポーネントを利用するだけです。なお、Slidevではコンポーネントの明示的なインポートが不要です。
<random-falls
:speed="300"
:max="50"
:interval="400"
class="text-6xl opacity-50"
>🐪</random-falls>
また、先ほど🐪を生成するコンポーネントと記述していたのにコード中に🐪が出てこなかった理由はvueのスロット機能を利用しているためです。落下する対象文字列をpropsとして渡しても実現できますが、拡張性を持たせるためにスロットとして定義してみました。そのため、次のような使い方もできます。
雪を降らせたり
<random-falls
:speed="300"
:max="50"
:interval="400"
class="text-6xl opacity-50"
>❄</random-falls>
img
タグ等を使って最近話題の遊〇王カードを降らすことも可能です。
<random-falls
:speed="300"
:max="50"
:interval="50"
>
<img src="/card.svg">
</random-falls>
以上で簡単に🐪が大量に降ってくるスライドを実現できました。
さいごに
今回はCSSアニメーションを利用しただけですが、vueコンポーネントが利用できるため同様にWEBでできることは大体何でもできます。インタラクティブな操作をしつつプレゼンできるため例えば新しいUIシステムの資料なんかにはかなり有効なのではと思っています。
今回作った資料のソースはこちらです。
その他、過去に作ったまじめな勉強会資料も貼っておきます。
Discussion