{article.alt}

Nuxt.js、Contentブログでthree.jsを活用

Nuxt.js Contentのマークダウンファイル内に、three.jsで作成した動的コンテンツを入れる方法に関する記事です。

開発環境の構築

node.jsのインストール

Node.jsとnpmのインストール。<<<公式のNode.jsサイト>>>からダウンロードしてインストールします。npmはNode.jsに同梱されています。

Nuxt.jsのプロジェクト作成

Nuxt.jsプロジェクトを作成します。

npx create-nuxt-app earth-demo

ライブラリインストール

Nuxt ContentモジュールとThree.jsをインストールします。

npm install @nuxt/content three

フロントエンドの構築

Componentファイルの作成

componentsディレクトリにEarthComponent.vueファイルを作成します。これは地球の球体が回るデモを表示するコンポーネントです。 'path_to_your_earth_image' に地球の平面地図のディレクトリを指定してください。

<template>
  <div id="earth-container"></div>
</template>

<script>
import * as THREE from 'three'

export default {
  mounted() {
    let container = this.$el;
    let camera, scene, renderer;
    let globe;

    init();
    animate();

    function init() {
      camera = new THREE.PerspectiveCamera(60, container.clientWidth / container.clientHeight, 1, 2000);
      camera.position.z = 500;

      scene = new THREE.Scene();

      let geometry = new THREE.SphereGeometry(200, 20, 20);
      let texture = new THREE.TextureLoader().load('path_to_your_earth_image'); // 地球の画像データのパスを指定
      let material = new THREE.MeshBasicMaterial({ map: texture });

      globe = new THREE.Mesh(geometry, material);
      scene.add(globe);

      renderer = new THREE.WebGLRenderer();
      renderer.setSize(container.clientWidth, container.clientHeight);
      container.appendChild(renderer.domElement);

      window.addEventListener('resize', onWindowResize, false);
    }

    function onWindowResize() {
      camera.aspect = container.clientWidth / container.clientHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(container.clientWidth, container.clientHeight);
    }

    function animate() {
      requestAnimationFrame(animate);
      globe.rotation.y += 0.005;
      renderer.render(scene, camera);
    }
  }
}
</script>

<style scoped>
#earth-container {
  width: 100%;
  height: 100vh;
}
</style>

_slug.vueファイル作成

pagesディレクトリ内に_slug.vueファイルを作成します。これは、個々のマークダウンファイルを表示するための動的ルートを作成します。

<template>
  <article>
    <h1>{{ article.title }}</h1>
    <nuxt-content :document="article" />
  </article>
</template>

<script>
import EarthComponent from '~/components/EarthComponent.vue'

export default {
  components: {
    EarthComponent
  },
  async asyncData({ $content, params }) {
    const article = await $content(params.slug || 'index').fetch()

    return {
      article
    }
  },

.mdファイル作成

contentディレクトリを作成し、その中にマークダウンファイル(たとえば、example.md)を作成します。ここに記事の内容を書きます。 マークダウンファイル内でコンポーネントを指定するとき、ハイフン区切りのケバブケース(kebab-case)を使用してコンポーネントを指定することに注意してください。

---
title: Your Article Title
description: Your Article Description
---
<client-only>
  <earth-component></earth-component>
</client-only>

デモ

使用する画像は1:1サイズで、画像の外枠がないほうが良い(デモでは画像の外枠がついているものをサンプルとして使っています)です。