うにょうにょ動かす(HTML/CSS/JS)

前書き

うにょうにょ動かす方法を掲載しています。

目次

  1. 波線(平面)
  2. 波(平面)
  3. 丸(重ねる)
  4. 波線(立体)
  5. 波(立体)
  6. 参考

波線

2つのパスを用意して、svgのanimate要素を使って動かしています。

2つのパス


<svg class="svg" width="720" height="10">
  <path d="M 0 5 Q 10 10 20 5 Q 30  0 40 5" fill="none" stroke="#845080" stroke-width="2" />
</svg>
<svg class="svg" width="720" height="10">
  <path d="M 0 5 Q 10  0 20 5 Q 30 10 40 5" fill="none" stroke="#845080" stroke-width="2" />
</svg>

.svg{ max-width: 100%; }

動かす


<svg class="svg" width="720" height="10">
  <path d="M 0 5 Q 10 10 20 5 Q 30 0 40 5" fill="none" stroke="#845080" stroke-width="2">
    <!-- d属性の値を変化させる -->
    <animate
      attributeName="d"
      dur="2s"
      repeatCount="indefinite"
      values="M 0 5 Q 10 10 20 5 Q 30  0 40 5;
              M 0 5 Q 10  0 20 5 Q 30 10 40 5;
              M 0 5 Q 10 10 20 5 Q 30  0 40 5" />
  </path>
</svg>

繰り返し


<svg class="svg" width="720" height="10">
  <!-- パターンを定義 -->
  <defs>
    <pattern id="ptn_nami11" x="0" y="0" width="40" height="10" patternUnits="userSpaceOnUse">
      <path d="M 0 5 Q 10 10 20 5 Q 30 0 40 5" fill="none" stroke="#845080" stroke-width="2">
        <animate
          attributeName="d"
          dur="2s"
          repeatCount="indefinite"
          values="M 0 5 Q 10 10 20 5 Q 30  0 40 5;
                  M 0 5 Q 10  0 20 5 Q 30 10 40 5;
                  M 0 5 Q 10 10 20 5 Q 30  0 40 5" />
      </path>
    </pattern>
  </defs>
  <!-- パターンで塗りつぶす -->
  <rect x="0" y="0" width="100%" height="100%" fill="url(#ptn_nami11)"></rect>
</svg>

左右に動かす


<svg class="svg" width="720" height="30">
  <defs>
    <!-- パス共通(左右に広げている) -->
    <path d="中略" id="path_nami12" fill="none" stroke-width="2">
      <animate
        attributeName="d"
        dur="2s"
        repeatCount="indefinite"
        values="M -40 5 Q -30 10 -20 5 Q -10  0 0 5 Q 10 10 20 5 Q 30  0 40 5 Q 50 10 60 5 Q 70  0 80 5;
                M -40 5 Q -30  0 -20 5 Q -10 10 0 5 Q 10  0 20 5 Q 30 10 40 5 Q 50  0 60 5 Q 70 10 80 5;
                M -40 5 Q -30 10 -20 5 Q -10  0 0 5 Q 10 10 20 5 Q 30  0 40 5 Q 50 10 60 5 Q 70  0 80 5;" />
    </path>
    <!-- パターン(use属性でパスを使い回す。パターンのx属性の値を変化させる) -->
    <!-- 一つ目(右に移動) -->
    <pattern id="ptn_nami12_a" x="0" y="0" width="40" height="10" patternUnits="userSpaceOnUse">
      <use href="#path_nami12" stroke="#a675a5" />
      <animate
        attributeName="x"
        dur="2s"
        repeatCount="indefinite"
        values="0; 40"/>
    </pattern>
    <!-- 二つ目(左に移動) -->
    <pattern id="ptn_nami12_b" x="0" y="0" width="40" height="10" patternUnits="userSpaceOnUse">
      <use href="#path_nami12" stroke="#845080" />
      <animate
        attributeName="x"
        dur="2s"
        repeatCount="indefinite"
        values="0; -40"/>
    </pattern>
    <!-- 三つ目(行ったり来たり) -->
    <pattern id="ptn_nami12_c" x="0" y="0" width="40" height="10" patternUnits="userSpaceOnUse">
      <use href="#path_nami12" stroke="#a675a5" />
      <animate
        attributeName="x"
        dur="4s"
        repeatCount="indefinite"
        values="0; 40; 0"/>
    </pattern>
  </defs>
  <!-- パターンで塗りつぶす -->
  <rect x="0" y="0"  width="720" height="10" fill="url(#ptn_nami12_a)"></rect>
  <rect x="0" y="10" width="720" height="10" fill="url(#ptn_nami12_b)"></rect>
  <rect x="0" y="20" width="720" height="10" fill="url(#ptn_nami12_c)"></rect>
