首页 > 其他分享 >Three.js基础入门介绍——【毕业季】Three.js动态相册

Three.js基础入门介绍——【毕业季】Three.js动态相册

时间:2024-03-22 13:30:14浏览次数:26  
标签:const 相册 object THREE Three camera position new js

前言
岁月匆匆,又是一年毕业季,这次做个动态相册展示图片,放些有意思的内容,一起回忆下校园生活吧。

预期效果

相册展示和点选切换,利用相机旋转和移动来实现一个点击切图平滑过渡的效果。

实现流程

基本流程

1、搭建场景
2、放置图片
3、鼠标事件
4、相机运动

工程文件
工程文件结构如下图:
static:存放静态资源文件
three.js-master:为官网下载的代码包,包含所有需要用到的资源包,链接:https://github.com/mrdoob/three.js/archive/master.zip
在这里插入图片描述
index.html:页面代码

<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<title>My first three.js app</title>
	<style>
		body {
			margin: 0;
		}
	</style>
</head>

<body>
	<script type="importmap">
			{
				"imports": {
					"three": "./three.js-master/build/three.module.js"
				}
			}
		</script>
	<script type="module">
		// 下文JS代码位置
		// ...
	</script>
</body>

</html>

搭建场景
需要导入的内容和提前声明的变量,包含后续不同function中可能用到的场景、相机、渲染器、控制器等要素。

import * as THREE from "three";
import { OrbitControls } from "./three.js-master/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "./three.js-master/examples/jsm/loaders/GLTFLoader.js";
import { TWEEN } from "./three.js-master/examples/jsm/libs/tween.module.min.js";

let scene, camera, renderer, controls; //场景、相机、渲染器、控制器
let pointLight, ambientLight; //光源
let curve = null, rate = 0; // 照片点位
let imgArr = []; //照片url
let imgCut = 50; //照片剪影大小
let rotateImg = true; // 是否继续轮转照片,其实是相机在转
const imgGroup = new THREE.Group(); //照片对象组
const textureLoader = new THREE.TextureLoader(); // 纹理加载器
const pointer = new THREE.Vector2(); //点击坐标
const raycaster = new THREE.Raycaster(); //射线
const threeEl = document.getElementById('container'); //元素获取

场景和部分数据初始化,其中图片这里直接是使用的网络资源图,可以使用本地文件或者自己搭建的服务环境文件。

function init() {
    imgArr = [
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg95.699pic.com%2Fphoto%2F40142%2F4204.gif_wh860.gif&refer=http%3A%2F%2Fimg95.699pic.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659077726&t=ea5e3abb8b838546ae9377321744f875",
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F019b795b2cc0dba80121bbec95a340.jpg%402o.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076627&t=be25ec64df151d68c5dd5296a1d68fcf",
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp4.itc.cn%2Fimages01%2F20200707%2Fcc1bec607b1949549f374f3e0e68bc2d.jpeg&refer=http%3A%2F%2Fp4.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659072591&t=34a56c58c383f15a5118d81859cad417",
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F019b795b2cc0dba80121bbec95a340.jpg%402o.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=aae752e553bbdeda27a4fa14b242e4e8",
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp.qpic.cn%2Fdnfbbspic%2F0%2Fdnfbbs_dnfbbs_dnf_gamebbs_qq_com_forum_202007_05_084137qjj5sjd9pqm9mprr.jpg%2F0&refer=http%3A%2F%2Fp.qpic.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=bd12aa3f0d060cc19880158e9ef7b16f",
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.keaidian.com%2Fuploads%2Fallimg%2F190713%2F13174657_15.jpg&refer=http%3A%2F%2Fwww.keaidian.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=47296d32109bbffce6844b557a109d24",
        "https://img2.baidu.com/it/u=3487630334,1818100496&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
        "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_source%2F29%2F09%2F31%2F290931cc8b21f13ff0ca273ff8e4865e.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=5c77914a690c1b53de57c7bf5692487a",
    ]
    changeImg(threeEl, imgArr[0]);
    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    renderer = new THREE.WebGLRenderer();
    // 添加相机并设置在原点
    camera.position.set(0, 0, 0);
    camera.lookAt(new THREE.Vector3(1, 0, 0));
    // 添加一个点光源
    pointLight = new THREE.PointLight(0xffffff);
    scene.add(pointLight);
    // 添加一个环境光
    ambientLight = new THREE.AmbientLight(0xffffff);
    scene.add(ambientLight);

    // 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
    // 添加坐标系到场景中
    // const axes = new THREE.AxesHelper(20);
    // scene.add(axes);
    // 创建渲染器对象
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
    renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染区域尺寸
    threeEl.appendChild(renderer.domElement); //body元素中插入canvas对象render
    // 监听鼠标
    pointerListing();
    // 初始化控制器
    controls = new OrbitControls(camera, renderer.domElement);//创建控件对象
    controls.enabled = false;
}

