🐫

Slidevを使ってユニークなプレゼン資料を作る

2022/02/11に公開

概要

Slidevを使うとこんなプレゼン資料も簡単に作れます。
資料のタイトル
もしゃもしゃ
実際に観て触れるリンク先はこちら
https://slides-what-is-lambda.vercel.app/

なぜ作ったのか

社内LT会を主催するに当たり、僅かな時間で興味のなさそうな視聴者にどれだけインパクトを与えられるかを考え、動きのあるプレゼンテーション資料を作成してみようと思ったのがきっかけです。興味のなさそうな方が対象だったため資料内容自体はかなーりライトになっています。

Slidevとは

MarkdownやHTML、CSS、Vue.js等を使ってプレゼンテーション資料が簡単に作れるツールです。詳しい内容はドキュメントをお読みください。Slidevの日本語のドキュメントはこちら

日本語翻訳してくださった@ikumatdkrさんに感謝です。

作り方

この🐪が大量に降ってくるスライドを題材に説明します。
資料のタイトル

これはCSSアニメーションとVueコンポーネントを利用して作っています。
Slidevのプロジェクト初期化(yarn create slidev)を行った前提でこちらの作り方を説明していきます。

🐪1つ分のコンポーネント

まず、🐪1つ分のコンポーネントを作成します。
途中に552というマジックナンバーが出てきますが、こちらはSlidevのスライド部分の高さです。Slidevでは980x552pxでサイズが固定されており、この値だけを考えれば画面上の配置を制御できます。なお、この資料でチーム開発はしないためマジックナンバーのまま使用しています。

components/Fall.vue
<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は画面より少し広い範囲でランダムに生成することでランダム性を実現しています。

components/RandomFalls.vue
<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システムの資料なんかにはかなり有効なのではと思っています。

今回作った資料のソースはこちらです。
https://github.com/WinterYukky/slides-what-is-lambda

その他、過去に作ったまじめな勉強会資料も貼っておきます。
https://slides-add-devops-for-enterprise.vercel.app/

Discussion