</svg>

波(平面)

2つのパスを用意して、svgのanimate要素を使って動かしています。

2つのパス


<svg class="svg" width="720" height="30">
  <path d="M 0 10 Q 10 20 20 10 Q 30  0 40 10 V 30 H 0 Z" fill="#845080" />
</svg>
<svg class="svg" width="720" height="30">
  <path d="M 0 10 Q 10  0 20 10 Q 30 20 40 10 V 30 H 0 Z" fill="#845080" />
</svg>

動かす


<svg class="svg" width="720" height="30">
  <!-- d属性の値を変化させる -->
  <path d="M 0 10 Q 10 20 20 10 Q 30 0 40 10 V 30 H 0 Z" fill="#845080" >
    <animate
      attributeName="d"
      dur="2s"
      repeatCount="indefinite"
      values="M 0 10 Q 10 20 20 10 Q 30  0 40 10 V 30 H 0 Z;
              M 0 10 Q 10  0 20 10 Q 30 20 40 10 V 30 H 0 Z;
              M 0 10 Q 10 20 20 10 Q 30  0 40 10 V 30 H 0 Z" />
  </path>
</svg>

繰り返し


<svg class="svg" width="720" height="30">
  <!-- パターンを定義 -->
  <defs>
    <pattern id="ptn_nami21" x="0" y="0" width="40" height="50" patternUnits="userSpaceOnUse">
      <path d="M 0 10 Q 10 20 20 10 Q 30 0 40 10 V 30 H 0 Z" fill="#845080" >
        <animate
          attributeName="d"
          dur="2s"
          repeatCount="indefinite"
          values="M 0 10 Q 10 20 20 10 Q 30  0 40 10 V 30 H 0 Z;
                  M 0 10 Q 10  0 20 10 Q 30 20 40 10 V 30 H 0 Z;
                  M 0 10 Q 10 20 20 10 Q 30  0 40 10 V 30 H 0 Z" />
      </path>
    </pattern>
  </defs>
  <!-- パターンで塗りつぶす -->
  <rect x="0" y="0" width="100%" height="100%" fill="url(#ptn_nami21)"></rect>
</svg>

左右に動かす


<svg class="svg" width="720" height="50">
  <defs>
    <!-- パス共通(左右に広げている。微調整) -->
    <path id="path_nami22" d="中略">
      <animate id="animate_nami22"
        attributeName="d"
        dur="2s"
        repeatCount="indefinite"
        values="M -40 10 Q -30 20 -20 10 Q -10  0 0 10 Q 10 20 20 10 Q 30  0 40 10 Q 50 20 60 10 Q 70 20 80 10 V 29 H -40 Z;
                M -40 10 Q -30  0 -20 10 Q -10 20 0 10 Q 10  0 20 10 Q 30 20 40 10 Q 50  0 60 10 Q 70  0 80 10 V 29 H -40 Z;
                M -40 10 Q -30 20 -20 10 Q -10  0 0 10 Q 10 20 20 10 Q 30  0 40 10 Q 50 20 60 10 Q 70 20 80 10 V 29 H -40 Z;" />
    </path>
    <!-- 奥(行ったり来たり) -->
    <pattern id="ptn_nami22_a" x="0" y="0" width="40" height="30" patternUnits="userSpaceOnUse" >
      <use href="#path_nami22" fill="#e7d4e3" />
      <animate
        attributeName="x"
        dur="4s"
        repeatCount="indefinite"
        values="0; 40; 0"/>
    </pattern>
    <!-- 中(左に移動) -->
    <pattern id="ptn_nami22_b" x="0" y="10" width="40" height="40" patternUnits="userSpaceOnUse">
      <use href="#path_nami22" fill="#a675a5" />
      <animate
        attributeName="x"
        dur="2s"
        repeatCount="indefinite"
        values="0; -40"/>
    </pattern>
    <!-- 手前(右に移動) -->
    <pattern id="ptn_nami22_c" x="0" y="20" width="40" height="50" patternUnits="userSpaceOnUse">
      <use href="#path_nami22" fill="#845080" />
      <animate
        attributeName="x"
        dur="2s"
        repeatCount="indefinite"
        values="0; 40"/>
    </pattern>
  </defs>
  <!-- パターンで塗りつぶす、大きさ調整 -->
  <rect x="0" y="0" width="720" height="30" fill="url(#ptn_nami22_a)"></rect>
  <rect x="0" y="0" width="480" height="40" fill="url(#ptn_nami22_b)" transform="scale(1.5,1)"></rect>
  <rect x="0" y="0" width="360" height="50" fill="url(#ptn_nami22_c)" transform="scale(2,1)"></rect>