放置图片
用到了基础网格材质(MeshBasicMaterial)和贴图,用贴图加载器(TextureLoader)将图片贴到放置的平面缓冲几何体(PlaneGeometry)上,再在外层增加一个有一定透明度的立方缓冲几何体(BoxGeometry),让它看上去就像是一个相框。

基础网格材质(MeshBasicMaterial)
一个以简单着色(平面或线框)方式来绘制几何体的材质,这种材质不受光照的影响。

构造函数(Constructor)
MeshBasicMaterial( parameters : Object )
parameters - (可选)用于定义材质外观的对象,具有一个或多个属性。材质的任何属性都可以从此处传入(包括从Material继承的任何属性)。
属性color例外,其可以作为十六进制字符串传递,默认情况下为 0xffffff(白色),内部调用Color.set(color)。

TextureLoader
加载texture的一个类。 内部使用ImageLoader来加载文件。

构造函数
TextureLoader( manager : LoadingManager )
manager — 加载器使用的loadingManager,默认值为THREE.DefaultLoadingManager.

// 图片贴到对应位置
function placeImg() {
    imgGroup.name = 'imgGroup';
    // 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
    // Create a closed wavey loop
    curve = new THREE.CatmullRomCurve3([
        new THREE.Vector3(75, 0, 0),
        new THREE.Vector3(0, 0, 75),
        new THREE.Vector3(-75, 0, 0),
        new THREE.Vector3(0, 0, -75)

    ]);
    // centripetal、chordal和catmullrom
    curve.curveType = "catmullrom";
    curve.closed = true;//设置是否闭环
    curve.tension = 1; //设置线的张力,0为无弧度折线

    // // 为曲线添加材质在场景中显示出来,不添加到场景显示也不会影响运动轨迹,相当于一个Helper
    // const points = curve.getPoints(50);
    // const geometry = new THREE.BufferGeometry().setFromPoints(points);
    // const material = new THREE.LineBasicMaterial({ color: 0x000000 });

    // // Create the final object to add to the scene
    // const curveObject = new THREE.Line(geometry, material);
    // scene.add(curveObject);

    const imgNum = imgArr.length;
    rate = imgNum <= 0 ? 0 : (1 / imgNum);

    imgArr.forEach((item, index) => {
        const imgPosition = curve.getPointAt(rate * index);
        // 材质对象Material
        const material = new THREE.MeshBasicMaterial({
            side: THREE.DoubleSide,
            opacity: 0.8,
            transparent: true,
            name: "material" + index,
            map: textureLoader.load(item)
        });
        const mesh = new THREE.Mesh(new THREE.PlaneGeometry(imgCut, imgCut), material);
        mesh.position.set(imgPosition.x, imgPosition.y, imgPosition.z);
        mesh.rotation.y = -Math.PI / 2;
        mesh.lookAt(scene.position) //设置朝向
        mesh.name = item;
        // imgGroup.add(mesh);
        scene.add(mesh);
        // 加一个框
        const boxMaterial = new THREE.MeshBasicMaterial({
            opacity: 0.1,
            transparent: true,
            color: 0x0081cc
        });
        const boxMesh = new THREE.Mesh(new THREE.BoxGeometry(imgCut * 1.1, imgCut * 1.1, imgCut * 0.1), boxMaterial);
        boxMesh.position.set(imgPosition.x, imgPosition.y, imgPosition.z);
        boxMesh.rotation.y = -Math.PI / 2;
        boxMesh.lookAt(scene.position) //设置朝向
        boxMesh.name = item;
        imgGroup.add(boxMesh);
    })
    scene.add(imgGroup);

}

