使用three.js的交互式3D手表

Interactive 3D watch using three.js
Interactive 3D watch using three.js

Interactive 3D watch using three.js The goal of our today’s lesson is the continuation of studying webgl using the three.js library. We create interactive (ticking) three-dimensional classical watches that consists of following major elements: round dial, the clock arrows and moving arrows. Watch model will be located in space, so you will easily be able to rotate it in the scene for viewing the watch from any angle.

使用three.js进行交互式3D观看本课的目的是继续使用three.js库学习webgl。 我们创建了互动(打折)三维古典手表,其中包括以下主要元素:圆形表盘,时钟箭头和移动箭头。 手表模型将位于太空中,因此您可以轻松地在场景中旋转它以从任何角度观看手表。

现场演示

HTML (HTML)

It has become common practice that we begin with the most simple – html markup:

从最简单的html标记开始,这已成为一种常见的做法:

index.html (index.html)

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <meta name="author" content="Script Tutorials" />
        <title>Interactive 3D watch using three.js | Script Tutorials</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <link href="css/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <script src="js/three.min.js"></script>
        <script src="js/THREEx.WindowResize.js"></script>
        <script src="js/OrbitControls.js"></script>
        <script src="js/stats.min.js"></script>
        <script src="js/script.js"></script>
        <div style="position: absolute; top: 10px; left: 20px; text-align: center;"><a href="https://www.script-tutorials.com/interactive-3d-watch-using-three-js/" target="_blank">"Interactive 3D watch using three.js"</a><br>Drag to spin</div>
    </body>
</html>

<!DOCTYPE html>
<html lang="en" >
    <head>
        <meta charset="utf-8" />
        <meta name="author" content="Script Tutorials" />
        <title>Interactive 3D watch using three.js | Script Tutorials</title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
        <link href="css/main.css" rel="stylesheet" type="text/css" />
    </head>
    <body>
        <script src="js/three.min.js"></script>
        <script src="js/THREEx.WindowResize.js"></script>
        <script src="js/OrbitControls.js"></script>
        <script src="js/stats.min.js"></script>
        <script src="js/script.js"></script>
        <div style="position: absolute; top: 10px; left: 20px; text-align: center;"><a href="https://www.script-tutorials.com/interactive-3d-watch-using-three-js/" target="_blank">"Interactive 3D watch using three.js"</a><br>Drag to spin</div>
    </body>
</html>

There is nothing complicated – we only need to connect all the required libraries.

没什么复杂的-我们只需要连接所有必需的库即可。

Java脚本 (Javascript)

Now we begin to implement our new webgl scene. First, let’s prepare the frame of the scene: add all variables, the scene, camera, renderer, controls and stats object:

现在,我们开始实施新的webgl场景。 首先,让我们准备场景的框架:添加所有变量,场景,摄像机,渲染器,控件和stats对象:

js / script.js (js/script.js)

var watch = {
    scene: null,
    camera: null,
    renderer: null,
    container: null,
    controls: null,
    clock: null,
    stats: null,
    arrowHr: null,
    arrowMin: null,
    arrowSec: null,
    timeHr: null,
    timeMin: null,
    timeSec: null,
    init: function() { // initialization
        // create main scene
        this.scene = new THREE.Scene();
        var SCREEN_WIDTH = window.innerWidth,
            SCREEN_HEIGHT = window.innerHeight;
        // prepare camera
        var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 5000;
        this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
        this.scene.add(this.camera);
        this.camera.position.set(0, 1500, 500);
        this.camera.lookAt(new THREE.Vector3(0,0,0));
        // prepare renderer
        this.renderer = new THREE.WebGLRenderer({antialias:true, alpha: false});
        this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
        this.renderer.setClearColor(0xffffff);
        this.renderer.shadowMapEnabled = true;
        this.renderer.shadowMapSoft = true;
        // prepare container
        this.container = document.createElement('div');
        document.body.appendChild(this.container);
        this.container.appendChild(this.renderer.domElement);
        // events
        THREEx.WindowResize(this.renderer, this.camera);
        // prepare controls (OrbitControls)
        this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
        this.controls.target = new THREE.Vector3(0, 0, 0);
        // prepare clock
        this.clock = new THREE.Clock();
        // prepare stats
        this.stats = new Stats();
        this.stats.domElement.style.position = 'absolute';
        this.stats.domElement.style.bottom = '0px';
        this.stats.domElement.style.zIndex = 10;
        this.container.appendChild( this.stats.domElement );
    }
};
// Animate the scene
function animate() {
    requestAnimationFrame(animate);
    render();
    update();
}
// Update controls and stats
function update() {
    watch.controls.update(watch.clock.getDelta());
    watch.stats.update();
}
// Render the scene
function render() {
    if (watch.renderer) {
        watch.renderer.render(watch.scene, watch.camera);
    }
}
// Initialize lesson on page load
function initializeLesson() {
    watch.init();
    animate();
}
if (window.addEventListener)
    window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
    window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;