</svg>

SVG

3つのパスを用意して、svgのanimate要素でpathを変化させています。また、要素を回転させています。

Generate blobs

<svg class="svg" viewBox="0 0 500 500">
  <!-- グラデーション -->
  <defs>
    <radialGradient id="svg03_gradient">
      <stop offset="0%"   stop-color = "#e7d4e3"></stop>
      <stop offset="100%" stop-color = "#845080"></stop>
    </radialGradient >
  </defs>
  <!-- パス d属性を変化させる。回転させる。 -->
  <path d="中略" fill="url(#svg03_gradient)">
    <animate
      attributeName="d"
      dur="20s"
      repeatCount="indefinite"
      values="中略; 中略; 中略; 中略;" />
    <animateTransform
      attributeName="transform"
      type="rotate"
      dur="12s"
      repeatCount="indefinite"
      from="0,250,250"
      to="360,250,250"/>
  </path>
</svg>

CSS

cssで角丸の値を変化させています。また、要素を回転させています。

FANCY-BORDER-RADIUS

<div class="css-circle"></div>

.css-circle{
  border-radius: 55% 45% 74% 26% / 66% 32% 68% 34%;
  background-image: radial-gradient(#e7d4e3 0%, 845080 100%);
  animation-name: corners, rotate;
  animation-duration: 20s, 12s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes corners {
  0%   { border-radius: 55% 45% 74% 26% / 66% 32% 68% 34%; }
  25%  { border-radius: 33% 67% 44% 56% / 28% 56% 44% 72%; }
  50%  { border-radius: 29% 71% 37% 63% / 55% 40% 60% 45%; }
  75%  { border-radius: 53% 47% 53% 47% / 49% 63% 37% 51%; }
  100% { border-radius: 55% 45% 74% 26% / 66% 32% 68% 34%; }
}
@keyframes rotate {
  0%   { transform: rotate(0deg);}
  100% { transform: rotate(360deg)}
}

丸(重ねる)

SVG

丸を3つ重ねて、各々を回転させています。描画モードは乗算にしています。


<svg class="svg" viewBox="0 0 500 500">
  <!-- パスを定義 d属性を変化させる -->
  <defs>
    <path id="path_circle01" d="中略">
      <animate
        attributeName="d"
        dur="20s"
        repeatCount="indefinite"
        values="中略; 中略; 中略;  中略;" />
    </path>
  </defs>
  <!-- 回転をそれぞれ設定 -->
  <use class="use-circle01" href="#path_circle01">
    <animateTransform
      attributeName="transform"
      type="rotate"
      dur="12s"
      repeatCount="indefinite"
      from="0,250,250"
      to="360,250,250"/>
  </use>
  <use class="use-circle01" href="#path_circle01">
    <animateTransform
      attributeName="transform"
      type="rotate"
      dur="12s"
      repeatCount="indefinite"
      from="60,250,250"
      to="-300,250,250"/>
  </use>
  <use class="use-circle01" href="#path_circle01">
    <animateTransform
      attributeName="transform"
      type="rotate"
      dur="12s"
      repeatCount="indefinite"
      from="-60,250,250"
      to="300,250,250"/>
  </use>
</svg>

.use-circle01{
  fill: #e7d4e3; mix-blend-mode: multiply;
}

CSS

丸を3つ重ねて、各々を回転させています。描画モードは乗算にしています。


<div class="css-circle-wrap">
  <div class="css-circle-b css-circle-b01"></div>
  <div class="css-circle-b css-circle-b02"></div>
  <div class="css-circle-b css-circle-b03"></div>
</div>

.css-circle-wrap{
  position: relative;
}
.css-circle-b{
  border-radius: 55% 45% 74% 26% / 66% 32% 68% 34%;
  background: #e7d4e3; mix-blend-mode: multiply;
  animation-name: corners, rotate;
  animation-duration: 20s, 12s;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  &02{
    position: absolute; top: 0; left: 0; right: 0; margin: auto;
    animation-name: corners, rotate02;
  }
  &03{
    position: absolute; top: 0; left: 0; right: 0; margin: auto;
    animation-name: corners, rotate03;
  }
}

@keyframes corners {
  0%   { border-radius: 55% 45% 74% 26% / 66% 32% 68% 34%; }
  25%  { border-radius: 33% 67% 44% 56% / 28% 56% 44% 72%; }
  50%  { border-radius: 29% 71% 37% 63% / 55% 40% 60% 45%; }
  75%  { border-radius: 53% 47% 53% 47% / 49% 63% 37% 51%; }
  100% { border-radius: 55% 45% 74% 26% / 66% 32% 68% 34%; }
}
@keyframes rotate {
  0%   { transform: rotate(0deg);}
  100% { transform: rotate(360deg);}
}
@keyframes rotate02 {
  0%   { transform: rotate(60deg);}
  100% { transform: rotate(-300deg);}
}
@keyframes rotate03 {
  0%   { transform: rotate(-60deg);}
  100% { transform: rotate(300deg);}
}

波線(立体)

下記ページを参考にthree.jsを使って表示しています。

three.js

Three.jsでラインアニメーション

概要

  • 線を作成する
  • 各頂点において、パーリンノイズを生成して、y座標を変化させる

見本


<div class="wrap js-wrap-c">
  <canvas class="canvas js-canvas03"></canvas>
</div>
<script src="./perlin.js"></script>

.wrap{
  position: relative; padding: 50% 0 0; overflow: hidden;
}
.canvas{
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
}

import * as THREE from './three_r127/build/three.module.js';
import { OrbitControls } from './three_r127/examples/jsm/controls/OrbitControls.js';

// サイズ------------------
const wrap = document.querySelector(".js-wrap-c");
let wrapWidth = wrap.clientWidth;
let wrapHeight = wrap.clientHeight;

// レンダラー------------------
const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector(".js-canvas03"),
  antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(wrapWidth, wrapHeight);

// シーン------------------
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf5edf3 ); //背景色

// カメラ------------------
const camera = new THREE.PerspectiveCamera(45, wrapWidth / wrapHeight);
camera.position.set(-100, 50, 200);
camera.lookAt(new THREE.Vector3(0, 0, 0));

// カメラコントローラーを作成
const controls = new OrbitControls( camera, renderer.domElement );

// 線------------------
const lineNum = 30; //線の数
const lineLength = 500; //線の長さ
const lineSegment = 100; //線の分割数
const lineSpace = 15 //線の間隔
const noiseScale = 5 / 100; //ノイズの大きさ
const amplitude = 100; //振り幅

//グループを作成
const lineGroup = new THREE.Group();
lineGroup.position.x = - lineLength / 2; //中心に寄せる
lineGroup.position.z = - lineNum * lineSpace / 2; //中心に寄せる
scene.add(lineGroup);

for(let i = 0; i < lineNum; i++){

  //頂点
  const points = [];
  for(let j = 0; j <= lineSegment; j++){
    const x = j / lineSegment * lineLength;
    const y = 0;
    const z = i * lineSpace;
    const p = new THREE.Vector3(x,y,z);
    points.push(p);
  }
  const line = new THREE.Line(
    new THREE.BufferGeometry().setFromPoints(points),
    new THREE.LineBasicMaterial()
  );

  // 色
  const h = 305 / 360; //色相
  const s = 100 / 100; //彩度
  const l = Math.round((lineNum - i) / lineNum * 60 + 20 ) / 100; //輝度(20~80)
  line.material.color.setHSL(h, s, l);

  lineGroup.add( line );//グループに追加
}

// 頂点の位置を変える------------------
const positionUpdate = () =>{
  const time = performance.now() * 0.00025;
  for(let i = 0; i < lineNum; i++){
    const line = lineGroup.children[i];
    const positions = line.geometry.attributes.position.array;
    for(let j = 0; j <= lineSegment; j++){
      const pn = noise.perlin2( i * noiseScale + time , j * noiseScale + time); //パーリンノイズ
      positions[j * 3 + 1] = amplitude * pn; //y座標(振り幅 × パーリンノイズ)
    }
    line.geometry.attributes.position.needsUpdate = true;
  }
}

//リサイズ------------------
const wrapResize = () =>{
  wrapWidth = wrap.clientWidth;
  wrapHeight = wrap.clientHeight;
  renderer.setSize(wrapWidth, wrapHeight);
  // camera.aspect = wrapWidth / wrapHeight;
  // camera.updateProjectionMatrix();
}

//一定時間毎に処理------------------
let tick;
const switching = (e) => {
  if( e[0].isIntersecting ){ //見えてる時
    tick = () => {
      wrapResize(); //リサイズ
      positionUpdate(); //頂点の位置を更新
      renderer.render(scene, camera); //レンダリング
      requestAnimationFrame( tick ); //繰り返し
    }
    requestAnimationFrame( tick );
  }else{ //見えてない時
    tick = () => {
      cancelAnimationFrame( tick )
    }
  }
}

//見えているかどうか(Intersection Observer API)------------------
const createObserver = () => {
  let observer;
  const options = { root: null, rootMargin: "0%", threshold: 0 };
  observer = new IntersectionObserver(switching, options); //コールバック関数とオプションを渡す
  observer.observe(wrap); //要素の監視を開始
}
createObserver();

波(立体)

下記ページを参考にthree.jsを使って表示しています。

three.js

three.js の PlaneGeometry で地形を作る

概要

  • 平面を作成する
  • canvasでグラデーションを作成して、平面に貼り付ける
  • 各頂点において、パーリンノイズを生成して、z座標を変化させる

見本


<div class="wrap js-wrap-b">
  <canvas class="canvas js-canvas02"></canvas>
</div>
<script src="./perlin.js"></script>

.wrap{
  position: relative; padding: 50% 0 0; overflow: hidden;
}
.canvas{
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
}

import * as THREE from './three_r127/build/three.module.js';
import { OrbitControls } from './three_r127/examples/jsm/controls/OrbitControls.js';

// サイズ------------------
const wrap = document.querySelector(".js-wrap-b");
let wrapWidth = wrap.clientWidth;
let wrapHeight = wrap.clientHeight;

// レンダラー------------------
const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector(".js-canvas02"),
  antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(wrapWidth, wrapHeight);

// シーン------------------
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf5edf3 ); //背景色

