楼倚霜树外、镜天无一毫

81 阅读2分钟

3d低代码可视化编辑预习

获取3d坐标 进行模型及动画移动-单走和双开

/* 定义接口 */
interface XYZInfo {
  x: number | string | null,
  y: number | string | null,
  z: number | string | null,
}
let camPos = reactive({
  x: null,
  y: null,
  z: null
})
let camPosArr = reactive([{
  x: null,
  y: null,
  z: null,
  delay: 0,
  duration: 1
}])

let contrPos = reactive({
  x: null,
  y: null,
  z: null
})
let contrPosArr = reactive([{
  x: null,
  y: null,
  z: null
}])

let clickPos = reactive({
  x: null,
  y: null,
  z: null
})
let clickPosArr = reactive([{
  x: null,
  y: null,
  z: null
}])

let movePosArr: any = reactive([
  new THREE.Vector3(0, 0, 0)
])


/* 单点位 */
function gsapbolFn() {
/* 相机点位 */
  let { x: camX, y: camY, z: camZ } = camera.position;
  camPos.x = camX;
  camPos.y = camY;
  camPos.z = camZ;
  
  camPosArr.push({
    x: camPos.x,
    y: camPos.y,
    z: camPos.z
    delay: i,
    duration: l
  })
/* 指向点位 */
  let { x: contrX, y: contrY, z: contrZ } = controls.target;
  contrPos.x = contrX;
  contrPos.y = contrY;
  contrPos.z = contrZ;
  
  contrPosArr.push({
    x: contrX,
    y: contrY,
    z: contrZ
  })
}


/* gsap动画 */
function gsapFn() {
  gsap.to(camera.position, {
    x: camPos.x,
    y: camPos.y,
    z: camPos.z,
    duration: 3,
    onUpdate: () => {
      controls.target.set(contrPos.x, contrPos.y, contrPos.z);
      controls.update()
    },
  })
}


/* 多重动画 */
function gsapplusFn() {
  let gsapt = gsap.timeline()
  for (let i = 0; i < camPosArr.length; i++) {
    gsapt.to(camera.position, {
      x: camPosArr[i].x,
      y: camPosArr[i].y,
      z: camPosArr[i].z,
      delay: camPosArr[i].delay,
      duration: camPosArr[i].duration,
      onStart: () => {
        controls.reset()
      },
      onUpdate: () => {
        controls.target.set(contrPosArr[i].x, contrPosArr[i].y, contrPosArr[i].z);
        controls.update()
      },
      onComplete: () => {
      }
    })
  }
  
}


/* 3d点位 */
renderer.domElement.addEventListener('click', (event: any) => {
  const px = event.offsetX;
  const py = event.offsetY;
  const x = (px / width) * 2 - 1;
  const y = -(py / height) * 2 + 1;
  
  if (!event.ctrlKey) return;
  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(new THREE.Vector2(x, y), camera);
  const inter = raycaster.intersectObjects(scene.children);
  
  if (inter.length > 0) {
    let { x: clickX, y: clickY, z: clickZ } = inter[0].point
    clickPos.x = clickX.toFixed(3)
    clickPos.y = clickY.toFixed(3)
    clickPos.z = clickZ.toFixed(3)
    
    clickPosArr.push({
      x: clickPos.x,
      y: clickPos.y,
      z: clickPos.z
    })
    
    let v3pos = new THREE.Vector3(clickX, clickY, clickZ)
    movePosArr.push(v3pos)
    firePathFn(movePosArr)
  } else return void 0;
})


/* 小鹿-妙哉! */
let firePath = new THREE.Group()
const firerouteFn = (movePosArr: any) => {
    const posval = new THREE.Vector3(0, 0, 0);
    let up = new THREE.Vector3(0, 1, 0);
    let pathPointList = new PathPointList();
    pathPointList.set(movePosArr, 0.5, 10, up, false);

    /* 精灵图 */
    const texLoader = new THREE.TextureLoader();
    let texture = texLoader.load('/public/mo-image/箭头.png');

    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

    let geometry = new PathGeometry();
    geometry.update(pathPointList, {
        width: 3,
        arrow: false
    });

    let material = new THREE.MeshPhongMaterial({
        color: '#000000',
        depthWrite: true,
        transparent: true,
        opacity: 1,
        side: THREE.DoubleSide,
        map: texture
    })

    firePath.add(new THREE.Mesh(geometry, material))

    firePath.position.copy(posval);
    firePath.position.y = posval.y + 2;
    scene.add(firePath)
    gsap.to(texture.offset, {
        x: -1,
        repeat: -1,
        ease: 'none'
    })
}

