ThreeJs绘制手串
ThreeJs绘制手串,先分析手串的结构,一根绳子,多个珠子,因此需要封装一个绘制珠子的方法并循环调用绘制出多个珠子,再封装一个绘制绳子的方法,绘制一根圆形闭合的绳子。另外为了方便动态调整绳子,可以定义变量,包括珠子个数,绳子直径,珠子的直径。
首先需要绘制ThreeJs的场景,之前的章节都有,就不做赘述,初始化场景,相机,灯光,渲染器等,然后开始绘制绳子,为了方便后续修改,我们把绳子的参数,包括绳子的半径,绳子上珠子的个数,颜色等作为一个变量的属性统一定义。
resultData:{bead:[],bracelet:{braceletRadius:12,beadCount:12,braceletColor:'#000000',lineWidth:7,beadRadius:2},}, // 创建连接珠子的线const curve = new THREE.EllipseCurve(0, 0, // 椭圆中心x,y坐标this.resultData.bracelet.braceletRadius, this.resultData.bracelet.braceletRadius, // 椭圆长半轴和短半轴0, 2 * Math.PI // 起始角度和结束角度,设置为0到2π使曲线闭合);const points = curve.getPoints(50); // 获取50个点const geometry = new THREE.BufferGeometry().setFromPoints(points);const material = new THREE.LineBasicMaterial({color: this.resultData.bracelet.braceletColor,linewidth: this.resultData.bracelet.lineWidth,});const line = new THREE.Line(geometry, material);line.name='bracelet'this.scene.add(line);
然后就可以得到一根绳子
随后我们开发绘制珠子的方法,绘制珠子就是绘制一个小的球体,并按照绳子排列,为了方便后续操作,我们也把珠子的直径,珠子的颜色引用变量,方便后期修改
let beadMaterial = new THREE.MeshPhongMaterial({color: this.beadData.beadColor,specular: this.beadData.beadColor }); //前 0xafc0ca :灰色if(this.beadData.beadMaterialName && this.beadData.beadMaterialName !== ""){let texturebracelet = new THREE.TextureLoader().load(this.beadData.beadMaterialUrl, function (texture) {texture.wrapS = texture.wrapT = THREE.RepeatWrapping;texture.repeat.set(1, 1);})beadMaterial.map = texturebracelet}const angle = (i / this.resultData.bracelet.beadCount) * 2 * Math.PI;const x = Math.cos(angle) * this.resultData.bracelet.braceletRadius;const y = Math.sin(angle) * this.resultData.bracelet.braceletRadius;const beadGeometry = new THREE.SphereGeometry(this.resultData.bracelet.beadRadius, 32, 32);const bead = new THREE.Mesh(beadGeometry, beadMaterial);bead.position.set(x, y, 0);bead.name='bead'+ithis.scene.add(bead);
此时就得到一个基础的手串了
然后,我们设置一些输入框,将刚才的参数添加到输入框中,并添加重新绘制按钮,在修改参数后重新绘制手串以按照我们设定的参数进行绘制
<div style="position:absolute;z-index: 100;width:200px; left:30px;top:20px;"><el-form :inline="true" class="demo-form-inline"><el-form-item label="手串半径"><el-input v-model="resultData.bracelet.braceletRadius" style="width:80px;" type="number" placeholder="手串半径" /></el-form-item><el-form-item label="珠子个数" ><el-input v-model="resultData.bracelet.beadCount" style="width:80px;" type="number" placeholder="珠子个数" /></el-form-item><el-form-item label="珠子半径" ><el-input v-model="resultData.bracelet.beadRadius" style="width:80px;" type="number" placeholder="珠子半径" /></el-form-item></el-form><el-button type="primary" @click="reDraw">重新绘制</el-button></div>reDraw(){for (let i = 0; i < this.scene.children.length; i++) {if (this.scene.children[i].name.indexOf("bead") !== -1) {this.scene.remove(this.scene.children[i])i--}}this.initBracelet();},
然后可以得到这样一个效果:
但是一直是白色的柱子显得不够美观,可以再添加动态修改柱子的贴图,之前章节有学过threejs的点击事件,因此可以采用点击的方式给每个柱子着色添加贴图,首先添加一些图片并设置贴图
materialList:[{name:'珍珠',url:'/static/images/material/zhenzhu.png'},{name:'紫水晶',url:'/static/images/material/zishuijing.png'},{name:'和田玉',url:'/static/images/material/yu.jpg'},{name:'水晶',url:'/static/images/material/shuijing.png'}], <div style="position:absolute;;left:30%;top:0;z-index:10"><el-radio-group v-model="material" @input="selectMaterial"><el-radio-button v-for="(item,index) in materialList" :key="index" :label="item.name"><div><el-image style="width: 30px; height: 30px" :src="item.url" fit="contain"></el-image></div><div>{{item.name}}</div></el-radio-button></el-radio-group></div><div id="container"></div>
然后添加点击事件,刚才绘制珠子的时候已经给每个柱子添加了名字,就是bead_+珠子的下标,因此点击的时候就通过这个名字来确认点击到的是哪个珠子
window.addEventListener( 'click', this.clickBead);
clickBead(event){let mouse = new THREE.Vector2();let x, y;if ( event.changedTouches ) {x = event.changedTouches[ 0 ].pageX;y = event.changedTouches[ 0 ].pageY;} else {x = event.clientX;y = event.clientY;}mouse.x = ( x / window.innerWidth ) * 2 - 1;mouse.y = - ( y / window.innerHeight ) * 2 + 1;let raycaster = new THREE.Raycaster();raycaster.setFromCamera( mouse, this.camera );let intersects = raycaster.intersectObjects( [ this.scene ], true );if(intersects.length>0 && intersects[0].object !== null){let tempBeadName = intersects[0].object.name;let exist = truefor (let i = 0; i < this.resultData.bead.length; i++) {if(tempBeadName === this.resultData.bead[i].beadName){this.resultData.bead[i].beadMaterialName = this.selectedMaterial.namethis.resultData.bead[i].beadMaterialUrl = this.selectedMaterial.urlexist = false}}if(exist){let temp = {beadName:tempBeadName,beadRadius:this.defaultBeadRadius,beadMaterialName:this.selectedMaterial.name,beadMaterialUrl:this.selectedMaterial.url,beadColor:"#FFFFFF"}this.resultData.bead.push(temp)}for (let i = 0; i < this.scene.children.length; i++) {if(tempBeadName === this.scene.children[i].name){const textureLoader = new THREE.TextureLoader();const newTexture = textureLoader.load(this.selectedMaterial.url);this.scene.children[i].material.name = this.selectedMaterial.name;this.scene.children[i].material.map = newTexture;this.scene.children[i].material.needsUpdate = true;}}}},
最终效果如下:
threejs绘制手串
以上包含了所有核心代码,所有代码比较多就不贴了,如果需要可以给我留言。