// カメラ------------------
const camera = new THREE.PerspectiveCamera(45, wrapWidth / wrapHeight);
camera.position.set(-100, 150, 500);
camera.lookAt(new THREE.Vector3(0, 0, 0));

// カメラコントローラーを作成
const controls = new OrbitControls( camera, renderer.domElement );
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI / 2;

// 光源------------------
const dLight = new THREE.DirectionalLight(0xffffff, 1); // 平行光源
scene.add(dLight);

// 波 ------------------
//グラデーションの準備
const canvas = document.createElement("canvas");
canvas.width = 100;
canvas.height = 100;
const context = canvas.getContext("2d");
context.beginPath();
const grad = context.createLinearGradient(0,0,0,100);
grad.addColorStop(0,"rgba(231,212,227,1)");
grad.addColorStop(0.7,"rgba(57,7,53,1)");
grad.addColorStop(1,"rgba(57,7,53,1)");

context.fillStyle = grad;
context.rect(0,0,100,100);
context.fill();
const texture = new THREE.CanvasTexture(canvas);

//メッシュ
const ground = new THREE.Mesh(
  new THREE.PlaneGeometry(4000, 4000, 75, 75),
  new THREE.MeshStandardMaterial( { map: texture } ),
);
ground.rotation.x = Math.PI / -2;
scene.add( ground );