var watch = {
    scene: null,
    camera: null,
    renderer: null,
    container: null,
    controls: null,
    clock: null,
    stats: null,
    arrowHr: null,
    arrowMin: null,
    arrowSec: null,
    timeHr: null,
    timeMin: null,
    timeSec: null,
    init: function() { // initialization
        // create main scene
        this.scene = new THREE.Scene();
        var SCREEN_WIDTH = window.innerWidth,
            SCREEN_HEIGHT = window.innerHeight;
        // prepare camera
        var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 5000;
        this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR);
        this.scene.add(this.camera);
        this.camera.position.set(0, 1500, 500);
        this.camera.lookAt(new THREE.Vector3(0,0,0));
        // prepare renderer
        this.renderer = new THREE.WebGLRenderer({antialias:true, alpha: false});
        this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
        this.renderer.setClearColor(0xffffff);
        this.renderer.shadowMapEnabled = true;
        this.renderer.shadowMapSoft = true;
        // prepare container
        this.container = document.createElement('div');
        document.body.appendChild(this.container);
        this.container.appendChild(this.renderer.domElement);
        // events
        THREEx.WindowResize(this.renderer, this.camera);
        // prepare controls (OrbitControls)
        this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement);
        this.controls.target = new THREE.Vector3(0, 0, 0);
        // prepare clock
        this.clock = new THREE.Clock();
        // prepare stats
        this.stats = new Stats();
        this.stats.domElement.style.position = 'absolute';
        this.stats.domElement.style.bottom = '0px';
        this.stats.domElement.style.zIndex = 10;
        this.container.appendChild( this.stats.domElement );
    }
};
// Animate the scene
function animate() {
    requestAnimationFrame(animate);
    render();
    update();
}
// Update controls and stats
function update() {
    watch.controls.update(watch.clock.getDelta());
    watch.stats.update();
}
// Render the scene
function render() {
    if (watch.renderer) {
        watch.renderer.render(watch.scene, watch.camera);
    }
}
// Initialize lesson on page load
function initializeLesson() {
    watch.init();
    animate();
}
if (window.addEventListener)
    window.addEventListener('load', initializeLesson, false);
else if (window.attachEvent)
    window.attachEvent('onload', initializeLesson);
else window.onload = initializeLesson;

Now we begin to create watch elements. first we’ll create the dial and rim shapes. We need to add the following code in the end of the ‘init’ function:

现在我们开始创建监视元素。 首先,我们将创建表盘和轮辋形状。 我们需要在“ init”函数的末尾添加以下代码:


  // add dial shape
  var dialMesh = new THREE.Mesh(
      new THREE.CircleGeometry(500, 50),
      new THREE.MeshBasicMaterial({ color:0xffffff * Math.random(), side: THREE.DoubleSide })
  );
  dialMesh.rotation.x = - Math.PI / 2;
  dialMesh.position.y = 0;
  this.scene.add(dialMesh);
  // add watch rim shape
  var rimMesh = new THREE.Mesh(
    new THREE.TorusGeometry(500, 20, 10, 100),
    new THREE.MeshBasicMaterial({ color:0xffffff * Math.random() })
  );
  rimMesh.rotation.x = - Math.PI / 2;
  this.scene.add(rimMesh);

  // add dial shape
  var dialMesh = new THREE.Mesh(
      new THREE.CircleGeometry(500, 50),
      new THREE.MeshBasicMaterial({ color:0xffffff * Math.random(), side: THREE.DoubleSide })
  );
  dialMesh.rotation.x = - Math.PI / 2;
  dialMesh.position.y = 0;
  this.scene.add(dialMesh);
  // add watch rim shape
  var rimMesh = new THREE.Mesh(
    new THREE.TorusGeometry(500, 20, 10, 100),
    new THREE.MeshBasicMaterial({ color:0xffffff * Math.random() })
  );
  rimMesh.rotation.x = - Math.PI / 2;
  this.scene.add(rimMesh);

Now we will create a watch dividers (arrows). Please note, that some arrows are a bit longer (as at real watches):