const firePathFn = (movePosArr: any) => {
    firerouteFn(movePosArr)
}



/* 拖拽元素之功效-妙哉! */
<el-card style="max-width: 330px">
    <template #header>
        <div class="card-header">
            <strong>拖拽生成</strong>
            <span>
                <el-button color="#626aef" plain 
                    @click="draggableFn" :loading="btnLoad">开始
                </el-button>
            </span>
        </div>
    </template>

    <div class="draggable">
        <p>模型列表
        <img id="box" draggable="true" @dragstart="dragstartFn(1)" src="../../assets/dragg1.jpg" width="50"height="50">
        </p>
    </div>
</el-card>
const createGround = (size: number) => {
    let geometry = new THREE.PlaneGeometry(size, size, 10, 10);
    let material = new THREE.MeshLambertMaterial({ color: 0x888888, wireframe: true });
    let mesh = new THREE.Mesh(geometry, material);
    mesh.rotation.x = -Math.PI / 2;
    mesh.receiveShadow = true;
    return mesh;
}

const groundSize = 1000;
const ground = createGround(groundSize);
scene.add(ground);


/* 拖拽前的羁绊! */
let dragstartFn = (val: any) => {
    console.log(val);
}
/*
document.querySelectorAll("p img").forEach((img) => {
    img.addEventListener("dragstart", function (event) {
    ?.
        let currentTarget = event.currentTarget;
        event.dataTransfer.setData("text", currentTarget.id);
    });
});
 */
document.querySelector?.(".web3d")?.addEventListener("dragover", (e) => {
    e.preventDefault();
});

document.querySelector?.(".web3d").addEventListener("drop", (e: any) => {
    e.preventDefault();
    let raycaster = new THREE.Raycaster();
    let mouse = new THREE.Vector2();
    mouse.x = (e.offsetX / width) * 2 - 1;
    mouse.y = -(e.offsetY / height) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);
    console.log(e.offsetX, e.offsetY);

    let inter = raycaster.intersectObject(scene);
    if (inter.length > 0 && draggablebol.value) {
        const geometry = new THREE.SphereGeometry(30, 32, 16);
        const material = new THREE.MeshBasicMaterial({ color: 0x000000 });
        sphere = new THREE.Mesh(geometry, material);
        let { x, y, z } = inter[0].point;
        sphere.position.set(x, y, z)
        sphereArr.push(sphere)
        scene.add(sphere);
  }

})

小猫咪

/* 有辅助3d中心坐标之功效-妙哉! */
const helper = new THREE.CameraHelper(camera);
scene.add(helper);

/* 标签自带拖拽属性-妙哉! */
draggable:true

丢你

let carMod = new THREE.Group()

loaderDraco().load('/mo-Group/魅族.glb', (gltf: any) => {

  gltf.scene.scale.set(2, 2, 2)

  gltf.scene.rotateY(Math.PI / 1)

  carMod.add(gltf.scene)

 

})

function moveFn() {
  let carArr = movePosArr

  if (carArr.length < 2) return;

  carMod.position.set(carArr[1].x, carArr[1].y, carArr[1].z)
  scene.add(carMod)

  let carpos = new THREE.CatmullRomCurve3(carArr);
  carpos.curveType = 'catmullrom'; // 张力阀门
  carpos.tension = 0.9; // 张力
  carpos.closed = true; // 闭环
  curMoveFn(carpos, carMod, 0.0008)
}

指南针

<div id="compass" style="width: 50px;height:50px">
        <img src="/public/mo-image/箭头.png" width="50" height="50" alt="">
    </div>
    
 function zhinanFn() {

    function render() {
        requestAnimationFrame(render)
        renderer.render(scene, camera)

        const dirx = camera.position.x - controls.target.x
        const dirz = camera.position.z - controls.target.z
        const theta = Math.atan2(dirx, dirz) * 180 / Math.PI + 140
        const compass: any = document.getElementById('compass')
        compass.style.transform = 'rotateZ(' + theta + 'deg)';

  


        // 可加入判断,根据不同的场景偏差偏移角度
        /* if (dmgroup.visible === true) {
            const theta = Math.atan2(dirx, dirz) * 180 / Math.PI + 140
            const compass = document.getElementById('compass')
            compass.style.transform = 'rotateZ(' + theta + 'deg)'
        } else {
            const theta = Math.atan2(dirx, dirz) * 180 / Math.PI + 100
            const compass = document.getElementById('compass')
            compass.style.transform = 'rotateZ(' + theta + 'deg)'
        } */

    }
    render()
}