<div id="info">

</div>
body {
  margin: 0;
  background-color: #000;
  color: #fff;
  font-family: Monospace;
  font-size: 13px;
  line-height: 24px;
  overscroll-behavior: none;
}

a {
  color: #ff0;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

button {
  cursor: pointer;
  text-transform: uppercase;
}

#info {
  position: absolute;
  top: 0px;
  width: 100%;
  padding: 10px;
  box-sizing: border-box;
  text-align: center;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
  pointer-events: none;
  z-index: 1; /* TODO Solve this in HTML */
}

a,
button,
input,
select {
  pointer-events: auto;
}

.lil-gui {
  z-index: 2 !important; /* TODO Solve this in HTML */
}

@media all and (max-width: 640px) {
  .lil-gui.root {
    right: auto;
    top: auto;
    max-height: 50%;
    max-width: 80%;
    bottom: 0;
    left: 0;
  }
}

#overlay {
  position: absolute;
  font-size: 16px;
  z-index: 2;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  background: rgba(0, 0, 0, 0.7);
}

#overlay button {
  background: transparent;
  border: 0;
  border: 1px solid rgb(255, 255, 255);
  border-radius: 4px;
  color: #ffffff;
  padding: 12px 18px;
  text-transform: uppercase;
  cursor: pointer;
}

#notSupported {
  width: 50%;
  margin: auto;
  background-color: #f00;
  margin-top: 20px;
  padding: 10px;
}
import * as THREE from "https://esm.sh/three";
import { OrbitControls } from "https://esm.sh/three/addons/controls/OrbitControls.js";

let cameraPersp, cameraOrtho, currentCamera;
let scene, renderer, control, orbit;

const targetData = {
  a: [1.78, 6.29, 0],
  b: [-1.78, 6.29, 0],
  tangentA: [0, 0, 1],
  tangentB: [0, 0, 1],
  c: [0, 6.29, -1.78],
  o: [0, 6.29, 0],
  tubeRadius: 0.1,
  torusRadius: 1.78,
  arc: Math.PI
};
const originData = {
  a: [1.78, 0, 0],
  b: [-1.78, 0, 0],
  tangentA: [0, -1, 0],
  tangentB: [0, -1, 0],
  c: [0, 1.78, 0],
  o: [0, 0, 0],
  tubeRadius: 0.1,
  torusRadius: 1.78,
  arc: Math.PI
};
// 计算旋转角度和方向
let originDirection = new THREE.Vector3(...originData.tangentA);
let targetDirection = new THREE.Vector3(...targetData.tangentA);
let quaternion = new THREE.Quaternion();

quaternion.setFromUnitVectors(
  originDirection.normalize(),
  targetDirection.normalize()
);

init();
render();

function init() {
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  const aspect = window.innerWidth / window.innerHeight;

  cameraPersp = new THREE.PerspectiveCamera(50, aspect, 0.01, 30000);
  cameraOrtho = new THREE.OrthographicCamera(
    -600 * aspect,
    600 * aspect,
    600,
    -600,
    0.01,
    30000
  );
  currentCamera = cameraPersp;

  currentCamera.position.set(5, 2.5, 5);

  scene = new THREE.Scene();
  scene.add(new THREE.GridHelper(5, 10, 0x888888, 0x444444));
  const axesHelper = new THREE.AxesHelper(5);
  scene.add(axesHelper);
  const ambientLight = new THREE.AmbientLight(0xffffff);
  scene.add(ambientLight);

  const light = new THREE.DirectionalLight(0xffffff, 4);
  light.position.set(1, 1, 1);
  scene.add(light);

  const geometry = new THREE.TorusGeometry(1.78, 0.1, 120, 64, Math.PI);
  geometry.tag = "user";
  const material = new THREE.MeshPhongMaterial({
    color: "yellow",
    side: THREE.DoubleSide,
    flatShading: true
  });
  orbit = new OrbitControls(currentCamera, renderer.domElement);
  orbit.update();
  orbit.addEventListener("change", render);

  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);
  // 应用旋转
  geometry.applyQuaternion(quaternion);

  // 设置Torus的位置
  mesh.position.set(targetData.o[0], targetData.o[1], targetData.o[2]);
  scene.add(control);

  window.addEventListener("resize", onWindowResize);
}

function onWindowResize() {
  const aspect = window.innerWidth / window.innerHeight;

  cameraPersp.aspect = aspect;
  cameraPersp.updateProjectionMatrix();

  cameraOrtho.left = cameraOrtho.bottom * aspect;
  cameraOrtho.right = cameraOrtho.top * aspect;
  cameraOrtho.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);

  render();
}

function render() {
  renderer.render(scene, currentCamera);
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.