鼠标事件
当鼠标放到相框上,贴有照片的平面几何体外层立方几何体的透明度改变,达到选中效果,选中贴图剪影时页面背景改变。

 // 背景替换
function changeImg(element, url) {
    element.style.backgroundImage = `url(${url})`;
    // element.style.backgroundSize = '100%'; 
}
// 添加鼠标监听
function pointerListing() {
    threeEl.addEventListener("pointerdown", onPointerDown);
    threeEl.addEventListener("pointermove", onPointerMove);
}
function onPointerMove(event) {
    // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    pointer.set(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1
    );
    // console.log(scene);
    raycaster.setFromCamera(pointer, camera);
    const intersects = raycaster.intersectObjects(
        [...imgGroup.children],
        false
    );
    // 有照片就换背景
    if (intersects.length > 0) {
        const intersect = intersects[0];
        intersect.object.material.opacity = 0.5;
    }else{
        imgGroup.children.forEach(item => {
            item.material.opacity = 0.1;
        });
    }
}
function onPointerDown(event) {
    // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    pointer.set(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1
    );
    // console.log(scene);
    raycaster.setFromCamera(pointer, camera);
    const intersects = raycaster.intersectObjects(
        [...imgGroup.children],
        false
    );
    // 有照片就换背景
    if (intersects.length > 0) {
        const intersect = intersects[0];
        // console.log(intersect.object);
        // 背景替换
        changeImg(threeEl, intersect.object.name);
        // 相机移动
        rotateImg = false;
        // 照片旋转停下来
        const changePosition = new THREE.Vector3(
            intersect.object.position.x*1.5,
            intersect.object.position.y*1.5,
            intersect.object.position.z*1.5);
        animateCamera(camera.position, changePosition);
    } else {
        // 相机回原点
        const o = new THREE.Vector3(0,0,0);
        animateCamera(camera.position, o);
        rotateImg = true;
    }
};

相机运动
相机本身在旋转,相对的图片看起来就像是在滚动播放,当点击某个图片时,相机移动到对应对象更外层,点击空白再回到原点,达到平滑切换的效果。

