美文网首页
Three.js模拟书架选书显示书名--Apple的学习笔记

Three.js模拟书架选书显示书名--Apple的学习笔记

作者: applecai | 来源:发表于2020-05-04 20:49 被阅读0次

昨天学习了Three.js的入门教程,然后就想做个小项目练手,首先想到的就是以前在网页上看的模拟故宫,觉得好漂亮呀!感觉在电脑的web上浏览就像进入了真的故宫一样。那么我的小项目受到此启发,我要做一个模拟书架。因为我家书很多,然后放书的位置也很多,包括书架,小书架,3个小柜子,桌子上,箱子中。所以导致我找书很麻烦,这次五一我就想整理下一,给每本书贴个标签排序。当然我可以用表格记录每个标号的书名称及位置。但是关于位置记录不可能描述的很到位,基本上就是书架或者小柜子等。

快速学习了Three.js后,我觉得我可以做一个模拟书架,这样用鼠标单击某本书就会弹出它的位置和书名。这样位置信息就很详细,比如书架第一行,也很直观。比用文字描述来的有趣。

有了想法(项目需求)后,我要分解下任务。通过入门教程如下些内容我还没有尝试过。所以做为小的专攻主题,一一解决了。(关于blender我制作时用了多个贴图,但是three.js如何导入多个贴图不清楚,贴图特效是在Three.js中直接做的)

  1. 会用webstorm调试(我之前python都是用pycharm,它们一脉相承,所以瞬间学会)
  2. threejs对象拾取。
  3. 创建文字--最后学习了ccs,用此方法。
  4. 随机创建创建多个长方形对象。
  5. blender绘制材质及贴图后导出。
  6. Three.js导入obj模型及贴图。
  7. 导入Json数据。(所有书籍信息,暂时还没有完善信息)
    这些主题学习的过程中,我感觉到了经验的迁移, 以前学的各种知识点和技能都派上用处了。但是我发现js的代码要是定义一个结构体数组都不太方便,另外js代码有错误也不提示,必须通过调试看出来,还好一开始我就选择了webstorm。还是它的函数function{}中的内容不是按顺序来执行的,我暂时还没搞清楚。明天需要专门补下js的课程,这样可以系统的了解js,而不仅仅是学习Three.js。

最后动态效果(单击某本书会移出书架到左边显示,并且显示书名,单击书架则书名无显示)今天只实现了一个书架,将来还会添加小柜子,桌子,箱子等。另外,背景等需要美化,若之后不继续精益求精,还不如看表格了哈,花费的时间只能算是js初学者练手,不能算正式的制作工具。


books.gif

静态效果


image.png

blender2.81原始渲染的书架(我也只是入门水平)


image.png

我未经过优化的源码(对js的函数及调用顺序不太清楚,先顺序着写了)