现在,我们将创建一个手表分隔线(箭头)。 请注意,某些箭头会更长一些(与实际手表一样):


  // add watch arrow
  var iHours = 12;
  var mergedArrows = new THREE.Geometry();
  var extrudeOpts = {amount: 10, steps: 1, bevelSegments: 1, bevelSize: 1, bevelThickness:1};
  var handFrom = 400, handTo = 450;
  for (i = 1; i <= iHours; i++) {
    // prepare each arrow in a circle
    var arrowShape = new THREE.Shape();
    var from = (i % 3 == 0) ? 350 : handFrom;
    var a = i * Math.PI / iHours * 2;
    arrowShape.moveTo(Math.cos(a) * from, Math.sin(a) * from);
    arrowShape.lineTo(Math.cos(a) * from + 5, Math.sin(a) * from + 5);
    arrowShape.lineTo(Math.cos(a) * handTo + 5, Math.sin(a) * handTo + 5);
    arrowShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo);
    var arrowGeom = new THREE.ExtrudeGeometry(arrowShape, extrudeOpts);
    THREE.GeometryUtils.merge(mergedArrows, arrowGeom);
  }
  var arrowsMesh = new THREE.Mesh(mergedArrows, new THREE.MeshBasicMaterial({ color:0x444444 * Math.random() }));
  arrowsMesh.rotation.x = - Math.PI / 2;
  arrowsMesh.position.y = 10;
  this.scene.add(arrowsMesh);

  // add watch arrow
  var iHours = 12;
  var mergedArrows = new THREE.Geometry();
  var extrudeOpts = {amount: 10, steps: 1, bevelSegments: 1, bevelSize: 1, bevelThickness:1};
  var handFrom = 400, handTo = 450;
  for (i = 1; i <= iHours; i++) {
    // prepare each arrow in a circle
    var arrowShape = new THREE.Shape();
    var from = (i % 3 == 0) ? 350 : handFrom;
    var a = i * Math.PI / iHours * 2;
    arrowShape.moveTo(Math.cos(a) * from, Math.sin(a) * from);
    arrowShape.lineTo(Math.cos(a) * from + 5, Math.sin(a) * from + 5);
    arrowShape.lineTo(Math.cos(a) * handTo + 5, Math.sin(a) * handTo + 5);
    arrowShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo);
    var arrowGeom = new THREE.ExtrudeGeometry(arrowShape, extrudeOpts);
    THREE.GeometryUtils.merge(mergedArrows, arrowGeom);
  }
  var arrowsMesh = new THREE.Mesh(mergedArrows, new THREE.MeshBasicMaterial({ color:0x444444 * Math.random() }));
  arrowsMesh.rotation.x = - Math.PI / 2;
  arrowsMesh.position.y = 10;
  this.scene.add(arrowsMesh);

All arrow geometries (ExtrudeGeometry) were merged (for better performance) into a single geometry (mergedArrows) using the ‘THREE.GeometryUtils.merge’ function. Finally, we will create three more arrows to point seconds, minutes and hours:

使用“ THREE.GeometryUtils.merge”功能将所有箭头几何形状(ExtrudeGeometry)合并(以获得更好的性能)为单个几何形状(mergedArrows)。 最后,我们将再创建三个箭头来指示秒,分钟和小时:


  // add seconds arrow
  handTo = 350;
  var arrowSecShape = new THREE.Shape();
  arrowSecShape.moveTo(-50, -5);
  arrowSecShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo);
  arrowSecShape.lineTo(-50, 5);
  var arrowSecGeom = new THREE.ExtrudeGeometry(arrowSecShape, extrudeOpts);
  this.arrowSec = new THREE.Mesh(arrowSecGeom, new THREE.MeshBasicMaterial({ color:0x000000 }));
  this.arrowSec.rotation.x = - Math.PI / 2;
  this.arrowSec.position.y = 20;
  this.scene.add(this.arrowSec);
  // add minutes arrow
  var arrowMinShape = new THREE.Shape();
  arrowMinShape.moveTo(0, -5);
  arrowMinShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo - 5);
  arrowMinShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo + 5);
  arrowMinShape.lineTo(0, 5);
  var arrowMinGeom = new THREE.ExtrudeGeometry(arrowMinShape, extrudeOpts);
  this.arrowMin = new THREE.Mesh(arrowMinGeom, new THREE.MeshBasicMaterial({ color:0x000000 }));
  this.arrowMin.rotation.x = - Math.PI / 2;
  this.arrowMin.position.y = 20;
  this.scene.add(this.arrowMin);
  // add hours arrow
  handTo = 300;
  var arrowHrShape = new THREE.Shape();
  arrowHrShape.moveTo(0, -5);
  arrowHrShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo - 5);
  arrowHrShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo + 5);
  arrowHrShape.lineTo(0, 5);
  var arrowHrGeom = new THREE.ExtrudeGeometry(arrowHrShape, extrudeOpts);
  this.arrowHr = new THREE.Mesh(arrowHrGeom, new THREE.MeshBasicMaterial({ color:0x000000 }));
  this.arrowHr.rotation.x = - Math.PI / 2;
  this.arrowHr.position.y = 20;
  this.scene.add(this.arrowHr);

  // add seconds arrow
  handTo = 350;
  var arrowSecShape = new THREE.Shape();
  arrowSecShape.moveTo(-50, -5);
  arrowSecShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo);
  arrowSecShape.lineTo(-50, 5);
  var arrowSecGeom = new THREE.ExtrudeGeometry(arrowSecShape, extrudeOpts);
  this.arrowSec = new THREE.Mesh(arrowSecGeom, new THREE.MeshBasicMaterial({ color:0x000000 }));
  this.arrowSec.rotation.x = - Math.PI / 2;
  this.arrowSec.position.y = 20;
  this.scene.add(this.arrowSec);
  // add minutes arrow
  var arrowMinShape = new THREE.Shape();
  arrowMinShape.moveTo(0, -5);
  arrowMinShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo - 5);
  arrowMinShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo + 5);
  arrowMinShape.lineTo(0, 5);
  var arrowMinGeom = new THREE.ExtrudeGeometry(arrowMinShape, extrudeOpts);
  this.arrowMin = new THREE.Mesh(arrowMinGeom, new THREE.MeshBasicMaterial({ color:0x000000 }));
  this.arrowMin.rotation.x = - Math.PI / 2;
  this.arrowMin.position.y = 20;
  this.scene.add(this.arrowMin);
  // add hours arrow
  handTo = 300;
  var arrowHrShape = new THREE.Shape();
  arrowHrShape.moveTo(0, -5);
  arrowHrShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo - 5);
  arrowHrShape.lineTo(Math.cos(a) * handTo, Math.sin(a) * handTo + 5);
  arrowHrShape.lineTo(0, 5);
  var arrowHrGeom = new THREE.ExtrudeGeometry(arrowHrShape, extrudeOpts);
  this.arrowHr = new THREE.Mesh(arrowHrGeom, new THREE.MeshBasicMaterial({ color:0x000000 }));
  this.arrowHr.rotation.x = - Math.PI / 2;
  this.arrowHr.position.y = 20;
  this.scene.add(this.arrowHr);

All these arrows are extruded from flat shapes.

所有这些箭头都是从扁平形状中挤出的。

Now, the time has come to teach arrows ticking. For this we will need to add the following code to the ‘update’ function:

现在,该教导箭头刻度线了。 为此,我们需要将以下代码添加到“更新”功能中:


  // get current time
  var date = new Date;
  watch.timeSec = date.getSeconds();
  watch.timeMin = date.getMinutes();
  watch.timeHr = date.getHours();
  // update watch arrows positions
  var rotSec = watch.timeSec * 2 * Math.PI / 60 - Math.PI/2;
  watch.arrowSec.rotation.z = -rotSec;
  var rotMin = watch.timeMin * 2 * Math.PI / 60 - Math.PI/2;
  watch.arrowMin.rotation.z = -rotMin;
  var rotHr = watch.timeHr * 2 * Math.PI / 12 - Math.PI/2;
  watch.arrowHr.rotation.z = -rotHr;

  // get current time
  var date = new Date;
  watch.timeSec = date.getSeconds();
  watch.timeMin = date.getMinutes();
  watch.timeHr = date.getHours();
  // update watch arrows positions
  var rotSec = watch.timeSec * 2 * Math.PI / 60 - Math.PI/2;
  watch.arrowSec.rotation.z = -rotSec;
  var rotMin = watch.timeMin * 2 * Math.PI / 60 - Math.PI/2;
  watch.arrowMin.rotation.z = -rotMin;
  var rotHr = watch.timeHr * 2 * Math.PI / 12 - Math.PI/2;
  watch.arrowHr.rotation.z = -rotHr;

现场演示

[sociallocker]

[社交储物柜]

打包下载

[/sociallocker]

[/ sociallocker]

结论 (Conclusion)

Stay tuned for new tutorials and you will always find something new and interesting for yourself.

请继续关注新教程,您将永远为自己找到一些新的有趣的东西。

翻译自: https://www.script-tutorials.com/interactive-3d-watch-using-three-js/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值