function onPointerDown(event) {
    // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    pointer.set(
        (event.clientX / window.innerWidth) * 2 - 1,
        -(event.clientY / window.innerHeight) * 2 + 1
    );
    // console.log(scene);
    raycaster.setFromCamera(pointer, camera);
    const intersects = raycaster.intersectObjects(
        [...imgGroup.children],
        false
    );
    // 有照片就换背景
    if (intersects.length > 0) {
        const intersect = intersects[0];
        // console.log(intersect.object);
        // 背景替换
        changeImg(threeEl, intersect.object.name);
        // 相机移动
        rotateImg = false;
        // 照片旋转停下来
        const changePosition = new THREE.Vector3(
            intersect.object.position.x*1.5,
            intersect.object.position.y*1.5,
            intersect.object.position.z*1.5);
        animateCamera(camera.position, changePosition);
    } else {
        // 相机回原点
        const o = new THREE.Vector3(0,0,0);
        animateCamera(camera.position, o);
        rotateImg = true;
    }
};
// current1 相机当前的位置
// target1 相机的目标位置
// current2 当前的controls的target
// target2 新的controls的target
function animateCamera(current1, target1,callBack) {
    var tween = new TWEEN.Tween({
        x1: current1.x, // 相机当前位置x
        y1: current1.y, // 相机当前位置y
        z1: current1.z, // 相机当前位置z
        // x2: current2.x, // 控制当前的中心点x
        // y2: current2.y, // 控制当前的中心点y
        // z2: current2.z // 控制当前的中心点z
    });
    tween.to({
        x1: target1.x, // 新的相机位置x
        y1: target1.y, // 新的相机位置y
        z1: target1.z, // 新的相机位置z
        // x2: target2.x, // 新的控制中心点位置x
        // y2: target2.y, // 新的控制中心点位置x
        // z2: target2.z // 新的控制中心点位置x
    }, 1000);
    tween.onUpdate(function (object) {
        camera.position.x = object.x1;
        camera.position.y = object.y1;
        camera.position.z = object.z1;
        // controls.target.x = object.x2;
        // controls.target.y = object.y2;
        // controls.target.z = object.z2;
        // controls.update();
    });
    tween.onComplete(function () {
        callBack && callBack()
    });
    tween.easing(TWEEN.Easing.Cubic.InOut);
    tween.start();
}

完整代码

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>My first three.js app</title>
    <style>
        body {
            margin: 0px;
        }
        #container {
            /* background: #000000 url("https://seopic.699pic.com/photo/50041/6756.jpg_wh1200.jpg") no-repeat; */
            background: #000000;
            background-size: 100vw 100vh;
            overflow: hidden;
            width: 100vw;
            height: 100vh;
        }
    </style>
</head>