<!DOCTYPE html>
<html>
    <head>
        <title>three.js css3d - molecules</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <style>
            body {
                background-color: #050505;
                background: radial-gradient(ellipse at center,  rgba(43,45,48,1) 0%,rgba(0,0,0,1) 100%);
            }

            #topmenu {
                position: absolute;
                top: 50px;
                width: 50%;
                padding: 10px;
                box-sizing: border-box;
                text-align: left;
                display:block;
            }

            label {
                color: rgb(255, 0, 0);
                background: rgb(255,255,255,1);
                border: 0px;
                padding: 5px 10px;
                margin: 2px;
                font-size: 14px;
            }
            .tagBook {
                color: #FFF;
                font-family: sans-serif;
                padding: 2px;
                background: rgba( 0, 0, 0, .6 );
            }

        </style>
    </head>
    <body>
        <div id="container"></div>
        <div id="topmenu">
            <label id="taga">文件名:未选择书籍</label>
        </div>
        <script type="text/javascript" src="./libs/three.js"></script>
        <script type="text/javascript" src="./libs/OrbitControls.js"></script>
        <script type="text/javascript" src="./libs/OBJLoader.js"></script>
        <script type="text/javascript" src="./libs/MTLLoader.js"></script>
        <script type="text/javascript" src="./libs/GLTFLoader.js"></script>
        <script type="text/javascript" src="./libs/CSS2DRenderer.js"></script>
        <script type="application/json" src="./data/data.json"></script>
        <script >
            var scene = null;
            var camera = null;
            var renderer = null;
            var mesh = null;
            var id = null;
            var INTERSECTED=null;
            var SELECTED = null;
            var SELECTEDpos =new THREE.Vector3();
            var bookstores =null;
            var materials = null;

            function readTextFile(file, callback) {
                var rawFile = new XMLHttpRequest();
                rawFile.overrideMimeType("application/json");
                rawFile.open("GET", file, true);
                rawFile.onreadystatechange = function() {
                    if (rawFile.readyState === 4 && rawFile.status == "200") {
                        callback(rawFile.responseText);
                    }
                }
                rawFile.send(null);
            }


            readTextFile("./data/data.json", function(text){
                bookstores = JSON.parse(text);
                console.log(bookstores);
                init();
            });


            
            function init() {

                renderer = new THREE.WebGLRenderer();
                renderer.setSize(window.innerWidth, window.innerHeight);
                document.getElementById('container').appendChild(renderer.domElement);


                scene = new THREE.Scene();//创建场景
                //var axisHelper = new THREE.AxisHelper(500);
                //scene.add(axisHelper);

                object = new THREE.Object3D();
                scene.add(object);

                var geometry = new THREE.BoxGeometry(0.02, 0.20, 0.1);
                var tempH = 0;

                    for (var i = 0; i < 35*5; i++) {
                        switch (bookstores.mybooks[i].u8line) {
                            case 0:
                                tempH = 0.715;
                                break;
                            case 1:
                                tempH = 0.35;
                                break;
                            case 2:
                                tempH = 0.06;
                                break;
                            case 3:
                                tempH = -0.32;
                                break;
                            case 4:
                                tempH = -0.72;
                                break;
                            default:
                                break
                        }
                        console.log(tempH);
                        var material = new THREE.MeshPhongMaterial({
                            color: 0xffffff * Math.random(), flatShading: true
                        });

                        var mesh = new THREE.Mesh(geometry, material);
                        mesh.position.set(-0.38 + bookstores.mybooks[i].u8pos * 0.02, tempH, 0);
                        //mesh.position.set(-0.4+i*0.02, 0.085, 0);
                        object.add(mesh);

                    }

                var width = window.innerWidth; //窗口宽度
                var height = window.innerHeight; //窗口高度
                var k = width / height; //窗口宽高比
                var s = 1; //三维场景显示范围控制系数,系数越大,显示的范围越大
                //创建相机对象
                camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 0.1, 100);
                //创建相机对象
                //camera = new THREE.OrthographicCamera(-5, 5, 3.75, -3.75, 0.1, 100);
                //camera.position.set(0, 0, 50); //正上方
                camera.position.set(10, 10, 50); //设置相机位置为斜上方
                camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
                var meshbookshelf=null;
                //导入模型
                var OBJLoader = new THREE.OBJLoader();//obj加载器
                var MTLLoader = new THREE.MTLLoader();//材质文件加载器
                MTLLoader.load('obj/bookshelf.mtl', function(materials) {
                    //obj的模型会和MaterialCreator包含的材质对应起来
                    OBJLoader.setMaterials(materials);
                    OBJLoader.load('obj/bookshelf.obj', function(obj) {
                        var texture = new THREE.TextureLoader().load('./img/tree3.jpg');
                        // 颜色贴图中已经包含了光照信息,所以直接使用不受光照影响的基础网格材质MeshBasicMaterial
                        obj.children[0].material= new THREE.MeshBasicMaterial({
                            map:texture,//设置颜色纹理贴图
                        })
                        obj.position.y = 0;
                        obj.position.z = 0;
                        obj.position.x = 0;
                        scene.add(obj);//返回的组对象插入场景中
                    })
                })


                mouse = new THREE.Vector2();
                raycaster = new THREE.Raycaster();
                var light = new THREE.DirectionalLight(0xffffff);//光源颜色
                light.position.set(30, 20, 10);//光源位置
                scene.add(light);//光源添加到场景中
                //环境光
                var ambient = new THREE.AmbientLight(0x333333);
                scene.add(ambient);
                var books = document.getElementById("taga");
                var index = -1;
                function onDocumentClick(event) {
                    //阻止默认动作
                    event.preventDefault();
                    //鼠标转为屏幕中的
                    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
                    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

                    raycaster.setFromCamera( mouse, camera );
                    //射线选中一系列直线
                    var intersects = raycaster.intersectObjects(scene.children,true);


                    //拾取物体数大于0时
                    if (intersects.length > 0) {

                        if (INTERSECTED != intersects[0].object) {
                            //恢复选择前的默认颜色和位置
                            if (SELECTED) {
                                //SELECTED.material.color.setHex(SELECTED.currentHex);
                                SELECTED.scale.y = 1;
                                SELECTED.scale.z = 1;
                                SELECTED.rotateY(Math.PI/2);//绕z轴旋转π/2
                                SELECTED.position.set(SELECTEDpos.x,SELECTEDpos.y,SELECTEDpos.z);
                            }
                            //选择书架后则退出
                            if(intersects[0].object.parent.type == "Group")
                            {
                                SELECTED = null;
                                books.textContent = '文件名:未选择书籍';
                                return;
                            }

                            SELECTED = intersects[0].object;
                            SELECTED.scale.y = 1.2;
                            SELECTED.scale.z = 1.5;
                            SELECTED.rotateY(-Math.PI/2);//绕z轴旋转π/4
                            SELECTEDpos.copy(SELECTED.position);
                            index = Math.round((SELECTEDpos.x+0.38)*50);
                            SELECTED.position.set(-0.6,0.085,0.3);
                            var lineNum=0;
                            switch (SELECTEDpos.y) {
                                case 0.715:
                                    lineNum = 1;
                                    break;
                                case 0.35:
                                    lineNum = 2;
                                    break;
                                case 0.06:
                                    lineNum = 3;
                                    break;
                                case -0.32:
                                    lineNum = 4;
                                    break;
                                case -0.72:
                                    lineNum = 5;
                                    break;
                                default:
                                    break
                            }
                            books.textContent = '行数'+lineNum+"-文件名"+bookstores.mybooks[index].strname;

                        } else {
                            if (SELECTED) {
                                SELECTED.material.color.set(SELECTED.currentHex);//恢复选择前的默认颜色
                            }
                            SELECTED = null;
                            books.textContent = '文件名:未选择书籍';
                        }
                    }
                }
                document.addEventListener('click', onDocumentClick, false);

                render();
                controls = new THREE.OrbitControls(camera,renderer.domElement);//创建控件对象
            }

            function render() {
                requestAnimationFrame(render);//请求再次执行渲染函数render
                renderer.render(scene, camera);//执行渲染操作

            }
        </script>

  </body>

</html>

2020-06-11更新:网友找我要demo,应网友要求,源码及模型已经上传gitee
https://gitee.com/applecai/mybookshelf_js
此demo中已添加了补间动画,参考我的blog
书架选书Tween补间动画应用--Apple的学习笔记

相关文章

网友评论

      本文标题:Three.js模拟书架选书显示书名--Apple的学习笔记

      本文链接:https://www.haomeiwen.com/subject/qutughtx.html