// 頂点の位置を変える------------------
const count = ground.geometry.attributes.position.count; //頂点の数
const positions = ground.geometry.attributes.position.array; //頂点の座標
const noiseScale = 10 / 4000; //ノイズの大きさ
const unevenness = 150;  //凸凹の大きさ

const positionUpdate = () =>{
  const time = performance.now() * 0.0005;
  for(let i = 0; i < count; i++){
    const x = positions[i*3]; //x座標
    const y = positions[i*3+1]; //y座標
    const pn = noise.perlin2( x * noiseScale + time , y * noiseScale + time); // パーリンノイズ
    positions[i*3+2] = unevenness * pn; //z座標(凸凹の大きさ×パーリンノイズ)
  }

  ground.geometry.attributes.position.needsUpdate = true;
  ground.geometry.computeVertexNormals();
}

//リサイズ------------------
const wrapResize = () =>{
  wrapWidth = wrap.clientWidth;
  wrapHeight = wrap.clientHeight;
  renderer.setSize(wrapWidth, wrapHeight);
  // camera.aspect = wrapWidth / wrapHeight;
  // camera.updateProjectionMatrix();
}

//一定時間毎に処理------------------
let tick;
const switching = (e) => {
  if( e[0].isIntersecting ){ //見えてる時
    tick = () => {
      wrapResize(); //リサイズ
      positionUpdate(); //頂点の位置を更新
      renderer.render(scene, camera); //レンダリング
      requestAnimationFrame( tick ); //繰り返し
    }
    requestAnimationFrame( tick );
  }else{ //見えてない時
    tick = () => {
      cancelAnimationFrame( tick )
    }
  }
}