<body>
    <div id="container">

    </div>
    <script type="importmap">
        {
            "imports": {
                "three": "./three.js-master/build/three.module.js"
            }
        }
    </script>
    <script type="module">
        import * as THREE from "three";
        import { OrbitControls } from "./three.js-master/examples/jsm/controls/OrbitControls.js";
        import { GLTFLoader } from "./three.js-master/examples/jsm/loaders/GLTFLoader.js";
        import { TWEEN } from "./three.js-master/examples/jsm/libs/tween.module.min.js";

        let scene, camera, renderer, controls; //场景、相机、渲染器、控制器
        let pointLight, ambientLight; //光源
        let curve = null, rate = 0; // 照片点位
        let imgArr = []; //照片url
        let imgCut = 50; //照片剪影大小
        let rotateImg = true; // 是否继续轮转照片,其实是相机在转
        const imgGroup = new THREE.Group(); //照片对象组
        const textureLoader = new THREE.TextureLoader(); // 纹理加载器
        const pointer = new THREE.Vector2(); //点击坐标
        const raycaster = new THREE.Raycaster(); //射线
        const threeEl = document.getElementById('container'); //元素获取

        function init() {
            imgArr = [
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg95.699pic.com%2Fphoto%2F40142%2F4204.gif_wh860.gif&refer=http%3A%2F%2Fimg95.699pic.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659077726&t=ea5e3abb8b838546ae9377321744f875",
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F019b795b2cc0dba80121bbec95a340.jpg%402o.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076627&t=be25ec64df151d68c5dd5296a1d68fcf",
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp4.itc.cn%2Fimages01%2F20200707%2Fcc1bec607b1949549f374f3e0e68bc2d.jpeg&refer=http%3A%2F%2Fp4.itc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659072591&t=34a56c58c383f15a5118d81859cad417",
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F019b795b2cc0dba80121bbec95a340.jpg%402o.jpg&refer=http%3A%2F%2Fimg.zcool.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=aae752e553bbdeda27a4fa14b242e4e8",
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fp.qpic.cn%2Fdnfbbspic%2F0%2Fdnfbbs_dnfbbs_dnf_gamebbs_qq_com_forum_202007_05_084137qjj5sjd9pqm9mprr.jpg%2F0&refer=http%3A%2F%2Fp.qpic.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=bd12aa3f0d060cc19880158e9ef7b16f",
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fwww.keaidian.com%2Fuploads%2Fallimg%2F190713%2F13174657_15.jpg&refer=http%3A%2F%2Fwww.keaidian.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=47296d32109bbffce6844b557a109d24",
                "https://img2.baidu.com/it/u=3487630334,1818100496&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500",
                "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fup.enterdesk.com%2Fedpic_source%2F29%2F09%2F31%2F290931cc8b21f13ff0ca273ff8e4865e.jpg&refer=http%3A%2F%2Fup.enterdesk.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1659076255&t=5c77914a690c1b53de57c7bf5692487a",
            ]
            changeImg(threeEl, imgArr[0]);
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            renderer = new THREE.WebGLRenderer();
            // 添加相机并设置在原点
            camera.position.set(0, 0, 0);
            camera.lookAt(new THREE.Vector3(1, 0, 0));
            // 添加一个点光源
            pointLight = new THREE.PointLight(0xffffff);
            scene.add(pointLight);
            // 添加一个环境光
            ambientLight = new THREE.AmbientLight(0xffffff);
            scene.add(ambientLight);

            // 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
            // 添加坐标系到场景中
            // const axes = new THREE.AxesHelper(20);
            // scene.add(axes);
            // 创建渲染器对象
            renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            renderer.setSize(window.innerWidth, window.innerHeight);//设置渲染区域尺寸
            threeEl.appendChild(renderer.domElement); //body元素中插入canvas对象render
            // 监听鼠标
            pointerListing();
            // 初始化控制器
            controls = new OrbitControls(camera, renderer.domElement);//创建控件对象
            controls.enabled = false;
        }

        // 背景替换
        function changeImg(element, url) {
            element.style.backgroundImage = `url(${url})`;
            // element.style.backgroundSize = '100%'; 
        }
        // 添加鼠标监听
        function pointerListing() {
            threeEl.addEventListener("pointerdown", onPointerDown);
            threeEl.addEventListener("pointermove", onPointerMove);
        }
        function onPointerMove(event) {
            // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
            pointer.set(
                (event.clientX / window.innerWidth) * 2 - 1,
                -(event.clientY / window.innerHeight) * 2 + 1
            );
            // console.log(scene);
            raycaster.setFromCamera(pointer, camera);
            const intersects = raycaster.intersectObjects(
                [...imgGroup.children],
                false
            );
            // 有照片就换背景
            if (intersects.length > 0) {
                const intersect = intersects[0];
                intersect.object.material.opacity = 0.5;
            }else{
                imgGroup.children.forEach(item => {
                    item.material.opacity = 0.1;
                });
            }
        }
        function onPointerDown(event) {
            // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
            pointer.set(
                (event.clientX / window.innerWidth) * 2 - 1,
                -(event.clientY / window.innerHeight) * 2 + 1
            );
            // console.log(scene);
            raycaster.setFromCamera(pointer, camera);
            const intersects = raycaster.intersectObjects(
                [...imgGroup.children],
                false
            );
            // 有照片就换背景
            if (intersects.length > 0) {
                const intersect = intersects[0];
                // console.log(intersect.object);
                // 背景替换
                changeImg(threeEl, intersect.object.name);
                // 相机移动
                rotateImg = false;
                // 照片旋转停下来
                const changePosition = new THREE.Vector3(
                    intersect.object.position.x*1.5,
                    intersect.object.position.y*1.5,
                    intersect.object.position.z*1.5);
                animateCamera(camera.position, changePosition);
            } else {
                // 相机回原点
                const o = new THREE.Vector3(0,0,0);
                animateCamera(camera.position, o);
                rotateImg = true;
            }
        };

        // current1 相机当前的位置
        // target1 相机的目标位置
        // current2 当前的controls的target
        // target2 新的controls的target
        function animateCamera(current1, target1,callBack) {
            var tween = new TWEEN.Tween({
                x1: current1.x, // 相机当前位置x
                y1: current1.y, // 相机当前位置y
                z1: current1.z, // 相机当前位置z
                // x2: current2.x, // 控制当前的中心点x
                // y2: current2.y, // 控制当前的中心点y
                // z2: current2.z // 控制当前的中心点z
            });
            tween.to({
                x1: target1.x, // 新的相机位置x
                y1: target1.y, // 新的相机位置y
                z1: target1.z, // 新的相机位置z
                // x2: target2.x, // 新的控制中心点位置x
                // y2: target2.y, // 新的控制中心点位置x
                // z2: target2.z // 新的控制中心点位置x
            }, 1000);
            tween.onUpdate(function (object) {
                camera.position.x = object.x1;
                camera.position.y = object.y1;
                camera.position.z = object.z1;
                // controls.target.x = object.x2;
                // controls.target.y = object.y2;
                // controls.target.z = object.z2;
                // controls.update();
            });
            tween.onComplete(function () {
                callBack && callBack()
            });
            tween.easing(TWEEN.Easing.Cubic.InOut);
            tween.start();
        }

        // 图片贴到对应位置
        function placeImg() {
            imgGroup.name = 'imgGroup';
            // 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
            // Create a closed wavey loop
            curve = new THREE.CatmullRomCurve3([
                new THREE.Vector3(75, 0, 0),
                new THREE.Vector3(0, 0, 75),
                new THREE.Vector3(-75, 0, 0),
                new THREE.Vector3(0, 0, -75)

            ]);
            // centripetal、chordal和catmullrom
            curve.curveType = "catmullrom";
            curve.closed = true;//设置是否闭环
            curve.tension = 1; //设置线的张力,0为无弧度折线

            // // 为曲线添加材质在场景中显示出来,不添加到场景显示也不会影响运动轨迹,相当于一个Helper
            // const points = curve.getPoints(50);
            // const geometry = new THREE.BufferGeometry().setFromPoints(points);
            // const material = new THREE.LineBasicMaterial({ color: 0x000000 });

            // // Create the final object to add to the scene
            // const curveObject = new THREE.Line(geometry, material);
            // scene.add(curveObject);

            const imgNum = imgArr.length;
            rate = imgNum <= 0 ? 0 : (1 / imgNum);

            imgArr.forEach((item, index) => {
                const imgPosition = curve.getPointAt(rate * index);
                // 材质对象Material
                const material = new THREE.MeshBasicMaterial({
                    side: THREE.DoubleSide,
                    opacity: 0.8,
                    transparent: true,
                    name: "material" + index,
                    map: textureLoader.load(item)
                });
                const mesh = new THREE.Mesh(new THREE.PlaneGeometry(imgCut, imgCut), material);
                mesh.position.set(imgPosition.x, imgPosition.y, imgPosition.z);
                mesh.rotation.y = -Math.PI / 2;
                mesh.lookAt(scene.position) //设置朝向
                mesh.name = item;
                // imgGroup.add(mesh);
                scene.add(mesh);
                // 加一个框
                const boxMaterial = new THREE.MeshBasicMaterial({
                    opacity: 0.1,
                    transparent: true,
                    color: 0x0081cc
                });
                const boxMesh = new THREE.Mesh(new THREE.BoxGeometry(imgCut * 1.1, imgCut * 1.1, imgCut * 0.1), boxMaterial);
                boxMesh.position.set(imgPosition.x, imgPosition.y, imgPosition.z);
                boxMesh.rotation.y = -Math.PI / 2;
                boxMesh.lookAt(scene.position) //设置朝向
                boxMesh.name = item;
                imgGroup.add(boxMesh);
            })
            scene.add(imgGroup);

        }


        //执行渲染操作   指定场景、相机作为参数
        function render() {
            renderer.render(scene, camera);//执行渲染操作
            if (rotateImg) {
                camera.rotateY(0.001);//每次绕y轴旋转0.001弧度
            }
            TWEEN.update();
            // newmesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
            requestAnimationFrame(render);//请求再次执行渲染函数render
        }

        function initWindow() {
            let onResize = function () {
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
                renderer.setSize(window.innerWidth, window.innerHeight);
            };
            window.addEventListener("resize", onResize, false);
        };
        init();
        placeImg();
        initWindow();
        render();

    </script>

