芝麻web文件管理V1.00
编辑当前文件:/home/qrafawbu/thefirstcookout.com/canvas/src/js/MenuDrop.js
import * as THREE from 'three' import C from 'cannon' import { pick } from './utils' // Options const force = 25 export default class Menu { constructor(scene, world, camera) { this.$navItems = document.querySelectorAll('.mainNav a') this.scene = scene this.world = world this.camera = camera this.loader = new THREE.FontLoader() this.clock = new THREE.Clock() // Setups this.totalMass = 1 this.cMaterial = new C.Material() this.worldMat = new C.Material() this.mouse = new THREE.Vector2() this.raycaster = new THREE.Raycaster() // Loader this.loader.load(fontURL, (f) => { this.setup(f) }) this.bindEvents() } bindEvents() { document.addEventListener('click', () => { this.onClick() }) window.addEventListener('mousemove', (e) => { this.onMouseMove(e) }) } setup(font) { this.words = [] this.margin = 6 this.offset = this.$navItems.length * this.margin * 0.5 const options = { font, size: 3, height: 0.4, curveSegments: 24, bevelEnabled: true, bevelThickness: 0.9, bevelSize: 0.3, bevelOffset: 0, bevelSegments: 10, } Array.from(this.$navItems).reverse().forEach(($item, i) => { const { innerText } = $item const words = new THREE.Group() words.len = 0 words.ground = new C.Body({ mass: 0, shape: new C.Box(new C.Vec3(50, 50, 0.1)), quaternion: new C.Quaternion().setFromEuler(Math.PI / -2, 0, 0), position: new C.Vec3(0, i * this.margin - this.offset, 0), material: this.worldMat, }) words.isGroundDisplayed = false const randomColor = pick(colors) Array.from(innerText).forEach((letter, j) => { const progress = (j) / (innerText.length - 1) // Three.js const material = new THREE.MeshPhongMaterial({ color: randomColor.from.clone().lerp(randomColor.to, progress) }) const geometry = new THREE.TextBufferGeometry(letter, options) geometry.computeBoundingBox() geometry.computeBoundingSphere() const mesh = new THREE.Mesh(geometry, material) // Get size mesh.size = mesh.geometry.boundingBox.getSize(new THREE.Vector3()) mesh.size.multiply(new THREE.Vector3(0.5, 0.5, 0.5)) // Cannon.js mesh.initPosition = new C.Vec3(words.len * 2, (this.$navItems.length - 1 - i) * this.margin - this.offset, 0) mesh.initPositionOffset = new C.Vec3(mesh.initPosition.x, mesh.initPosition.y + (i + 1) * 30 + 30 + j * 0.01, mesh.initPosition.z) words.len += mesh.size.x const box = new C.Box(new C.Vec3(mesh.size.x, mesh.size.y, mesh.size.z)) mesh.body = new C.Body({ mass: this.totalMass / innerText.length, position: mesh.initPositionOffset, material: this.cMaterial, // linearDamping: 0.1, angularDamping: 0.99, }) mesh.body.addShape(box, new C.Vec3(mesh.geometry.boundingSphere.center.x, mesh.geometry.boundingSphere.center.y, mesh.geometry.boundingSphere.center.z)) this.world.addBody(mesh.body) words.add(mesh) }) words.children.forEach((letter) => { letter.body.position.x -= words.len }) this.words.push(words) this.scene.add(words) }) const contactMat = new C.ContactMaterial(this.cMaterial, this.worldMat, { friction: 0.002, frictionEquationStiffness: 1e6, frictionEquationRelaxation: 3, restitution: 0.2, contactEquationStiffness: 1e20, contactEquationRelaxation: 3, }) this.world.addContactMaterial(contactMat) this.setConstraints() } /* Handlers --------------------------------------------------------- */ onMouseMove(event) { this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1 this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1 this.raycaster.setFromCamera(this.mouse, this.camera) const intersects = this.raycaster.intersectObjects(this.scene.children, true) document.body.style.cursor = intersects.length > 0 ? 'pointer' : '' } onClick() { // update the picking ray with the camera and mouse position this.raycaster.setFromCamera(this.mouse, this.camera) // calculate objects intersecting the picking ray const intersects = this.raycaster.intersectObjects(this.scene.children, true) if (intersects.length > 0) { const obj = intersects[0] const { object, face } = obj if (!object.isMesh) return const impulse = new C.Vec3().copy(face.normal).scale(-force); this.words.forEach((word, i) => { word.children.forEach((letter) => { const { body } = letter if (letter !== object) return body.applyLocalImpulse(impulse, new C.Vec3()) }) setTimeout(() => { this.world.removeBody(word.ground) }, 150 * (1 - this.clock.getDelta()) * (this.words.length + i)) }) } } /* Actions --------------------------------------------------------- */ update() { if (!this.words) return this.words.forEach((word, j) => { for (let i = 0; i < word.children.length; i++) { const letter = word.children[i] letter.position.copy(letter.body.position) letter.quaternion.copy(letter.body.quaternion) if (j === this.words.length - 1 && letter.body.position.y <= -50) { this.reset() } if (word.isGroundDisplayed) continue if (letter.body.position.y + letter.initPosition.y <= 0) { this.world.addBody(word.ground) word.isGroundDisplayed = true } } }) } reset() { this.words.forEach((word) => { word.isGroundDisplayed = false const randomColor = pick(colors) for (let i = 0; i < word.children.length; i++) { const progress = (i) / (word.children.length - 1) const letter = word.children[i] letter.body.sleep() const { x, y, z } = letter.initPositionOffset letter.material.color = randomColor.from.clone().lerp(randomColor.to, progress) letter.material.needsUpdate = true letter.body.position.set(x - word.len, y, z) letter.body.quaternion.set(0, 0, 0, 1) letter.body.angularVelocity.setZero() letter.body.torque.setZero() letter.body.force.setZero() letter.body.wakeUp() } }) } /* Values --------------------------------------------------------- */ setConstraints() { this.words.forEach((word) => { for (let i = 0; i < word.children.length; i++) { const letter = word.children[i] const nextLetter = i + 1 === word.children.length ? null : word.children[i + 1] if (!nextLetter) continue const c = new C.ConeTwistConstraint(letter.body, nextLetter.body, { pivotA: new C.Vec3(letter.size.x * 2, 0, 0), pivotB: new C.Vec3(0, 0, 0), axisA: C.Vec3.UNIT_X, axisB: C.Vec3.UNIT_X, angle: 0, twistAngle: 0, maxForce: 1e30, }) c.collideConnected = true this.world.addConstraint(c) } }) } } /* CONSTANTS & HELPERS ---------------------------------------------------------------------------------------------------- */ const fontURL = './dist/fonts/helvetiker_bold.typeface.json' const colors = [ { from : new THREE.Color('#ff699f'), to : new THREE.Color('#a769ff'), }, { from : new THREE.Color('#683fee'), to : new THREE.Color('#527ee1'), }, { from : new THREE.Color('#ee663f'), to : new THREE.Color('#f5678d'), }, { from : new THREE.Color('#ee9ca7'), to : new THREE.Color('#ffdde1'), }, { from : new THREE.Color('#f7971e'), to : new THREE.Color('#ffd200'), }, { from : new THREE.Color('#56ccf2'), to : new THREE.Color('#2f80ed'), }, { from : new THREE.Color('#fc5c7d'), to : new THREE.Color('#6a82fb'), }, { from : new THREE.Color('#dce35b'), to : new THREE.Color('#45b649'), }, ]