美文网首页
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