//見えているかどうか(Intersection Observer API)------------------
const createObserver = () => {
  let observer;
  const options = { root: null, rootMargin: "0%", threshold: 0 };
  observer = new IntersectionObserver(switching, options); //コールバック関数とオプションを渡す
  observer.observe(wrap); //要素の監視を開始
}
createObserver();

下記ページを参考にthree.jsを使って表示しています。

three.js

球体の頂点アニメーション

概要

  • 球体を作成する
  • 各頂点において、パーリンノイズを生成して、x,y,z座標を変化させる

見本


<div class="wrap js-wrap-a">
  <canvas class="canvas js-canvas"></canvas>
</div>
<script src="./perlin.js"></script>

.wrap{
  position: relative; padding: 50% 0 0; overflow: hidden;
}
.canvas{
  position: absolute; top: 0; left: 0; width: 100%; height: 100%;
}

import * as THREE from './three_r127/build/three.module.js';
import { OrbitControls } from './three_r127/examples/jsm/controls/OrbitControls.js';

// サイズ------------------
const wrap = document.querySelector(".js-wrap-a");
let wrapWidth = wrap.clientWidth;
let wrapHeight = wrap.clientHeight;

// レンダラー------------------
const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector(".js-canvas"),
  antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(wrapWidth, wrapHeight);

// シーン------------------
const scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf5edf3 ); //背景色
scene.fog = new THREE.Fog(0xf5edf3, 500, 2200); //フォグ

// カメラ------------------
const camera = new THREE.PerspectiveCamera(45, wrapWidth / wrapHeight);
camera.position.set(250, 250, 250);

// カメラコントローラーを作成
const controls = new OrbitControls( camera, renderer.domElement );

// 光源------------------
const dLight = new THREE.DirectionalLight(0xffffff, 1); // 平行光源
dLight.position.set(1, 1, 1);
scene.add(dLight);

