#はじめに
こんな感じのやつを電子工作で作ってみたくなって、イメージを沸かせるためにjavascriptで書いてみました。
自分用のメモ代わりの記事です。
ちなみに電子工作全くやったことないです。
three.jsを使っています。three.js初めて使いました。
#ソース
書きなぐっています。すいません。
あーコロン動かすの忘れた。まあいいや。おやすみなさい。
<html>
<head>
<meta charset="utf-8" />
<style>
body {
margin: 0;
}
</style>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
<script>
// ページの読み込みを待つ
$(e => {
// サイズを指定
const width = 960 / 2;
const height = 540 / 2;
// レンダラーを作成
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#myCanvas')
});
renderer.setSize(width, height);
// シーンを作成
const scene = new THREE.Scene();
// カメラを作成
const camera = new THREE.PerspectiveCamera(
45,
width / height,
1,
10000
);
camera.position.set(0, 200, 1000);
camera.lookAt(new THREE.Vector3(0, 0, 0));
// カメラを作成
//const camera = new THREE.OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2);
// コンテナーを作成
const container = new THREE.Object3D();
scene.add(container);
// マテリアルを作成
const material = new THREE.MeshStandardMaterial({
color: 0xcccccc,
side: THREE.DoubleSide,
flatShading: true,
});
// 平行光源を作成
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1);
scene.add(directionalLight);
// 環境光を作成
const ambientLight = new THREE.AmbientLight(0x999999);
scene.add(ambientLight);
// アニメーション用の回転角度
let rotXShift = 0.0025;
let rotYShift = 0.01;
const numDefs = [
/* 0 */ [ 0, 1, 2, 4, 5, 6 ],
/* 1 */ [ 2, 5 ],
/* 2 */ [ 0, 2, 3, 4, 6 ],
/* 3 */ [ 0, 2, 3, 5, 6 ],
/* 4 */ [ 1, 2, 3, 5 ],
/* 5 */ [ 0, 1, 3, 5, 6 ],
/* 6 */ [ 0, 1, 3, 4, 5, 6 ],
/* 7 */ [ 0, 2, 5 ],
/* 8 */ [ 0, 1, 2, 3, 4, 5, 6 ],
/* 9 */ [ 0, 1, 2, 3, 5, 6 ],
];
const meshes = [];
createClock();
const shifts = [];
for(let i = 0; i < meshes.length; i += 1) {
shifts.push([ 0, 0, 0, 0, 0, 0, 0 ]);
}
tick();
function updateShifts() {
const time = getCurrentTime();
initShifts();
for(let i = 0; i < shifts.length; i += 1) {
const shift = shifts[i];
const t = parseInt(time[i]);
const def = numDefs[t];
def.forEach(n => {
shift[n] = 1;
});
}
}
function initShifts() {
for(let i = 0; i < shifts.length; i += 1) {
for(let j = 0; j < 7; j += 1) {
shifts[i][j] = -1;
}
}
}
function createClock() {
const a = 20,
b = 60,
c = 100,
s = 5, // 数字の要素の幅
ss = 10, // 数字間の幅
cr = 20, // :(コロン)の半径
cbs = 60; // コロンの上下のスペース
cs = 20; // コロンと数値のスペース
// 時
meshes[0] = createNumber(-a - ss * 0.5
- (a + ss + a + s + a + b + a + s + a + cs + cr * 2 + cs + a + s + a + b + a + s), a, b, c, s, ss);
meshes[1] = createNumber(a + ss * 0.5 + s + 2 * a + b + s
- (a + ss + a + s + a + b + a + s + a + cs + cr * 2 + cs + a + s + a + b + a + s), a, b, c, s, ss);
// 分
meshes[2] = createNumber(-a - ss * 0.5, a, b, c, s, ss);
meshes[3] = createNumber(a + ss * 0.5 + s + 2 * a + b + s, a, b, c, s, ss);
// 秒
meshes[4] = createNumber(-a - ss * 0.5
+ a + ss + a + s + a + b + a + s + a + cs + cr * 2 + cs + a + s + a + b + a + s, a, b, c, s, ss);
meshes[5] = createNumber(a + ss * 0.5 + s + 2 * a + b + s
+ a + ss + a + s + a + b + a + s + a + cs + cr * 2 + cs + a + s + a + b + a + s, a, b, c, s, ss);
// 時と分のコロン
createColon(-a - ss * 0.5, a, b, c, s, ss, cr, cbs, cs);
// 分と秒のコロン
createColon(-a - ss * 0.5
+ cr + cs + a + s + a + b + a + s + a + ss + a + s + a + b + a + s + a + cs + cr , a, b, c, s, ss, cr, cbs, cs);
}
//先頭ゼロ付加
function padZero(num) {
var result;
if (num < 10) {
result = "0" + num;
} else {
result = "" + num;
}
return result;
}
function getCurrentTime() {
var now = new Date();
return padZero(now.getHours()) + padZero(now.getMinutes()) + padZero(now.getSeconds());
}
// コロンを作成
function createColon(shiftX, a, b, c, s, ss, cr, cbs, cs) {
const ret = [];
let geometry, mesh;
geometry = new THREE.CylinderGeometry(cr, cr, c, 8);
mesh = new THREE.Mesh(geometry, material);
mesh.position.x = shiftX - 2 * a - b - s * 2 - a - cs - cr;
mesh.position.y = cbs / 2;
mesh.position.z = 0;
mesh.rotation.x = Math.PI * 0.5;
container.add(mesh);
ret.push(mesh);
geometry = new THREE.CylinderGeometry(cr, cr, c, 8);
mesh = new THREE.Mesh(geometry, material);
mesh.position.x = shiftX - 2 * a - b - s * 2 - a - cs - cr;
mesh.position.y = -cbs / 2;
mesh.position.z = 0;
mesh.rotation.x = Math.PI * 0.5;
container.add(mesh);
ret.push(mesh);
return ret;
}
// 数字を作成する
function createNumber(shiftX, a, b, c, s, ss) {
return [
createHexGeometry({x: shiftX - b * 0.5 - a - s, y: 2 * a + b + s, z: 0 }, a, b, c),
createHexGeometry({x: shiftX - 2 * a - b - s * 2 , y: (2 * a + b + s) * 0.5, z: 0 }, a, b, c, 1),
createHexGeometry({x: shiftX, y: (2 * a + b + s) * 0.5, z: 0 }, a, b, c, 1),
createHexGeometry({x: shiftX - b * 0.5 - a - s, y: 0, z: 0 }, a, b, c),
createHexGeometry({x: shiftX - 2 * a - b - s * 2 , y: -(2 * a + b + s) * 0.5, z: 0 }, a, b, c, 1),
createHexGeometry({x: shiftX, y: -(2 * a + b + s) * 0.5, z: 0 }, a, b, c, 1),
createHexGeometry({x: shiftX - b * 0.5 - a - s, y: -(2 * a + b + s), z: 0 }, a, b, c),
];
}
// 六角柱を作成する
function createHexGeometry(pos, a, b, c, rot) {
// 六角柱の蓋を定義する
const vertices = [
// 上面の蓋
new THREE.Vector3(a + b / 2, 0, c / 2), // #0
new THREE.Vector3(b / 2, a, c / 2), // #1
new THREE.Vector3(-b / 2, a, c / 2), // #2
new THREE.Vector3(-a - b / 2, 0, c / 2), // #3
new THREE.Vector3(-b / 2, -a, c / 2), // #4
new THREE.Vector3(b / 2, -a, c / 2), // #5
new THREE.Vector3(a + b / 2, 0, -c / 2), // #6
new THREE.Vector3(b / 2, a, -c / 2), // #7
new THREE.Vector3(-b / 2, a, -c / 2), // #8
new THREE.Vector3(-a - b / 2, 0, -c / 2), // #9
new THREE.Vector3(-b / 2, -a, -c / 2), // #10
new THREE.Vector3(b / 2, -a, -c / 2), // #11
];
const faces = [
new THREE.Face3(0, 1, 5), // 上面
new THREE.Face3(1, 2, 5), // 上面
new THREE.Face3(2, 4, 5), // 上面
new THREE.Face3(2, 3, 4), // 上面
new THREE.Face3(6, 11, 7), // 下面
new THREE.Face3(7, 11, 8), // 下面
new THREE.Face3(8, 11, 10), // 下面
new THREE.Face3(8, 10, 9), // 下面
new THREE.Face3(0, 6, 7), // 側面
new THREE.Face3(7, 1, 0), // 側面
new THREE.Face3(1, 7, 8), // 側面
new THREE.Face3(8, 2, 1), // 側面
new THREE.Face3(2, 8, 9), // 側面
new THREE.Face3(9, 3, 2), // 側面
new THREE.Face3(3, 9, 10), // 側面
new THREE.Face3(10, 4, 3), // 側面
new THREE.Face3(4, 10, 11), // 側面
new THREE.Face3(11, 5, 4), // 側面
new THREE.Face3(5, 11, 6), // 側面
new THREE.Face3(6, 0, 5), // 側面
];
const geometry = new THREE.Geometry();
vertices.forEach(v => geometry.vertices.push(v));
faces.forEach(f => geometry.faces.push(f));
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = pos.x;
mesh.position.y = pos.y;
mesh.position.z = pos.z;
if(rot) {
mesh.rotation.z = Math.PI * 0.5;
}
container.add(mesh);
return mesh;
}
// 毎フレーム時に実行されるループイベントです
function tick() {
// 移動情報取得
updateShifts();
// 数字を動かす
for(let i = 0; i < meshes.length; i += 1) {
const shift = shifts[i];
for(j = 0; j < 7; j += 1) {
const delta = shift[j];
meshes[i][j].position.z += 5 * delta;
if(meshes[i][j].position.z > 50) {
meshes[i][j].position.z = 50;
} else if(meshes[i][j].position.z < -50) {
meshes[i][j].position.z = -50;
}
}
}
// コンテナを回転させる
container.rotation.x += rotXShift;
container.rotation.y += rotYShift;
if(container.rotation.x >= Math.PI * 0.2
|| container.rotation.x <= -Math.PI * 0.2) {
rotXShift *= -1;
}
if(container.rotation.y >= Math.PI * 0.2 ||
container.rotation.y <= -Math.PI * 0.2) {
rotYShift *= -1;
}
// レンダリング
renderer.render(scene, camera);
requestAnimationFrame(tick);
}
});
</script>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>