</body>

</html>

实现效果

在这里插入图片描述

标签:const,相册,object,THREE,Three,camera,position,new,js
From: https://blog.csdn.net/I_loveCong/article/details/136938107

相关文章

  • 如何快速上手Vue.js,Vue.js怎么学习,看这篇就够了(全网最牛)
    1、官方文档Vue.js官方文档 是最重要的学习资源,其中包含了详细的教程、示例和API参考,让你快速了解Vue.js的核心概念和用法。2、VueCLI使用VueCLI创建项目是一个快速搭建Vue.js应用的好方法,它提供了一个交互式的界面和现代的开发工具链,让您轻松生成项目结构和......
  • Json泛型化处理
    importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.TypeReference;importjava.util.List;publicclassJSONCommonBuilder{/***Json泛型化处理*/publicstatic<T>BusinessCache<T>getBusinessCache(Objectobject,Clas......
  • js 如何提取富文本里的图片路径
    在JavaScript中,要从富文本内容中提取图片路径,你可以创建一个DOM元素来作为解析富文本内容的容器,然后将富文本内容作为文本节点插入这个容器中。接着,你可以使用querySelectorAll方法和CSS选择器来选择所有的img元素,并获取它们的src属性。以下是一个简单的示例代码functionextra......
  • 【附源码】Node.js毕业设计高校后勤管理系统(Express)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:在当今信息化时代,高校后勤管理作为学校日常运营的重要组成部分,承担着保障校园环境、维护学生生活和教学秩序的重要职责。随着教育体系的不断壮大,传统的人工......
  • 【附源码】Node.js毕业设计高校后勤保修系统(Express)
    本系统(程序+源码)带文档lw万字以上  文末可获取本课题的源码和程序系统程序文件列表系统的选题背景和意义选题背景:在当今信息化时代,高效、便捷的管理方式已经成为了各个领域追求的目标。对于高校来说,后勤保修工作是保障校园正常运行的重要环节。传统的高校后勤保修工作主......
  • JSON格式数据
    JSON简介JSON是一种轻量级的数据交换格式,全称为JavaScriptObjectNotation。它采用完全独立于编程语言的文本格式来表示数据,具有简洁、易读、易解析等特点。简洁和清晰的层次结构使得JSON成为理想的数据交换语言,易于一般人阅读和编写。 JSON的作用可在多种语言......
  • Jackson进行JSON序列化/反序列化添加Java 8的日期和时间库支持
     添加依赖包<!--Jackson进行JSON序列化/反序列化添加Java8的日期和时间库支持--> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.13.0</version> ......
  • 前端基础之原生js事件绑定案例
    原生js事件绑定开关灯案例<script><divid="d1"class="c1bg_greenbg_red"></div><buttonid="d2">变色</button><script>letbtnEle=document.getElementById('d2')letdivEle=docum......
  • JSP语法 《Java Web开发从入门到实战》第三章节
    目录一、JSP页面的基本构成1、普通的HTML标记2、JSP注释(增强JSP文件的可读性,便于Web项目的更新和维护)3、Java脚本元素:声明、Java程序片、Java表达式4、JSP标记:指令标记、动作标记、自定义标记等二、<%%>和<%!%>的不同之处1、翻译不同2、定义不同3、声明不同......
  • 移动端 页面适配 原生js及lib-flexible 插件
    1.js原生;(function(win,doc){change()functionchange(){varremSize=window.innerWidth/7.5||50/*设计稿是以width=750px为基准的*/document.querySelector('html').style.fontSize=(remSize>100?100:remSize)+'px'}......