let { scene, camera, renderer } = Utils.init(
{ bgColor: 0xf0f0f0 },
fov: 85,
aspect: document.getElementById('canvas').innerWidth / document.getElementById('canvas').innerHeight,
near: 0.1,
far: 100000
function init (
sceneConfig = {
bgColor: 0xeeeeee
cameraConfig = {
fov: 75,
aspect: document.getElementById('canvas').innerWidth / document.getElementById('canvas').innerHeight,
near: 0.1,
far: 1000
rendererConfig = {
canvas: document.getElementById('canvas')
const scene = new THREE.Scene();
scene.background = new THREE.Color(sceneConfig.bgColor);
const camera = new THREE.PerspectiveCamera( cameraConfig.fov, cameraConfig.aspect, cameraConfig.near, cameraConfig.far );
const renderer = new THREE.WebGLRenderer(rendererConfig);
return { scene, camera, renderer };
camera.position.set( 0, 250, 1000 );
scene.add( new THREE.AmbientLight( 0xf0f0f0 ) );
const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
let transformControl = new TransformControls( camera, renderer.domElement );
transformControl.addEventListener( 'dragging-changed', ( event ) => {
controls.enabled = !event.value;
} );
scene.add( transformControl )
- 添加一个底座平面,并且在这个底座上添加一些网格,以便标点参考位置
const planeGeometry = new THREE.PlaneGeometry( 2000, 2000 );
planeGeometry.rotateX( - Math.PI / 2 );
const planeMaterial = new THREE.MeshBasicMaterial();
const plane = new THREE.Mesh( planeGeometry, planeMaterial );
plane.position.y = -200;
scene.add( plane );
const helper = new THREE.GridHelper( 2000, 100 );
helper.position.y = - 199;
helper.material.opacity = 0.25;
helper.material.transparent = true;
scene.add( helper );
- 添加一个立方体在(0, 0, 0),给标点做参考物体
const geometry = new THREE.BoxGeometry(50, 50, 50);
const material = new THREE.MeshPhongMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
cube.position.set(0, 0, 0);
scene.add( cube );
let objArr = []
const map = new THREE.TextureLoader().load( "/public/icon.png" );
const spriteMaterial = new THREE.SpriteMaterial( { map: map, sizeAttenuation: false } );
const spriteMaterial2 = new THREE.SpriteMaterial( { map: map, sizeAttenuation: false, depthTest: false, opacity: 0.2 } );
function createMarker (m) {
return new THREE.Sprite( m );
function createMarkerCon() {
let sprite1 = createMarker(spriteMaterial)
let sprite2 = createMarker(spriteMaterial2)
sprite1.add(sprite2)
sprite1.scale.set(0.1, 0.1, 0.1);
sprite1.position.set(100, 100, 0);
sprite1.isMarker = true;
scene.add(sprite1);
objArr.push(sprite1);
createMarkerCon()
let getPosition = () => {
for (let i = 0; i < objArr.length; i++) {
let p = new THREE.Vector3();
objArr[i].getWorldPosition(p);
console.log('--------------- -- ');
console.log('marker - ', i);
console.log(p);
alert('位置信息已在控制台输出');
- 在html里面添加两个按钮,添加标点的按钮 和 获取标点位置的按钮,给两个按钮添加点击事件
.btn {
position: absolute;
top: 25px;
right: 25px;
background-color: #000;
padding: 5px;
color: #fff;
cursor: pointer;
border-radius: 5px;
.btn-2 { top: 60px; }
.btn-3 { top: 95px; }
...一些其他代码
<div class="btn" id="add">Add Popup Marker</div>
<div class="btn btn-2" id="get">Get Position</div>
...一些其他代码
let add = document.getElementById('add');
let get = document.getElementById('get');
add.onclick = createMarkerCon;
get.onclick = getPosition;
- 给标点添加鼠标的点击、拖拽等事件,以便能利用鼠标对标点位置进行调整。在threejs的视图里面不进行一些转换,是没办法监听模型的事件的,要利用Raycaster来计算焦点,获取哪个模型与射线相交,从而让模型触发事件
let raycaster = new THREE.Raycaster();
let mouse = new THREE.Vector2();
let onDownPosition = new THREE.Vector2();
let onUpPosition = new THREE.Vector2();
let onPointermove = ( event ) => {
mouse = Utils.onTransitionMouseXYZ(event, renderer.domElement);
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects(objArr);
if ( intersects.length > 0 ) {
const object = intersects[ 0 ].object;
if ( object !== transformControl.object ) {
transformControl.attach( object );
let onPointerdown = ( event ) => {
onDownPosition.x = event.clientX;
onDownPosition.y = event.clientY;
let onPointerup = ( event ) => {
onUpPosition.x = event.clientX;
onUpPosition.y = event.clientY;
if ( onDownPosition.distanceTo( onUpPosition ) === 0 ) {
transformControl.detach();
onClick(event)
let onClick = (event) => {
let mouse = Utils.onTransitionMouseXYZ(event, renderer.domElement);
raycaster.setFromCamera( mouse, camera );
let intersects = raycaster.intersectObjects(objArr);
if ( intersects.length > 0 ) {
const object = intersects[ 0 ].object;
if (object.isMarker) {
let info = document.getElementById('info');
info.style = 'display: inline-block;top: ' + (event.clientY - 50) + 'px;left: ' + (event.clientX + 50) + 'px;'
let body = document.getElementById('html');
setTimeout(() => {
body.scrollTop = 1;
body.scrollLeft = 1;
if (body.scrollTop) {
info.style.top = event.clientY - info.clientHeight + 50 + 'px';
if (event.clientY < info.clientHeight) {
let { num2 } = numLow10(event.clientX, info.clientWidth)
info.style.height = num2 + 100 + 'px';
info.style.top = event.clientX - num2 + 'px';
if (body.scrollLeft) {
info.style.left = event.clientX - info.clientWidth - 50 + 'px';
if (event.clientX < info.clientWidth) {
let { num2 } = numLow10(event.clientX, info.clientWidth)
info.style.width = num2 - 100 + 'px';
info.style.left = event.clientX - num2 + 'px';
}, 10)
function numLow10 (num1, num2) {
console.log(num1, num2)
if (num1 < num2)
return numLow10(num1, num2 - 10)
return { num1, num2 }
} else {
info.style = 'display: none';
window.addEventListener( 'pointermove', onPointermove, false );
window.addEventListener( 'pointerdown', onPointerdown, false );
window.addEventListener( 'pointerup', onPointerup, false );
function onTransitionMouseXYZ( event, domElement ) {
let mouse = new THREE.Vector2();
let domElementLeft = domElement.getBoundingClientRect().left
let domElementTop = domElement.getBoundingClientRect().top
mouse.x = ((event.clientX - domElementLeft) / domElement.clientWidth) * 2 - 1
mouse.y = -((event.clientY - domElementTop) / domElement.clientHeight) * 2 + 1
return mouse;
- 最后,虽然上面添加了很多东西,但是还是缺少很重要的一步,现在页面是看不到效果的,因为还没有利用初始化好的
renderer
对象来把相机和场景渲染到视图上。现在来完成这一步
function animate() {
requestAnimationFrame( animate );
Utils.updateCameraAspect(renderer, camera);
renderer.render( scene, camera );
animate();
function updateCameraAspect (renderer, camera) {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
function resizeRendererToDisplaySize (renderer) {
const canvas = renderer.domElement;
const pixelRatio = window.devicePixelRatio;
const width = canvas.clientWidth * pixelRatio | 0;
const height = canvas.clientHeight * pixelRatio | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
return needResize;
什么是Three.js?百度百科上是这么说的:Three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能。运行在浏览器中的 3D 引擎,你可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象。你可以在它的主页上看到许多精彩的演示。不过,这款引擎还处在比较不成熟的开发阶段,其不够丰富的 API 以及匮乏的文档增加了初学者的学习难度(尤其是文档的匮乏)three.js的代码托管在github上面。一些有用的链接Three.js的基本概念:https://
在很多的实际的项目中,你可能需要给一个Three.js的模型添加标签,那么我们可以使用three.js的精灵模型来表示,使用精灵模型表示一个模型对象的标签,那么精灵模型就要位于模型对象的附近。可以获得要标注模型的世界坐标,然后来设置精灵标签的位置,适当偏移一点就可以,当然也可以把精灵对象插入到模型对象的父对象中,和模型对象一样作为父对象的子对象,这样的话如果模型父对象的位置变化,精灵模型可以跟着一起变化。如下效果就是使用精灵实现的效果
* 创建点精灵模型
场景查看
一、 创建一个场景
为了真正能够让你的场景借助three.js来进行显示,我们需要以下几个对象:场景、相机和渲染器,这样我们就能透过摄像机渲染出场景。
。。。 花里胡哨 好像也讲不明白 可以直接看官网文档 贼详细 three.js
实现查看一共分为
场景创建
// 场景
const scene = new THREE.Scene();
// 透视相机
const camera = new THREE.PerspectiveCamera(
需要在3d模型上实现标注的功能,一开始是直接通过添加一个普通的mesh来实现的,但是这样就会有一个问题-当视图缩放的时候,标注也会跟着一起放大缩小,影响视觉效果,因此需要实现一个不会随着视图一起放大或者缩小的mesh
根据需求,可以知道我们其实需要实现的就是更改模型渲染的默认方式,而模型的渲染是由模型的MVP三个矩阵来决定的
再进一步分析MVP三个矩阵,Model矩阵决定模型的旋转、缩放和位移,View决定相机的变换, Projection决定模型最终到屏幕的输出。根据需求
下午花了几个小时边学边做,做出来了一个中国地图的倒角模型。其中中国地图的数据是geojson的格式,由于相关法律这里无法提供地图数据。如果想要学习交流使用可以前往github上翻一翻。
使用的工具很单纯:
THREE.js (ver 11.5,主要用了挤压模型和缓冲模型,材质使用的基础半透明材质和线材质)
d3.v5.js (只用到了d3-geo的Mercator变换和其他的坐标变换小工具)...
1. 创建一个场景 (Scene),场景中是你要渲染的对象。
2. 创建一个相机 (Camera),相机观察场景中的对象。
3. 创建一个渲染器 (Renderer),渲染器渲染场景中的对象并将结果显示在浏览器中。
4. 创建一个地球 (SphereGeometry),并设置其材质 (Material) 和纹理 (Texture)。
5. 将地球添加到场景中。
6. 使用相机渲染场景。
这是一个简单的示例代码:
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建地球
const geometry = new THREE.SphereGeometry(5, 32, 32);
const texture = new THREE.TextureLoader().load('earth.jpg');
const material = new THREE.MeshBasicMaterial({ map: texture });
const earth = new THREE.Mesh(geometry, material);
// 将地球添加到场景中
scene.add(earth);
// 使用相机渲染场景
renderer.render(scene, camera);
希望这能帮到你。