<!--
 * @Author: your name
 * @Date: 2020-08-28 15:20:15
 * @LastEditTime: 2020-10-10 17:19:06
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /xfhd-vue-scaffold/src/page/index/Home/Index.vue
-->
<template>
	<div class="curve">
		<canvas id="stage" ref="stage"></canvas>
	</div>
</template>
<script>
// @ is an alias to /src
import * as THREE from "three";
import { TransformControls } from "three/examples/jsm/controls/TransformControls.js";
import Stats from "three/examples/jsm/libs/stats.module.js";
import { Flow } from "three/examples/jsm/modifiers/CurveModifier.js";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
import helvetikerRegular from "three/examples/fonts/helvetiker_regular.typeface.json";

export default {
	name: "Curve",
	components: {},
	data() {
		return {
			ACTION_SELECT: 1,
			ACTION_NONE: 0,
			curveHandles: [],
			mouse: new THREE.Vector2(),
			stats: null,
			scene: null,
			camera: null,
			renderer: null,
			rayCaster: null,
			control: null,
			flow: null,
			initialPoints: [
				{ x: 1, y: 0, z: -1 },
				{ x: 1, y: 0, z: 1 },
				{ x: -1, y: 0, z: 1 },
				{ x: -1, y: 0, z: -1 },
			],
			boxGeometry: new THREE.BoxGeometry(0.1, 0.1, 0.1),
			boxMaterial: new THREE.MeshBasicMaterial({
				transparent: true,
				color: 0xffffff,
				opacity: 0,
			}),
		};
	},
	mounted() {
		this.action = this.ACTION_NONE;

		// 初始化场景
		this.initScene();
	},
	methods: {
		// 初始化画布
		async initScene() {
			this.scene = new THREE.Scene();
			this.camera = new THREE.PerspectiveCamera(
				40,
				window.innerWidth / window.innerHeight,
				1,
				1000
			);
			this.camera.position.set(2, 1, 4);
			this.camera.lookAt(this.scene.position);

			// 循环四个方块加入scene
			for (const handlePos of this.initialPoints) {
				const handle = new THREE.Mesh(
					this.boxGeometry,
					this.boxMaterial
				);
				handle.position.copy(handlePos);
				this.curveHandles.push(handle);
				this.scene.add(handle);
			}

			// 根据方块绘制贝塞尔曲线
			this.curve = new THREE.CatmullRomCurve3(
				this.curveHandles.map((handle) => handle.position)
			);
			this.curve.curveType = "centripetal";
			this.curve.closed = true;

			const points = this.curve.getPoints(50);
			this.line = new THREE.LineLoop(
				new THREE.BufferGeometry().setFromPoints(points),
				new THREE.LineBasicMaterial({
					transparent: true,
					opacity: 0,
					color: 0x00ff00,
				})
			);
			this.scene.add(this.line);

			// 环境加入灯光
			this.initLight();

			// 加载字体并创建3d文字
			await this.laodFont();

			// 初始化rennder
			this.renderer = new THREE.WebGLRenderer({
				canvas: this.$refs.stage,
				alpha: true,
				antialias: true,
			});
			this.renderer.setPixelRatio(window.devicePixelRatio);
			this.renderer.setSize(window.innerWidth, window.innerHeight);

			// 监测点击
			this.renderer.domElement.addEventListener(
				"pointerdown",
				this.onPointerDown
			);

			//初始化 检测目标是否被点击
			this.initRayCaster();

			// 初始化监测表盘
			this.stats = new Stats();
			document.body.appendChild(this.stats.dom);

			// 循环动画
			this.animate();

			// 适配视窗
			window.addEventListener("resize", this.onWindowResize);
		},
		// 初始化灯光
		initLight() {
			const light = new THREE.DirectionalLight(0xffaa33);
			light.position.set(-10, 10, 10);
			light.intensity = 1.0;
			this.scene.add(light);

			const light2 = new THREE.AmbientLight(0x003973);
			light2.intensity = 1.0;
			this.scene.add(light2);
		},
		// 加载字体并创建3d文字
		laodFont() {
			return new Promise((reslove, reject) => {
				const loader = new FontLoader();
				loader.load(
					"./static/font/fzcy.json", //helvetiker_regular.typeface
					(font) => {
						const geometry = new TextGeometry("你好，信蜂互动！", {
							font: font,
							size: 0.2,
							height: 0.05,
							curveSegments: 12,
							bevelEnabled: true,
							bevelThickness: 0.02,
							bevelSize: 0.01,
							bevelOffset: 0,
							bevelSegments: 5,
						});

						// geometry.rotateX(Math.PI);
						geometry.rotateZ(-Math.PI);

						const material = new THREE.MeshStandardMaterial({
							color: 0x99ff00,
						});

						const objectToCurve = new THREE.Mesh(
							geometry,
							material
						);

						this.flow = new Flow(objectToCurve);
						this.flow.updateCurve(0, this.curve);
						this.scene.add(this.flow.object3D);

						reslove();
					}
				);
			});
		},
		// 检测目标是否被点击
		initRayCaster() {
			this.rayCaster = new THREE.Raycaster();
			this.control = new TransformControls(
				this.camera,
				this.renderer.domElement
			);
			this.control.addEventListener("dragging-changed", (event) => {
				if (!event.value) {
					const points = this.curve.getPoints(50);
					this.line.geometry.setFromPoints(points);
					this.flow.updateCurve(0, this.curve);
				}
			});
		},
		// 拖拽方块改变curve
		onPointerDown(event) {
			return false;
			this.action = this.ACTION_SELECT;
			this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
			this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
		},
		// 适配视窗
		onWindowResize() {
			this.camera.aspect = window.innerWidth / window.innerHeight;
			this.camera.updateProjectionMatrix();
			this.renderer.setSize(window.innerWidth, window.innerHeight);
		},
		// 循环动画
		animate() {
			requestAnimationFrame(this.animate);

			if (this.action === this.ACTION_SELECT) {
				this.rayCaster.setFromCamera(this.mouse, this.camera);
				this.action = this.ACTION_NONE;
				const intersects = this.rayCaster.intersectObjects(
					this.curveHandles,
					false
				);
				if (intersects.length) {
					const target = intersects[0].object;
					this.control.attach(target);
					this.scene.add(this.control);
				}
			}

			if (this.flow) {
				this.flow.moveAlongCurve(0.001);
			}

			this.render();
		},
		// 绘制
		render() {
			this.renderer.render(this.scene, this.camera);

			this.stats.update();
		},
	},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="less">
.curve {
	.prLayout(100%,100%);
	overflow: hidden;
	color: #fff;
}
</style>