iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0

不要單純地以為,從CSS改字型可以妝點<Text />
但照著官方文件教的,在<Text />裡寫上font={fontUrl},卻會遇到Cannot destructure property 'ascender' of 'fontObj' as it is undefined.

我想過幾個原因。一開始以為是<Text />不支援woff2所致,但換成woff結果相同。插曲是我想直接外連github上的ttf時,遇到CORS問題,哈。所以也試過把字型檔傳到codesandbox上,甚至另闢蹊徑,嘗試<Text3D />的useLoader搭配typeface.json等怪招……最後發現是資料格式作祟。
只要一直用陣列,寫成單純map,別麻煩split就沒問題了。

中文內容這邊,借用別具魅力的西幽角色「萬軍破」的詩號。28個字繞一圈蠻剛好的。
私心希望文字是好讀的由左至右,遂將angleStep那邊從2改成-2。

import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { MeshTransmissionMaterial, Text } from "@react-three/drei";

export default function Ring({ text = "", radius, height, segments }) {
  const ref = useRef();
  useFrame(() => {
    ref.current.rotation.x += 0.01;
    ref.current.rotation.y += 0.01;
    ref.current.rotation.z += 0.01;
  });

  const textPositions = [];
  const angleStep = (-2 * Math.PI) / text.length;
  for (let i = 0; i < text.length; i++) {
    const angle = i * angleStep;
    const x = radius * Math.cos(angle);
    const z = radius * Math.sin(angle);
    textPositions.push({ x, z });
  }

  return (
    <group ref={ref}>
      <mesh>
        <cylinderGeometry args={[radius, radius, height, segments]} />
        <MeshTransmissionMaterial
          thickness={3}
          roughness={0.1}
          distortion={0.5}
        />
      </mesh>
      {text.map((char, index) => (
        <Text
          key={index}
          position={[textPositions[index].x, 0, textPositions[index].z]}
          rotation={[0, -angleStep * index + Math.PI / 2, 0]}
          font="https://fonts.cdnfonts.com/s/93375/ZenKakuGothicNew-Light.woff"
          fontSize={0.3}
          lineHeight={1}
          letterSpacing={0.02}
          color="white"
          textAlign="center"
        >
          {char}
        </Text>
      ))}
    </group>
  );
}

最後我決定把中心的圓柱也改為別的量體。
挑來挑去覺得符合「繞一圈條件」的<torusGeometry />蠻順眼,或許是因為甜甜圈外型,喚起我在《媽的多重宇宙》看到the Everything Bagel的震撼了吧。

替換的過程中基本上不會有大狀況,只要新的props有對應到就行。
為了貼合torus不同的半徑,我也把textPositions 乘上了1.5。
再送上一個小叮嚀:要讓站立的torus於虛擬空間中斜倚或平躺,可不是在<torusGeometry />裡附上rotate就能了事,而應當在mesh套用旋轉才是正解。

耶,成品變得像顆繞有星環的天體了。

Edit R3F

//App.js
import { Canvas } from "@react-three/fiber";
import { OrbitControls } from "@react-three/drei";
import Ring from "./ring";
import "./styles.css";

export default function App() {
  return (
    <main>
      <Canvas>
        <OrbitControls />
        <Ring
          radius={2}
          tube={1}
          radialSegments={16}
          tubularSegments={28}
          text={[
            "招",
            "汚",
            "負",
            "蔑",
            "清",
            "名",
            "礙",
            "豈",
            "識",
            "初",
            "心",
            "未",
            "曾",
            "改",
            "明",
            "朝",
            "浴",
            "血",
            "不",
            "枉",
            "義",
            "吾",
            "之",
            "大",
            "道",
            "吾",
            "主",
            "宰",
          ]}
        />
      </Canvas>
    </main>
  );
}
//ring.js
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { MeshTransmissionMaterial, Text } from "@react-three/drei";

export default function Ring({
  text = "",
  radius,
  tube,
  radialSegments,
  tubularSegments,
}) {
  const ref = useRef();
  useFrame(() => {
    ref.current.rotation.x += 0.01;
    ref.current.rotation.y += 0.01;
    ref.current.rotation.z += 0.01;
  });

  const textPositions = [];
  const angleStep = (-2 * Math.PI) / text.length;
  for (let i = 0; i < text.length; i++) {
    const angle = i * angleStep;
    const x = radius * Math.cos(angle);
    const z = radius * Math.sin(angle);
    textPositions.push({ x, z });
  }

  return (
    <group ref={ref}>
      <mesh rotation={[Math.PI / 4, 0, 0]}>
        <torusGeometry args={[radius, tube, radialSegments, tubularSegments]} />
        <MeshTransmissionMaterial
          thickness={1.5}
          roughness={0.1}
          distortion={0.5}
        />
      </mesh>
      {text.map((char, index) => (
        <Text
          key={index}
          position={[
            textPositions[index].x * 1.5,
            0,
            textPositions[index].z * 1.5,
          ]}
          rotation={[0, -angleStep * index + Math.PI / 2, 0]}
          font="https://fonts.cdnfonts.com/s/93375/ZenKakuGothicNew-Light.woff"
          fontSize={0.3}
          lineHeight={1}
          letterSpacing={0.02}
          color="white"
          textAlign="center"
        >
          {char}
        </Text>
      ))}
    </group>
  );
}

上一篇
【Day25】R3F 1
下一篇
【Day27】R3F 3
系列文
【現在學React還來得及嗎?】30天Takeaway分享30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言