three.js流动线

8 篇文章 0 订阅

效果:

先看最基本的

 function initThree(el, options) {
        options = options || {}
        const t = this
        appInstance  = this
        const width = el.offsetWidth
        const height = el.offsetHeight
        const asp = width / height

        // scene
        const scene = new THREE.Scene()

        // camera
        let camera
        if (options.camera) {
            camera = options.camera
        } else {
            camera = new THREE.PerspectiveCamera(45, asp, 1, 100000)
            window.addEventListener('resize', function() {
                camera.aspect = el.offsetWidth / el.offsetHeight
                renderer.setSize(el.offsetWidth, el.offsetHeight) // 重新获取
                camera.updateProjectionMatrix()
                renderer.render(scene, camera)
            }, false)
        }
        camera.position.set(30, 30, 30)

        // renderer
        const 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 蓝z
        if (options.gridHelper) scene.add(new THREE.GridHelper(100, 100))// 网格参考线

        //按序渲染
        renderer.sortObjects = options.sortObjects

        // to the instance
        t.renderer = renderer
        t.scene = scene
        t.camera = camera
        t.el = el
    }

    const el = document.getElementById('box')

    const app = new initThree(el,{
        // gridHelper:true,//网格参考线
        // axes:true,//坐标辅助
        clearColor:'#000'//画布颜色
    })
    const camera = app.camera
    const renderer = app.renderer
    const scene = app.scene
    function 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 light
    const directionalLight = new THREE.DirectionalLight( '#fff' )
    directionalLight.position.set( 30, 30, 30 ).normalize()
    scene.add( directionalLight )
    const ambientLight = new THREE.AmbientLight('#fff',0.3) // obj 唯一 id
    scene.add(ambientLight)
   const pointList1 = [
        [20,5,10],
        [10,5,-9],
        [10,5,20],
        [-40,5,-40]
    ]
    let line1
    textureLoader.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 = this
        appInstance  = this
        const width = el.offsetWidth
        const height = el.offsetHeight
        const asp = width / height

        // scene
        const scene = new THREE.Scene()

        // camera
        let camera
        if (options.camera) {
            camera = options.camera
        } else {
            camera = new THREE.PerspectiveCamera(45, asp, 1, 100000)
            window.addEventListener('resize', function() {
                camera.aspect = el.offsetWidth / el.offsetHeight
                renderer.setSize(el.offsetWidth, el.offsetHeight) // 重新获取
                camera.updateProjectionMatrix()
                renderer.render(scene, camera)
            }, false)
        }
        camera.position.set(30, 30, 30)

        // renderer
        const 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 蓝z
        if (options.gridHelper) scene.add(new THREE.GridHelper(100, 100))// 网格参考线

        //按序渲染
        renderer.sortObjects = options.sortObjects

        // to the instance
        t.renderer = renderer
        t.scene = scene
        t.camera = camera
        t.el = el
    }

    const el = document.getElementById('box')

    const app = new initThree(el,{
        // gridHelper:true,//网格参考线
        // axes:true,//坐标辅助
        clearColor:'#000'//画布颜色
    })
    const camera = app.camera
    const renderer = app.renderer
    const scene = app.scene
    function 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 light
    const directionalLight = new THREE.DirectionalLight( '#fff' )
    directionalLight.position.set( 30, 30, 30 ).normalize()
    scene.add( directionalLight )
    const ambientLight = new THREE.AmbientLight('#fff',0.3) // obj 唯一 id
    scene.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.PI
        const 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 curve
        if (option.kind === 'sphere') { // 由两点之间连线成贝塞尔曲线
            const sphereHeightPointsArgs = option.sphereHeightPointsArgs
            const pointList = this.getSphereHeightPoints(...sphereHeightPointsArgs) // v0,v3,n1,n2,p0
            curve = 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 { // 使用 meshLine
            if (!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 line1
    textureLoader.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 = true
    const 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 line3
    textureLoader.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 = true
    const 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>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值