整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          three.js教程一 使用three.js創建一個三維場景

          、html頁面樣式

          html,body{margin: 0; padding: 0; overflow: hidden}

          2、引入js

          <script src="../js/three.js"></script>
          <script src="./js/jquery.min.js"></script>
          //Stats.js性能監控器,主要用于檢測動畫運行時的幀數
          <script src="./js/Stats.js"></script>
          //圖形界面庫,使用這個庫可以很容易地創建出能夠改變代碼變量的界面組件
          <script src="./js/dat.gui.js"></script>

          3、代碼

          //性能監控器輸出容器
          <div id="stats"></div>
          $(function(){
          //統計功能的引入
          function initStats(){
          var stats = new Stats();
          //0 FPS 刷新頻率,一秒渲染次數
          //1 MS 刷新周期,渲染一次時間
          stats.setMode(0);
          stats.domElement.style.position = "absolute";
          stats.domElement.style.left = "0px";
          stats.domElement.style.top = "0px";
          $("#stats").append(stats.domElement);
          return stats;
          }
          var stats = initStats();
          //定義dat.GUI需要修改的變量
          var controls = new function(){
          this.rotationSpeed = 0.02;
          this.bouncingSpeed = 0.03;
          }
          var gui = new dat.GUI();
          //變量傳遞給dat.GUI并限定取值范圍
          gui.add(controls,"rotationSpeed",0,0.5);
          gui.add(controls,"bouncingSpeed",0,0.5);
          //屏幕尺寸
          var size = {
          "w": window.innerWidth,
          "h": window.innerHeight,
          //屏幕比例
          "s": window.innerWidth / window.innerHeight
          };
          //定義場景
          var scene = new THREE.Scene();
          //定義相機,參數一是角度,參數二屏幕比例,參數三最小可視范圍,參數四最大可視范圍
          var camera = new THREE.PerspectiveCamera(45,size.s,0.1,1000);
          //相機觀察位置、及方向
          camera.position.x = -30;
          camera.position.y = 40;
          camera.position.z = 30;
          camera.lookAt(scene.position);
          //定義渲染器
          var renderer = new THREE.WebGLRenderer();
          //設置場景背景色
          renderer.setClearColor(new THREE.Color(0xeeeeee));
          //設置場景尺寸
          renderer.setSize(size.w,size.h);
          //開啟陰影
          renderer.shadowMapEnabled = true;
          //設置聚光燈光源
          var spotLight = new THREE.SpotLight(0xffffff);
          spotLight.position.set(-40,60,-10);
          //開啟陰影
          spotLight.castShadow = true;
          scene.add(spotLight);
          
          //定義坐標,參數為坐標的大小
          var axes = new THREE.AxesHelper(20);
          scene.add(axes);
          //定義模型
          //地板模型的形狀
          var planeGeometry = new THREE.PlaneGeometry(60,20,1,1);
          //模型材質
          var planeMaterial = new THREE.MeshLambertMaterial({
          color: 0xcccccc });
          var plane = new THREE.Mesh(planeGeometry,planeMaterial);
          //開啟陰影
          plane.receiveShadow = true;
          plane.rotation.x = -0.5*Math.PI;//Math.PI = 180度
          plane.position.x = 15;
          plane.position.y = 0;
          plane.position.z = 0;
          scene.add(plane);
          //立體體模型
          var cubeGeometry = new THREE.BoxGeometry(4,4,4);
          var cubeMaterial = new THREE.MeshLambertMaterial({
          color: 0xff0000,
          //wireframe是否開啟模型的邊框線
          wireframe: false
          });
          var cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
          //開啟陰影
          cube.castShadow = true;
          cube.position.x = -4;
          cube.position.y = 3;
          cube.position.z = 0;
          scene.add(cube);
          //球體模型
          var sphereGeometry = new THREE.SphereGeometry(4,20,20);
          var sphereMaterial = new THREE.MeshLambertMaterial({
          color: 0x7777ff,
          //wireframe是否開啟模型的邊框線
          wireframe: false
          });
          var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);
          //開啟陰影
          sphere.castShadow = true;
          sphere.position.x = 20;
          sphere.position.y = 4;
          sphere.position.z = 2;
          scene.add(sphere);
          //渲染到頁面中
          $("body").append(renderer.domElement);
          //定義渲染函數及動畫效果
          var step = 0;
          function renderScene(){
          stats.update();
          cube.rotation.x += controls.rotationSpeed;
          cube.rotation.y += controls.rotationSpeed;
          cube.rotation.z += controls.rotationSpeed;
          step += controls.bouncingSpeed;
          sphere.position.x = 20+(10*Math.cos(step));
          sphere.position.y = 4+(10*Math.abs(Math.sin(step)));
          //requestAnimationFrame是瀏覽器用于定時循環操作的一個接口
          requestAnimationFrame(renderScene);
          renderer.render(scene,camera);
          }
          renderScene();
          });
          </script>

          運行效果:

          左一為性能監控器輸,右一為dat.GUI

          本文由dragonir授權發布,作者還有很多關于Three.js的作品,剛開始看到這個作品,覺得很有趣,雖然對Three.js完全不懂,哈哈,原文地址:https://segmentfault.com/a/1190000041261707,接下來分享這篇文章。

          先看看效果:

          背景

          虎年 春節將至,本文使用 React + Three.js 技術棧,實現趣味 3D 創意頁面。本文包含的知識點主要包括:ShadowMaterial、 MeshPhongMaterial 兩種基本材質的使用、使用 LoadingManager 展示模型加載進度、OrbitControls 的緩動動畫、TWEEN簡單補間動畫效果等。

          實現

          在線預覽,已適配移動端:https://dragonir.github.io/3d/#/lunar

          引入資源

          其中 GLTFLoader、FBXLoader 用于加在模型、OrbitControls 用戶鏡頭軌道控制、TWEEN 用于生成補間動畫。

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

          場景初始化

          這部分內容主要用于初始化場景和參數,詳細講解可點擊文章末尾鏈接閱讀我之前的文章,本文不再贅述。

            container = document.getElementById('container');
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            container.appendChild(renderer.domElement);
            // 場景
            scene = new THREE.Scene();
            scene.background = new THREE.TextureLoader().load(bgTexture);
            // 霧化效果
            scene.fog = new THREE.Fog(0xdddddd, 100, 120);
            // 攝像機
            camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(100, 100, 100);
            camera.lookAt(new THREE.Vector3(0, 0, 0));
            // 平行光
            const cube = new THREE.Mesh(new THREE.BoxGeometry(0.001, 0.001, 0.001), new THREE.MeshLambertMaterial({ color: 0xdc161a }));
            cube.position.set(0, 0, 0);
            light = new THREE.DirectionalLight(0xffffff, 1);
            light.position.set(20, 20, 8);
            light.target = cube;
            scene.add(light);
            // 環境光
            const ambientLight = new THREE.AmbientLight(0xffffff);
            scene.add(ambientLight);
            // 聚光燈
            const spotLight = new THREE.SpotLight(0xffffff);
            spotLight.position.set(-20, 20, -2);
            scene.add(spotLight);
          

          Fog 場景霧化

          本例中,打開頁面時模型由遠及近加載,顏色由白色變為彩色的功能就是通過 Fog 實現的。Fog 類定義的是線性霧,霧的密度是隨著距離線性增大的,即場景中物體霧化效果隨著隨距離線性變化。

          構造函數:Fog(color, near, far)

          color 屬性: 表示霧的顏色,比如設置為紅色,場景中遠處物體為黑色,場景中最近處距離物體是自身顏色,最遠和最近之間的物體顏色是物體本身顏色和霧顏色的混合效果。
          near 屬性:表示應用霧化效果的最小距離,距離活動攝像機長度小于 near 的物體將不會被霧所影響。
          far 屬性:表示應用霧化效果的最大距離,距離活動攝像機長度大于 far 的物體將不會被霧所影響。

          創建地面

          本例中使用了背景圖,我需要一個既能呈現透明顯示背景、又能產生陰影的材質生成地面,于是使用到 ShadowMaterial 材質。

          var planeGeometry = new THREE.PlaneGeometry(100, 100);
          var planeMaterial = new THREE.ShadowMaterial({ opacity: .5 });
          var plane = new THREE.Mesh(planeGeometry, planeMaterial);
          plane.rotation.x = -0.5 * Math.PI;
          plane.position.set(0, -8, 0);
          plane.receiveShadow = true;
          scene.add(plane);
          

          ShadowMaterial 陰影材質

          此材質可以接收陰影,但在其他方面完全透明。

          構造函數:ShadowMaterial(parameters: Object)

          parameters:(可選)用于定義材質外觀的對象,具有一個或多個屬性。

          特殊屬性:

          .isShadowMaterial[Boolean]:用于檢查此類或派生類是否為陰影材質。默認值為 true。因為其通常用在內部優化,所以不應該更改該屬性值。
          .transparent[Boolean]:定義此材質是否透明。默認值為 true。

          創建魔法陣

          在老虎 底部地面創建一個炫酷的旋轉自發光圓形魔法陣。

          cycle = new THREE.Mesh(new THREE.PlaneGeometry(40, 40), new THREE.MeshPhongMaterial({
            map: new THREE.TextureLoader().load(cycleTexture),
            transparent: true
          }));
          cycle.rotation.x = -0.5 * Math.PI;
          cycle.position.set(0, -9, 0);
          cycle.receiveShadow = true;
          scene.add(cycle);
          

          魔法陣的貼圖:

          MeshPhongMaterial 網格材質

          一種用于具有鏡面高光的光澤表面的材質。該材質使用非物理的 Blinn-Phong 模型來計算反射率。

          構造函數:MeshPhongMaterial(parameters: Object)

          parameters:(可選)用于定義材質外觀的對象,具有一個或多個屬性。

          特殊屬性:

          .emissive[Color]:材質的放射(光)顏色,基本上是不受其他光照影響的固有顏色。默認為黑色。
          .emissiveMap[Texture]:設置放射(發光)貼圖。默認值為 null。放射貼圖顏色由放射顏色和強度所調節。如果你有一個放射貼圖,請務必將放射顏色設置為黑色以外的其他顏色。
          .emissiveIntensity[Float]:放射光強度。調節發光顏色。默認為 1。
          .shininess[Float]:specular 高亮的程度,越高的值越閃亮。默認值為 30。
          .specular[Color]:材質的高光顏色。默認值為 0x111111 的顏色 Color。這定義了材質的光澤度和光澤的顏色。
          .specularMap[Texture]:鏡面反射貼圖值會影響鏡面高光以及環境貼圖對表面的影響程度。默認值為 null。

          MeshLambertMaterial 中使用的 Lambertian 模型不同,該材質可以模擬具有鏡面高光的光澤表面(例如涂漆木材)。使用 Phong 著色模型計算著色時,會計算每個像素的陰影,與 MeshLambertMaterial 使用的 Gouraud 模型相比,該模型的結果更準確,但代價是犧牲一些性能。

          MeshStandardMaterialMeshPhysicalMaterial 也使用這個著色模型。在 MeshStandardMaterialMeshPhysicalMaterial 上使用此材質時,性能通常會更高 ,但會犧牲一些圖形精度。

          文字模型

          使用 FBXLoader 來加載恭喜發財,歲歲平安字樣的 3D 文字模型。

          const fbxLoader = new FBXLoader();
          fbxLoader.load(textModel, mesh => {
            mesh.traverse(child => {
              if (child.isMesh) {
                meshes.push(mesh);
                child.castShadow = true;
                child.receiveShadow = true;
                // 調節材質的金屬度、粗糙度、顏色等樣式
                child.material.metalness = .2;
                child.material.roughness = .8;
                child.material.color = new THREE.Color(0x111111);
              }
            });
            mesh.position.set(4, 6, -8);
            mesh.rotation.set(-80, 0, 0);
            mesh.scale.set(.32, .32, .32);
            group.add(mesh);
          });
          

          嗶哩嗶哩 3D 文字生成教程傳送門:iBlender中文版插件 老外教你用漢字中文字體 Font 3D Chinese And Japanese Characters Blender 插件教程

          老虎模型

          老虎模型是 gltf 格式,在使用 GLTFLoader 加載模型的過程中,發現有 bug,loader 無法讀取到模型體積的 total 值,于是使用通用加載器 LoadingManager 來管理模型加載進度。

          const manager = new THREE.LoadingManager();
          manager.onStart = (url, loaded, total) => {};
          manager.onLoad = () => {};
          manager.onProgress = async(url, loaded, total) => {
            if (Math.floor(loaded / total * 100) === 100) {
              this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
            } else {
              this.setState({ loadingProcess: Math.floor(loaded / total * 100) });
            }
          };
          const gltfLoader = new GLTFLoader(manager);
          gltfLoader.load(tigerModel, mesh => {
            mesh.scene.traverse(child => {
              if (child.isMesh) {
                child.castShadow = true;
                child.material.metalness = 0;
                child.material.roughness = .8;
                child.material.transparent = true;
                child.material.side = THREE.DoubleSide;
                child.material.color = new THREE.Color(0xffffff);
              }
            });
            mesh.scene.rotation.y = Math.PI * 9 / 8;
            mesh.scene.position.set(0, -4, 2);
            mesh.scene.scale.set(.75, .75, .75);
            //  加載模型自身動畫
            let meshAnimation = mesh.animations[0];
            mixer = new THREE.AnimationMixer(mesh.scene);
            let animationClip = meshAnimation;
            let clipAction = mixer.clipAction(animationClip).play();
            animationClip = clipAction.getClip();
            group.add(mesh.scene);
            scene.add(group)
          });
          

          LoadingManager 加載器管理器

          它的功能是處理并跟蹤已加載和待處理的數據。如果未手動設置加強管理器,則會為加載器創建和使用默認全局實例加載器管理器。一般來說,默認的加載管理器已足夠使用了,但有時候也需要設置單獨的加載器,比如,你想為對象和紋理顯示單獨的加載條時。

          構造方法:LoadingManager(onLoad: Function, onProgress: Function, onError: Function)

          onLoad:可選,所有加載器加載完成后,將調用此函數。
          onProgress:可選,當每個項目完成后,將調用此函數。
          onError:可選,當一個加載器遇到錯誤時,將調用此函數。

          屬性:

          .onStart[Function]:加載開始時被調用。參數: url 被加載的項的url;itemsLoaded 目前已加載項的個數;itemsTotal 總共所需要加載項的個數。此方法默認未定義。
          .onLoad[Function]:所有的項加載完成后將調用此函數。默認情況下,此方法時未定義的,除非在構造函數中進行傳遞。
          .onProgress[Function]:此方法加載每一個項,加載完成時進行調用。參數:url 被加載的項的 url;itemsLoaded 目前已加載項的個數;itemsTotal 總共所需要加載項的個數。默認情況下,此方法時未定義的,除非在構造函數中進行傳遞。
          .onError[Function]:此方法將在任意項加載錯誤時調用。參數:url 所加載出錯誤的項的 url。默認情況下,此方法時未定義的,除非在構造函數中進行傳遞。

          添加鏡頭移動補間動畫

          模型加載完成后,通過結合使用 TWEEN.js 實現相機 移動實現漫游,也就是打開頁面時看到的模型由遠及近逐漸變大的動畫效果。

          const Animations = {
            animateCamera: (camera, controls, newP, newT, time = 2000, callBack) => {
              var tween = new TWEEN.Tween({
                x1: camera.position.x,
                y1: camera.position.y,
                z1: camera.position.z,
                x2: controls.target.x,
                y2: controls.target.y,
                z2: controls.target.z,
              });
              tween.to({
                x1: newP.x,
                y1: newP.y,
                z1: newP.z,
                x2: newT.x,
                y2: newT.y,
                z2: newT.z,
              }, time);
              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 () {
                controls.enabled = true;
                callBack();
              });
              tween.easing(TWEEN.Easing.Cubic.InOut);
              tween.start();
            },
          }
          export default Animations;
          
          

          調用示例:

          Animations.animateCamera(camera, controls, { x: 0, y: 5, z: 21 }, { x: 0, y: 0, z: 0 }, 2400, () => {});
          

          TWEEN.js

          是一個補間動畫庫,可以實現很多動畫效果。它使一個對象在一定時間內從一個狀態緩動變化到另外一個狀態。TWEEN.js 本質就是一系列緩動函數算法,結合Canvas、Three.js 很簡單就能實現很多效果。

          基本使用:

          var tween = new TWEEN.Tween({x: 1})     // position: {x: 1}
          .delay(100)                             // 等待100ms
          .to({x: 200}, 1000)                     // 1s時間,x到200
          .onUpdate(render)                       // 變更期間執行render方法
          .onComplete(() => {})                   // 動畫完成
          .onStop(() => {})                       // 動畫停止
          .start();                               // 開啟動畫
          
          

          要讓動畫真正動起來,需要在 requestAnimationFrame 中調用 update 方法。

          TWEEN.update()

          緩動類型:

          TWEEN.js 最強大的地方在于提供了很多常用的緩動動畫類型,由 api easing() 指定。如示例中用到的:

          tween.easing(TWEEN.Easing.Cubic.InOut);

          鏈式調用:

          TWEEN.js 支持鏈式調用,如在 動畫A 結束后要執行 動畫B,可以這樣 tweenA.chain(tweenB) 利用鏈式調用創建往復來回循環的動畫:

          var tweenA = new TWEEN.Tween(position).to({x: 200}, 1000);
          var tweenB = new TWEEN.Tween(position).to({x: 0}, 1000);
          tweenA.chain(tweenB);
          tweenB.chain(tweenA);
          tweenA.start();
          

          控制器緩動移動

          controls.enableDamping 設置為true 可以開啟鼠標移動場景時的緩動效果,產生運動慣性,開啟后 3D 更具真實感。

          controls = new OrbitControls(camera, renderer.domElement);
          controls.target.set(0, 0, 0);
          controls.enableDamping = true;
          controls.maxDistance = 160;
          
           THREE.OrbitControls 參數控制一覽
          //鼠標控制是否可用
          controls.enabled = true;
          //聚焦坐標
          controls.target = new THREE.Vector3();
          //最大最小相機移動距離(PerspectiveCamera 景深相機)
          controls.minDistance = 0;
          controls.maxDistance = Infinity;
          //最大最小鼠標縮放大小(OrthographicCamera正交相機)
          controls.minZoom = 0;
          controls.maxZoom = Infinity;
          //最大仰視角和俯視角,范圍是0到Math.PI
          controls.minPolarAngle = 0;
          controls.maxPolarAngle = Math.PI;
          //水平方向視角限制,范圍[-Math.PI, Math.PI]
          controls.minAzimuthAngle = - Infinity;
          controls.maxAzimuthAngle = Infinity;
          //慣性滑動,滑動大小默認0.25,若開啟,那么controls.update()需要加到動畫循環函數中
          controls.enableDamping = false;
          controls.dampingFactor = 0.25;
          //滾輪是否可控制zoom,zoom速度默認1
          controls.enableZoom = true;
          controls.zoomSpeed = 1.0;
          //是否可旋轉,旋轉速度
          controls.enableRotate = true;
          controls.rotateSpeed = 1.0;
          //是否可平移,默認移動速度為7px
          controls.enablePan = true;
          // 點擊箭頭鍵時移動的像素值
          controls.keyPanSpeed = 7.0;
          //是否自動旋轉,自動旋轉速度。默認每秒30圈,如果是enabled,那么controls.update()需要加到動畫循環函數中
          controls.autoRotate = false;
          // 當fps為60時每轉30s
          controls.autoRotateSpeed = 2.0;
          //是否能使用鍵盤
          controls.enableKeys = true;
          //默認鍵盤控制上下左右的鍵
          controls.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
          //鼠標點擊按鈕
          controls.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
          

          最后不要忘記添加窗口縮放適配方法和 requestAnimationFrame 更新方法。

          function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
          }
          function animate() {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
            let time = clock.getDelta();
            // 老虎動畫
            mixer && mixer.update(time);
            // 補間動畫
            TWEEN && TWEEN.update();
            // 控制器
            controls && controls.update();
            // 魔法陣
            cycle && (cycle.rotation.z += .01);
          }
          

          Loading 頁3D文字樣式

          3D 文字樣式主要通過疊加多層 text-shadow 實現的。

          .loading {
            font-size: 64px;
            color: #FFFFFF;
            text-shadow:     0 1px 0 hsl(174,5%,80%),
                             0 2px 0 hsl(174,5%,75%),
                             0 3px 0 hsl(174,5%,70%),
                             0 4px 0 hsl(174,5%,66%),
                             0 5px 0 hsl(174,5%,64%),
                             0 6px 0 hsl(174,5%,62%),
                             0 7px 0 hsl(174,5%,61%),
                             0 8px 0 hsl(174,5%,60%),
                             0 0 5px rgba(0,0,0,.05),
                            0 1px 3px rgba(0,0,0,.2),
                            0 3px 5px rgba(0,0,0,.2),
                           0 5px 10px rgba(0,0,0,.2),
                          0 10px 10px rgba(0,0,0,.2),
                          0 20px 20px rgba(0,0,0,.3);
          }
          

          效果

          最終實現效果如下圖所示,大家感興趣可在線預覽,已適配移動端。

          總結

          本文中主要涉及到的知識點包括:

          • Fog 場景霧化
          • ShadowMaterial 陰影材質
          • MeshPhongMaterial 網格材質
          • LoadingManager 加載器管理器
          • TWEEN.js 補間動畫
          • THREE.OrbitControls 參數控制
          • CSS 3D 文字樣式

          附錄

          想了解場景初始化、光照、陰影及其他網格幾何體的相關知識,可閱讀我的其他文章。如果覺得文章對你有幫助,不要忘了 一鍵三連。

          [1]. 使用Three.js實現炫酷的酸性風格3D頁面
          [2]. Three.js 實現臉書元宇宙3D動態Logo
          [3]. Three.js 實現3D全景偵探小游戲
          [4]. 模型來源:sketchfab

          前一直想過用CSS做一個3D足球,前幾天使用CSS畫地球之后,突然就來了靈感。使用相同的思路來完成3D足球,沒有看過請參考一下之前的文字《css-畫一個3D地球 》


          先看效果:


          需求分析:

          足球是由12個五邊形和20個六邊形縫合而成。

          首先,我們需要準備邊長相同的五邊形和六邊形。

          css畫多邊形的方式很多,這里我考慮到后期偏移的時候需要確定多邊形重心的位置。

          使用css的clip-path:polygon屬性來完成多邊形。首先我們畫出六邊形和五邊形的外切圓,來確定外層div的尺寸。

          暫定多邊形邊長為100:則可以算出,六邊形外層div寬度為200;五邊形外層div為170。

          并且計算出多邊形的每個頂點,在外切圓上的位置。(發揮你初中水平的時候到了)

          .polygon {
          	border-radius: 50%;
          }
          .polygon::after {
          	position: relative;
          	content: '';
          	display: block;
          	width: 100%;
          	height: 100%;
          }
          .polygon6 {
          	width: 200px;
          	height: 200px;
          	background: rgba(255, 0, 0, .6);
          }
          .polygon6::after {
          	background: #FFF;
          	clip-path: polygon(0 50%, 25% 6.7%, 75% 6.7%, 100% 50%, 75% 93.3%, 25% 93.3%);
          }
          .polygon5 {
          	width: 170px;
          	height: 170px;
          	background: rgba(0, 255, 0, .6);
          }
          .polygon5::after {
          	background: #000;
          	clip-path: polygon(50% 0, 97.55% 34.55%, 79.39% 90.45%, 20.6% 90.45%, 2.45% 34.55%);
          }

          第二步:我們需要將按規律將五邊形和六邊形貼到球形的表面

          首先,我們要先算出來球的半徑,在足球赤道上,其實是10個六邊形的五條邊和五條中線的連線。

          我們大致推算出足球的赤道周長約為1500(沒有計算直線和弧形的誤差,此處我們不需要做到如此精細)

          以此計算出,球的半徑約為235左右。

          我們從足球的北極開始到足球的南極,依次排布:1個五邊形、5個六邊形、5個五邊形、5個六邊形、5個六邊形、5個五邊形、5個六邊形、1個五邊形。

          /**
          * deg 緯度
          * len 圖形個數
          * className 圖形是5邊形還是6邊形
          * Y軸上偏移角度
          * Z軸上旋轉角度
          */
          function draw(deg, len, className, startDeg = 0, rotateDeg = 0) {
          	var fragment = document.createDocumentFragment()
          	for (var i = 0; i < len; i++) {
          		var div = document.createElement('div')
          		div.className = className
          		div.style.transform = 'rotateY(' + ~~(360 / len * i + startDeg) + 'deg) rotateX(' + deg + 'deg) rotateZ(' + rotateDeg + 'deg) translateZ(238px)'
          		fragment.appendChild(div)
          	}
          	document.querySelector('.box').appendChild(fragment)
          }
          window.onload = function () {
          	draw(-90, 1, 'polygon5')
          	draw(-53, 5, 'polygon6', 36)
          	draw(-28, 5, 'polygon5', 0, 180)
          	draw(-11, 5, 'polygon6', 36)
          	draw(11, 5, 'polygon6')
          	draw(28, 5, 'polygon5', 36, 0)
          	draw(53, 5, 'polygon6', 72)
          	draw(90, 1, 'polygon5')
          }


          簡單加個動畫效果,一個3D的足球就完成了。

          由于是平面拼接而成,所有效果無法像真實足球那樣特別的圓。

          不過一個足球的原理基本解釋清楚了。后期我在考慮將五邊形和六邊形繼續拆分成更小的圖形來讓足球更加的圓潤。


          代碼倉庫地址:

          https://github.com/shb190802/html5

          演示地址:

          https://suohb.com/demo/win/globe3.html


          主站蜘蛛池模板: 老熟女高潮一区二区三区| 无码人妻精品一区二区三区66 | 国产成人AV区一区二区三| 日本一区午夜爱爱| 日韩亚洲AV无码一区二区不卡| 中文字幕人妻AV一区二区| 一区二区三区免费看| 蜜桃无码一区二区三区| 国产99精品一区二区三区免费 | 久久久久女教师免费一区| 99精品国产一区二区三区不卡| 国产精品美女一区二区| 亚洲av午夜福利精品一区| 国产一区二区三区不卡在线观看| 国产丝袜无码一区二区三区视频| 国产精品一区在线麻豆| 亚洲一区二区三区在线观看精品中文| 日本高清一区二区三区| 无码AV中文一区二区三区| 日本美女一区二区三区| 无码精品人妻一区二区三区中| 加勒比精品久久一区二区三区| 91午夜精品亚洲一区二区三区 | 国产成人AV区一区二区三| 国产精品丝袜一区二区三区| 精品一区二区91| 国产激情一区二区三区小说| 亚洲一区二区电影| 亚洲熟妇av一区二区三区| 福利视频一区二区牛牛| 国精产品一区一区三区有限公司| 亚洲AV综合色一区二区三区| 国产一区二区三区夜色| 日本人真淫视频一区二区三区| 国产高清在线精品一区| 91福利国产在线观一区二区| 一区二区乱子伦在线播放| 人成精品视频三区二区一区| 国产在线一区观看| 夜夜高潮夜夜爽夜夜爱爱一区| 亚洲熟妇成人精品一区|