此功能借助meshline插件来实现最为简便,初学就暂不探究其原理了。

github地址:   https://github.com/spite/THREE.MeshLine

本文主要学习了跃焱邵隼作者的源代码,但由于作者已将自己的插件一步一步壮大,导致初学时,无法准确切入到核心,因此特别记录一下。

https://www.wellyyss.cn/ysThree/main/app.html

效果

先看最基本的

  function initThree(el, options) {options = options || {}const t = thisappInstance  = thisconst width = el.offsetWidthconst height = el.offsetHeightconst asp = width / height// sceneconst scene = new THREE.Scene()// cameralet cameraif (options.camera) {camera = options.camera} else {camera = new THREE.PerspectiveCamera(45, asp, 1, 100000)window.addEventListener('resize', function() {camera.aspect = el.offsetWidth / el.offsetHeightrenderer.setSize(el.offsetWidth, el.offsetHeight) // 重新获取camera.updateProjectionMatrix()renderer.render(scene, camera)}, false)}camera.position.set(30, 30, 30)// rendererconst renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })renderer.setPixelRatio(window.devicePixelRatio)renderer.setSize(width, height)el.append(renderer.domElement)renderer.setClearColor(options.clearColor || '#000')// 辅助if (options.axes) scene.add(new THREE.AxesHelper(10))// 坐标轴辅助红x 绿y 蓝zif (options.gridHelper) scene.add(new THREE.GridHelper(100, 100))// 网格参考线//按序渲染renderer.sortObjects = options.sortObjects// to the instancet.renderer = renderert.scene = scenet.camera = camerat.el = el}const el = document.getElementById('box')const app = new initThree(el,{// gridHelper:true,//网格参考线// axes:true,//坐标辅助clearColor:'#000'//画布颜色})const camera = app.cameraconst renderer = app.rendererconst scene = app.scenefunction initControls(scene,camera,renderer) {const controls = new THREE.OrbitControls( camera, renderer.domElement );// 如果使用animate方法时,将此函数删除controls.addEventListener( 'change', ()=>{renderer.render( scene, camera );});// // 使动画循环使用时阻尼或自转 意思是否有惯性// controls.enableDamping = true;// //动态阻尼系数 就是鼠标拖拽旋转灵敏度// //controls.dampingFactor = 0.25;// //是否可以缩放// controls.enableZoom = true;// //是否自动旋转// controls.autoRotate = true;// controls.autoRotateSpeed = 0.5;// //设置相机距离原点的最远距离// controls.minDistance  = 1;// //设置相机距离原点的最远距离// controls.maxDistance  = 200;// //是否开启右键拖拽//controls.enablePan = true;return controls}var controls = initControls(scene,camera,renderer)const clock = new THREE.Clock()//add lightconst directionalLight = new THREE.DirectionalLight( '#fff' )directionalLight.position.set( 30, 30, 30 ).normalize()scene.add( directionalLight )const ambientLight = new THREE.AmbientLight('#fff',0.3) // obj 唯一 idscene.add(ambientLight)const pointList1 = [[20,5,10],[10,5,-9],[10,5,20],[-40,5,-40]]let line1textureLoader.load( '../../images/ysThree/green_line.png', function (texture1) {const material1 = new MeshLineMaterial({color: "green",map: texture1,useMap: true,lineWidth: 4,resolution: resolution,dashArray: 0.8,  // 破折号之间的长度和间距。(0 -无破折号)dashRatio: 0.5, // 定义可见和不可见之间的比率(0 -更可见,1 -更不可见)。dashOffset: 0,transparent: true,sizeAttenuation: 1, //使线宽不变,不管距离(1个单位是屏幕上的1px)(0 -衰减,1 -不衰减)side: THREE.FrontSide,depthTest: true,blending: THREE.AdditiveBlending,near: camera.near,far: camera.far,})const l = []pointList1.forEach(e => l.push(new THREE.Vector3(e[0], e[1], e[2])))let curve = new THREE.CatmullRomCurve3(l) // 曲线路径const geo = new THREE.Geometry()geo.vertices = curve.getPoints( 50)console.log(geo)const meshLine = new MeshLine()meshLine.setGeometry(geo)console.log(meshLine.geometry)line1=new THREE.Mesh(meshLine.geometry, material1)scene.add(line1)})function render() {controls.update(clock.getDelta())renderer.render( scene,camera)requestAnimationFrame(render)//if(line1){line1.material.uniforms.dashOffset.value -= 0.01}}render()

由上文可以看到,其实核心就是基于meshline作者提供的插件来完成的。

完成代码如下:

<script src="../../plugins/threeLibrary/three.min.js"></script>
<script src="../../plugins/threeLibrary/js/controls/OrbitControls.js"></script>
<script src="../../plugins/threeLibrary/js/lines/MeshLine.js"></script><script>function initThree(el, options) {options = options || {}const t = thisappInstance  = thisconst width = el.offsetWidthconst height = el.offsetHeightconst asp = width / height// sceneconst scene = new THREE.Scene()// cameralet cameraif (options.camera) {camera = options.camera} else {camera = new THREE.PerspectiveCamera(45, asp, 1, 100000)window.addEventListener('resize', function() {camera.aspect = el.offsetWidth / el.offsetHeightrenderer.setSize(el.offsetWidth, el.offsetHeight) // 重新获取camera.updateProjectionMatrix()renderer.render(scene, camera)}, false)}camera.position.set(30, 30, 30)// rendererconst renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })renderer.setPixelRatio(window.devicePixelRatio)renderer.setSize(width, height)el.append(renderer.domElement)renderer.setClearColor(options.clearColor || '#000')// 辅助if (options.axes) scene.add(new THREE.AxesHelper(10))// 坐标轴辅助红x 绿y 蓝zif (options.gridHelper) scene.add(new THREE.GridHelper(100, 100))// 网格参考线//按序渲染renderer.sortObjects = options.sortObjects// to the instancet.renderer = renderert.scene = scenet.camera = camerat.el = el}const el = document.getElementById('box')const app = new initThree(el,{// gridHelper:true,//网格参考线// axes:true,//坐标辅助clearColor:'#000'//画布颜色})const camera = app.cameraconst renderer = app.rendererconst scene = app.scenefunction initControls(scene,camera,renderer) {const controls = new THREE.OrbitControls( camera, renderer.domElement );// 如果使用animate方法时,将此函数删除controls.addEventListener( 'change', ()=>{renderer.render( scene, camera );});// // 使动画循环使用时阻尼或自转 意思是否有惯性// controls.enableDamping = true;// //动态阻尼系数 就是鼠标拖拽旋转灵敏度// //controls.dampingFactor = 0.25;// //是否可以缩放// controls.enableZoom = true;// //是否自动旋转// controls.autoRotate = true;// controls.autoRotateSpeed = 0.5;// //设置相机距离原点的最远距离// controls.minDistance  = 1;// //设置相机距离原点的最远距离// controls.maxDistance  = 200;// //是否开启右键拖拽//controls.enablePan = true;return controls}var controls = initControls(scene,camera,renderer)const clock = new THREE.Clock()//add lightconst directionalLight = new THREE.DirectionalLight( '#fff' )directionalLight.position.set( 30, 30, 30 ).normalize()scene.add( directionalLight )const ambientLight = new THREE.AmbientLight('#fff',0.3) // obj 唯一 idscene.add(ambientLight)camera.position.set(100,100,100)const resolution = new THREE.Vector2( el.offsetWidth,  el.offsetHeight );const textureLoader = new THREE.TextureLoader()function getSphereHeightPoints (v0, v3, n1, n2, p0) {// 夹角const angle = (v0.angleTo(v3) * 180) / Math.PI / 10 // 0 ~ Math.PIconst aLen = angle * (n1 || 10)const hLen = angle * angle * (n2 || 120)p0 = p0 || new THREE.Vector3(0, 0, 0) // 默认以 坐标原点为参考对象// 法线向量const rayLine = new THREE.Ray(p0, v0.clone().add(v3.clone()).divideScalar(2))// 顶点坐标const vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0))// 计算制高点const getLenVector = (v1, v2, len) => v1.lerp(v2, len / v1.distanceTo(v2))// 控制点坐标return [getLenVector(v0.clone(), vtop, aLen), getLenVector(v3.clone(), vtop, aLen)]}/*  **** **** ****   ****/function createAnimateLine (option) {let curveif (option.kind === 'sphere') { // 由两点之间连线成贝塞尔曲线const sphereHeightPointsArgs = option.sphereHeightPointsArgsconst pointList = this.getSphereHeightPoints(...sphereHeightPointsArgs) // v0,v3,n1,n2,p0curve = new THREE.CubicBezierCurve3(sphereHeightPointsArgs[0], pointList[0], pointList[1], sphereHeightPointsArgs[1])} else { // 由多个点数组构成的曲线 通常用于道路const l = []option.pointList.forEach(e => l.push(new THREE.Vector3(e[0], e[1], e[2])))curve = new THREE.CatmullRomCurve3(l) // 曲线路径}if (option.type === 'pipe') { // 使用管道线// 管道体const tubeGeometry = new THREE.TubeGeometry(curve, option.number || 50, option.radius || 1, option.radialSegments)return new THREE.Mesh(tubeGeometry, option.material)} else { // 使用 meshLineif (!MeshLine || !MeshLineMaterial) console.error('you need import MeshLine & MeshLineMaterial!')else {const geo = new THREE.Geometry()geo.vertices = curve.getPoints(option.number || 50)const meshLine = new MeshLine()meshLine.setGeometry(geo)return new THREE.Mesh(meshLine.geometry, option.material)}}}const pointList1 = [[20,5,10],[10,5,-9],[10,5,20],[-40,5,-40]]let line1textureLoader.load( '../../images/ysThree/green_line.png', function (texture1) {const material1 = new MeshLineMaterial({color: "green",map: texture1,useMap: true,lineWidth: 4,resolution: resolution,dashArray: 0.8,  // 破折号之间的长度和间距。(0 -无破折号)dashRatio: 0.5, // 定义可见和不可见之间的比率(0 -更可见,1 -更不可见)。dashOffset: 0,transparent: true,sizeAttenuation: 1, //使线宽不变,不管距离(1个单位是屏幕上的1px)(0 -衰减,1 -不衰减)side: THREE.FrontSide,depthTest: true,blending: THREE.AdditiveBlending,near: camera.near,far: camera.far,})const l = []pointList1.forEach(e => l.push(new THREE.Vector3(e[0], e[1], e[2])))let curve = new THREE.CatmullRomCurve3(l) // 曲线路径const geo = new THREE.Geometry()geo.vertices = curve.getPoints( 50)console.log(geo)const meshLine = new MeshLine()meshLine.setGeometry(geo)console.log(meshLine.geometry)line1=new THREE.Mesh(meshLine.geometry, material1)scene.add(line1)})/** 2:绘制普通pipeLine**/const pointList2 = [[-20,5,-10],[30,5,-15],[10,5,20],[40,5,40]]const texture2 = textureLoader.load("../../images/ysThree/red_line.png")texture2.wrapS = texture2.wrapT = THREE.RepeatWrapping; //每个都重复texture2.repeat.set(1, 1)const  material2 = new THREE.MeshBasicMaterial({map:texture2,side:THREE.BackSide,transparent:true})texture2.needsUpdate = trueconst line2 = createAnimateLine({// kind: 'sphere',//默认不填 为普通 ; 如为sphere,则表示球面建点type: 'pipe',//默认不填 为MeshLine ; 如为pipe,则表示管道线pointList: pointList2,material: material2,number: 100})scene.add(line2)/** 1:在球面上绘制meshLine**/const v0 =  new THREE.Vector3( -80, 10,  0 )const v3 =  new THREE.Vector3( 80, 10,  0 )let line3textureLoader.load( '../../images/ysThree/green_line.png', function (texture3) {const material3 = new MeshLineMaterial({color: "green",map: texture3,useMap: true,lineWidth: 4,resolution: resolution,dashArray: 0.8,  // 破折号之间的长度和间距。(0 -无破折号)dashRatio: 0.5, // 定义可见和不可见之间的比率(0 -更可见,1 -更不可见)。dashOffset: 0,transparent: true,sizeAttenuation: 1, //使线宽不变,不管距离(1个单位是屏幕上的1px)(0 -衰减,1 -不衰减)side: THREE.FrontSide,depthTest: true,blending: THREE.AdditiveBlending,near: camera.near,far: camera.far,})line3 = createAnimateLine({kind: 'sphere',//默认不填 为普通 ; 如为sphere,则表示球面建点// type: 'pipe',//默认不填 为MeshLine ; 如为pipe,则表示管道线sphereHeightPointsArgs: [v0,v3],material: material3})scene.add(line3)})/** 1:在球面上绘制pipeLine**/const v0_1 =  new THREE.Vector3( -60, 10,  0 )const v3_1 =  new THREE.Vector3( 60, 10,  0 )const texture4 = textureLoader.load("../../images/ysThree/red_line.png")texture4.wrapS = texture4.wrapT = THREE.RepeatWrapping; //每个都重复texture4.repeat.set(1, 1)const  materia4 = new THREE.MeshBasicMaterial({map:texture4,side:THREE.BackSide,transparent:true})texture4.needsUpdate = trueconst line4 = createAnimateLine({kind: 'sphere',//默认不填 为普通 ; 如为sphere,则表示球面建点type: 'pipe',//默认不填 为MeshLine ; 如为pipe,则表示管道线sphereHeightPointsArgs: [v0_1,v3_1],material: materia4,number: 100,radius: 1 // 默认})scene.add(line4)/*  **** **** ****   ****/function render() {controls.update(clock.getDelta())renderer.render( scene,camera)requestAnimationFrame(render)//if(line1){line1.material.uniforms.dashOffset.value -= 0.01}//texture2.offset.x -= 0.01//if(line3){line3.material.uniforms.dashOffset.value -= 0.01}texture4.offset.x -= 0.01}render()
</script>

three.js流动线相关推荐

  1. three.js 流动线shader实现方式(伪)

    原文来至,在原文的基础上,优化了一些逻辑 https://blog.csdn.net/weixin_43842660/article/details/97940152 <!DOCTYPE htm ...

  2. cesium 流动线 发光线(道路线)

    cesium 流动线 发光线 以下为源码,直接复制粘贴就好 第一步创建 polyline.js 文件 import './PolylineCityLinkMaterialProperty.js' ex ...

  3. three.js流动道路

    此前探索过流动线的实现方式,看到另一个案例,学习了另一种实现流动道路的方法 用到了一个开源计算库 https://github.com/yszhao91/xtorcga,实现效果 核心就是基于 cga ...

  4. cesium 流动线(尾迹线)

    cesium 流动线(尾迹线)以下为源码直接复制可用 起步 import "./PolylineWakeLineMaterialProperty"; viewer.entities ...

  5. html js涨幅率怎么计算,JS加速线指标的计算公式及买卖分析

    在趋势指标中,有一种JS加速线指标,这个指标可以帮助我们确定股价运行的快慢.那究竟什么是JS加速线指标?该指标在实战运用中怎么样进行买卖?下面赢家学院的主编老师就来为大家进行相关的介绍. 一.概述 J ...

  6. 【WebGIS】Openlayers流动线与风场效果

    目录 基础开发 一.流动线 二.风场 源码 基础开发 一.流动线 效果展示 基础原理 通过openlayers API,设置线段样式 ol/style/Stroke下的 lineDash 和 line ...

  7. cesium 生成抛物线 流动线

    效果图 代码思路,根据起点终点坐标以及需要设置的抛物线高度生成抛物线坐标串,然后根据坐标串生成cesium实体线,并修改材质为流动线 viewer.entities.removeById('pwxli ...

  8. 发条js调试工具_小工具大帮手,利用 @open-node/antman 实现 node.js 进程线上调试,无须重启...

    @open-node/antman 窥探进程内部,让 Node.js 生产环境线上调试成为可能 解决了什么问题? 日常在开发服务端代码,很多是服务类型的,比如基于http的api,或者一些任务脚本,需 ...

  9. 禅道 bug状态 open_小工具大帮手,利用 @open-node/antman 实现 node.js 进程线上调试,无须重启...

    @open-node/antman 窥探进程内部,让 Node.js 生产环境线上调试成为可能 解决了什么问题? 日常在开发服务端代码,很多是服务类型的,比如基于http的api,或者一些任务脚本,需 ...

最新文章

  1. Java实现Redis分布锁
  2. 玲珑杯 ACM Round #10
  3. AAAI2020录用论文汇总(三)
  4. oracle查找异常中断的sqlid方法,Oracle查找锁定对象以及强制解除锁定的方法
  5. asp.net中RegularExpressionValidator控件中正则表达式用法
  6. 因为apple无法检查其是否包含恶意软件_新Linux恶意脚本——清理其他恶意软件后再感染...
  7. Java中String类的concat方法___java的String字符串的concat()方法连接字符串和“+“连接字符串解释
  8. How to enable nested virtualization in KVM
  9. vue 如何处理两个组件异步问题_Vue异步组件处理路由组件加载状态的解决方案...
  10. 用好SVG格式 让网站速度提升起来
  11. PostgreSQL DELETE 大表性能 explain 测试
  12. Oracle学习之路-- 案例分析实现行列转换的几种方式
  13. 华为OLT(MA5680T)查看光模块信息及光衰
  14. 4.jvm入门到精通
  15. 开发“小米商城官网首页”(静态页面)
  16. 行业寒冬,Android程序员前景一片灰暗?
  17. CTF题库—实验吧(密码学)之奇妙的音乐
  18. Mysql(三)Mysql索引基本原理
  19. 很搞笑,今天才弄清楚什么是二级域名和三级域名的区别
  20. IOS 公司开发者账号申请详细教程-13810208661

热门文章

  1. Office Visio 2007(64位)密钥
  2. 伽马矫正(Gamma correction)
  3. 从《Java特种兵》中感知老A的世界
  4. delphi 快速制作通用LiveUpdate升级程序
  5. 今天除夕夜了 祝大家新年快乐 恭喜发财
  6. draft伦理第三章
  7. 完美Vista纯洁版下载原版Vista Ultimate ghost(完美激活 可以通过正版验证)
  8. 如何使用Boxer在Mac上轻松玩经典DOS游戏
  9. 基于Java毕业设计研究生推免系统源码+系统+mysql+lw文档+部署软件
  10. 引见GPG饬令加密文件的措施