const hLight = new THREE.HemisphereLight(0xffffff, 0.5); // 半球光源
scene.add(hLight);

// 物体------------------
//地面
const ground = new THREE.LineSegments(
  new THREE.WireframeGeometry(
    new THREE.PlaneGeometry(4000, 4000, 75, 75),
  ),
  new THREE.LineBasicMaterial({color: 0x845080, transparent:true, opacity:0.25}),
);
ground.rotation.x = Math.PI / -2;
scene.add( ground );

//球体
const radius = 100; //球体の半径
const ball = new THREE.Mesh(
  new THREE.SphereGeometry(radius, 60, 40),
  new THREE.MeshPhongMaterial( { color: 0x845080 ,transparent:true, opacity:0.75} ),
);
ball.position.y = 125;
scene.add(ball);
camera.lookAt(ball.position); //カメラの向き
controls.target.set( ball.position.x, ball.position.y, ball.position.z); //カメラコントローラーの向き

// 頂点の位置を変える------------------
const count = ball.geometry.attributes.position.count; //頂点の数
const positions = ball.geometry.attributes.position.array; //頂点の座標
const noiseScale = 1.2; //ノイズの大きさ
const unevenness = radius * 0.1;  //凸凹の大きさ

const positionUpdate = () =>{
  const time = performance.now() * 0.0005;
  for(let i = 0; i < count; i++){
    const p = new THREE.Vector3(
      positions[i*3],
      positions[i*3+1],
      positions[i*3+2]
    );

    // 単位ベクトルに変換
    p.normalize();
    // パーリンノイズ
    const pn = noise.perlin3( p.x * noiseScale + time, p.y * noiseScale + time, p.z * noiseScale + time);
    // 拡縮( 半径 + 凹凸の大きさ * パーリンノイズ )
    p.multiplyScalar( radius + unevenness * pn );

    positions[i*3] = p.x
    positions[i*3+1] = p.y
    positions[i*3+2] = p.z;
  }

  ball.geometry.attributes.position.needsUpdate = true;
  ball.geometry.computeVertexNormals();
}

//リサイズ------------------
const wrapResize = () =>{
  wrapWidth = wrap.clientWidth;
  wrapHeight = wrap.clientHeight;
  renderer.setSize(wrapWidth, wrapHeight);
  // camera.aspect = wrapWidth / wrapHeight;
  // camera.updateProjectionMatrix();
}

//一定時間毎に処理------------------
let tick;
const switching = (e) => {
  if( e[0].isIntersecting ){ //見えてる時
    tick = () => {
      wrapResize(); //リサイズ
      positionUpdate(); //頂点の位置を更新
      renderer.render(scene, camera); //レンダリング
      requestAnimationFrame( tick ); //繰り返し
    }
    requestAnimationFrame( tick );
  }else{ //見えてない時
    tick = () => {
      cancelAnimationFrame( tick )
    }
  }
}

//見えているかどうか(Intersection Observer API)------------------
const createObserver = () => {
  let observer;
  const options = { root: null, rootMargin: "0%", threshold: 0 };
  observer = new IntersectionObserver(switching, options); //コールバック関数とオプションを渡す
  observer.observe(wrap); //要素の監視を開始
}
createObserver();

参考

HTML SVG の基本的な使い方

SVG アニメーション(SMIL を使ったアニメーション)

Selected Demos 2019-2020

Generate blobs

FANCY-BORDER-RADIUS

CSSアニメーションで実現!コピペで使えるマイクロインタラクション

球体の頂点アニメーション

Three.jsでラインアニメーション

three.js の PlaneGeometry で地形を作る

[threejs] CanvasTextureでグラデーションテクスチャを貼る(1)

関連記事

簡単な地図を自作する(HTML/CSS/JS)

パーティクルを作成する(HTML/JS)

文字を立体で表示する(HTML/CSS/JS)

360度画像を表示する(HTML/JS)

円を描くように要素を動かす(HTML/CSS/JS)

マウスの位置に合わせて要素を動かす(HTML/CSS/JS)

先頭に戻る
ページの先頭に戻る