作者:范天缘
https://juejin.cn/post/7293463921729372201
在网上找了很久都没有找到使用Three.js开发3d的免费文章或者免费视频,自己花了一点时间做了一个纯前端的项目demo。 模型[1]都是在网上免费下载的,没有那么精细、美观请见谅。技术栈都是最新的:vue3+vite+typeScript+Three+antv G2
项目源码[2]
此文章只用于想学习three.js的小伙伴做学习用途。
有地面版本
无地面版本
开发各种框架的版本
- "@antv/g2plot": "^2.4.29"
- "@types/three": "^0.150.2"
搭建three场景
引入three.js,先初始化场景,相机,渲染器,光线,轨道控制器。先打印一下three看一下有没有输出,然后再搭建场景等…
"container" id="container">
现在我们就看到了three坐标轴了,接下来我们开始导入模型和天空图盒子
加载gltf模型
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
//5.导入gltf模型
const gltfLoader = new GLTFLoader();
gltfLoader.load('./model/scene.gltf',function(object){
console.log(object)
scene.add(object.scene);
});
加载天空盒子
//1.1 创建天空盒子
const textureCubeLoader = new THREE.CubeTextureLoader();
const textureCube = textureCubeLoader.load([
"../public/img/right.jpg",//右
"../public/img/left.jpg",//左
"../public/img/top.jpg",//上
"../public/img/bottom.jpg",//下
"../public/img/front.jpg",//前
"../public/img/back.jpg",//后
])
scene.background = textureCube;
scene.environment = textureCube;
现在我们可以看到模型和天空盒子了,接下来我们讲如何给three加文字进去以及触发文字事件
加贴图文字
这里我们使用canvas写文字然后转成图片 最后使用three的纹理材质导入到three里面
文字显示到three后,使用监听鼠标的方法,点击了网页触发事件
let canvas = null as any //文字
//创建three文字
const threeText = () => {
//用canvas生成图片
canvas = document.getElementById('canvas');
canvas.width = 300
canvas.height = 300
let ctx = canvas.getContext('2d')
//制作矩形
ctx.fillStyle = "rgba(6,7,80,0.8)";
ctx.fillRect(0,0,80,20);
//设置文字
ctx.fillStyle = "#fff";
ctx.font = 'normal 10pt "楷体"'
ctx.fillText('东方明珠', 12.5, 15)
//生成图片
let url = canvas.toDataURL('image/png');
//将图片放到纹理中
let geoMetry1 = new THREE.PlaneGeometry(30,30);
let texture = new THREE.TextureLoader().load(url);
let material1 = new THREE.MeshBasicMaterial({
map:texture,
side:THREE.DoubleSide,
opacity:1,
transparent:true
})
let rect = new THREE.Mesh(geoMetry1,material1)
rect.position.set(10,1,-13)
scene.add(rect)
}
//触发东方明珠点击事件
const threeTextClick = () =>{
window.addEventListener('click',(event)=>{
console.log(event.clientX)
if(event.clientX > 855 && event.clientX < 1022){
alert("触发了点击事件")
}else{return}
})
}
onMounted(()=>{
threeText()
threeTextClick()
})
我们接下来做一个three动态光圈出来
做一个three动态光圈
let cylinderGeometry = null as any//光圈
//创建光圈
const aperture = () =>{
//创建圆柱
let gemetry = new THREE.CylinderGeometry(1,1,0.2,64);
//加载纹理
let texture = new THREE.TextureLoader().load('../public/img/cheng.png');
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;//每个都重复
texture.repeat.set(1,1);
texture.needsUpdate = true;
let material = [
//圆柱侧面材质,使用纹理贴图
new THREE.MeshBasicMaterial({
map:texture,
side:THREE.DoubleSide,
transparent:true
}),
//圆柱顶材质
new THREE.MeshBasicMaterial({
transparent:true,
opacity:0,
side:THREE.DoubleSide
}),
//圆柱顶材质
new THREE.MeshBasicMaterial({
transparent:true,
opacity:0,
side:THREE.DoubleSide
})
];
cylinderGeometry = new THREE.Mesh(gemetry,material);
cylinderGeometry.position.set(0,-0.2,1);
scene.add(cylinderGeometry);
}
onMounted(()=>{
aperture()
})
让几何体(光圈)动起来,这个动态方法要放在animate方法里面
let cylinderRadius = 0;
let cylinderOpacity = 1;
//圆柱光圈扩散动画
const cylinderAnimate = () => {
cylinderRadius += 0.01;
cylinderOpacity -= 0.003;
if (cylinderRadius > 1.6) {
cylinderRadius = 0;
cylinderOpacity = 1;
}
if (cylinderGeometry) {
cylinderGeometry.scale.set(1 + cylinderRadius, 1, 1 + cylinderRadius); //圆柱半径增大
cylinderGeometry.material[0].opacity = cylinderOpacity; //圆柱可见度减小
}
}
const animate = () =>{
cylinderAnimate()
requestAnimationFrame(animate);
renderer.render(scene,camera);
}
这样光圈就开始动起来了,3d部分就讲完了,接下来就是图表和页面样式
图标和头部
"container" id="container">
效果如下:
大致结构我们搭建好了,接下来的步骤
- 1.我们做几个antv的组件柱状图、条形图、折线图的组件
- 2.然后引入到我们刚刚创建好的app.vue 的 div 里面去
在views文件夹里面创建如下图的文件夹:
然后在app.vue引入组件
"container" id="container">
创建一个地面
素材:
图片放在public文件夹
以下请加在加载gltf模型的前面
//4.创建地面 const groundTexture = new THREE.TextureLoader().load("./2.png");
groundTexture.wrapS = groundTexture.wrapT = THREE.RepeatWrapping;
groundTexture.repeat.set(100, 100);
const ground = new THREE.CircleGeometry(500, 100);
const groundMaterial = new THREE.MeshLambertMaterial({ side: THREE.DoubleSide, map: groundTexture, });
const groundMesh = new THREE.Mesh(ground, groundMaterial);
groundMesh.name = "地面";
groundMesh.rotateX(-Math.PI / 2);
groundMesh.position.set(0, -0.345, 1);
scene.add(groundMesh);
最终效果:
参考资料
[1]模型提取码1234: https://pan.baidu.com/share/init?surl=07PKQEfDZfmsBN_aKbPcgw
[2]项目源码: https://gitee.com/fantianyuan/wisdom-city