【Three.js基础学习】15.scroll-based-animation

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

 课程要点

        结合html等场景 做滚动动画

        1.遇到的问题, 在向下滚动时,下方会显白(部分浏览器)

            解决:alpha:true ,在WebGLRenderer 中设置alpha : true ; 同时在style.css文件中设置html 背景颜色

        2.添加 圆环,锥型,圆环纠结形状

        3.添加材质 卡通 由于 卡通对光 才能看见 因此 加一个定向光

        4.此时 显示的颜色是两种 ,但是根据文档 可以看到有三种色, 因此可以通过 纹理实现

        gradientTexture.magFilter = THREE.NearestFilter

        5.设置位置,同时转动

        6.向下移动网页 更改camera视角

        7. 视差 :通过不同观察点看到一个物体的行为

            希望能有深度 ,在鼠标移动时,相机视角能有适当的强度变化

        实现: 由于滚动 和 移动鼠标都是 移动camera视角 导致 滚动不生效

            1. 创建组 ,在视差时候 移动组, 而camera在组中, 滚动时 移动相机 解决

       

        8.实现平滑 缓慢 移动 速度不要太快    

        9.实现在不同电脑中 不同屏幕频率相同的移动速度

        let previousTime = 0

        const deltaTime = elapsedTime - previousTime

        previousTime = elapsedTime

        10. GSAP


一、代码

import * as THREE from 'three'
import * as dat from 'lil-gui'
import gsap from 'gsap'


/**
 * Debug
 */
const gui = new dat.GUI()

const parameters = {
    materialColor: '#ffeded'
}

gui
    .addColor(parameters, 'materialColor')
    .onChange(()=>{
        material.color.set(parameters.materialColor)
        particleMaterial.color.set(parameters.materialColor)
    })

/**
 * Base
 */
// Canvas
const canvas = document.querySelector('canvas.webgl')

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


/* 
    Objects
*/
// Texture
const textureLoad = new THREE.TextureLoader()
const gradientTexture =  textureLoad.load('/textures/gradients/3.jpg')
gradientTexture.magFilter = THREE.NearestFilter // 设置最近过滤器 牵扯到WebGL原理

// Material
const material = new THREE.MeshToonMaterial({
    color:parameters.materialColor,
    gradientMap:gradientTexture, 
}) // 卡通材质 有光的情况才会出现

//  Meshs
const objectsDistance = 4
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),  // 环形
    material
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32), // 锥
    material
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16), // 环形缓冲
    material
)
// mesh1.position.y = 2
// mesh1.scale.set(0.5,0.5,0.5)

// mesh2.visible = false

// mesh3.position.y = -2
// mesh3.scale.set(0.5,0.5,0.5)

const sectionMeshes = [mesh1,mesh2,mesh3]

mesh1.position.y = - objectsDistance * 0
mesh2.position.y = - objectsDistance * 1
mesh3.position.y = - objectsDistance * 2

mesh1.position.x = 2
mesh2.position.x = -2
mesh3.position.x = 2

scene.add(mesh1,mesh2,mesh3)

/* 
    Particles
*/
// Geometry
const particleCount = 200
const positions = new Float32Array(particleCount * 3)
for(let i = 0; i< particleCount;i++){
    positions[i * 3 + 0] = (Math.random() - 0.5) * 10
    positions[i * 3 + 1] = objectsDistance * 0.5 - Math.random() * objectsDistance * 3
    positions[i * 3 + 2] = (Math.random() - 0.5) * 10
}

const particleGeometry = new THREE.BufferGeometry()
particleGeometry.setAttribute('position',new THREE.BufferAttribute(positions,3))

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.03
particleMaterial.color = new THREE.Color(parameters.materialColor)
particleMaterial.sizeAttenuation = true

const particle = new THREE.Points(
    particleGeometry,
    particleMaterial
)
scene.add(particle)



/* 
    ligths
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',1)
directionalLight.position.set(1,1,0)
scene.add(directionalLight)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})


/* 
    Group
*/
const cameraGroup = new THREE.Group()
scene.add(cameraGroup)

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(35, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 6
cameraGroup.add(camera)

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    alpha:true,
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/* 
    Scroll
*/
let scrollY = window.scrollY
let currentSection = 0
window.addEventListener('scroll',()=>{
    scrollY = window.scrollY

    const newSection = Math.round(scrollY / sizes.height) // 四舍五入判断 几何体旋转的时机
    if(newSection != currentSection){
        currentSection = newSection
        console.log(sectionMeshes[currentSection].rotation)
        gsap.to(
            sectionMeshes[currentSection].rotation, // 设置动画 0,1,2的动画效果
            {
                duration:1.5, // 时间
                ease:'power2.inOut', // 进出
                x:'+=6',
                y:'+=3',
                z:'+=1.5',
            }
        )
    }
})

/* 
    Cursor
*/
const cursor = {}
cursor.x = 0
cursor.y = 0

window.addEventListener('mousemove',(event)=>{
    cursor.x = event.clientX / sizes.width - 0.5
    cursor.y = event.clientY / sizes.height - 0.5
})

/**
 * Animate
 */
const clock = new THREE.Clock()
let previousTime = 0
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()
        
    const deltaTime = elapsedTime - previousTime
    previousTime = elapsedTime

    // Aniamte Camera   移动的距离/窗口的高度 等于一个单位, *  objectsDistance 距离
    camera.position.y = - scrollY / sizes.height * objectsDistance

    const parallaxX = cursor.x * 0.5
    const parallaxY = -cursor.y * 0.5
    cameraGroup.position.x += (parallaxX - cameraGroup.position.x) * 5 * deltaTime
    cameraGroup.position.y += (parallaxY - cameraGroup.position.y) * 5 * deltaTime

    // Aniamte meshes
    for(const mesh of sectionMeshes){ // 每一帧变化时,应该改变
        mesh.rotation.x +=  deltaTime * 0.1
        mesh.rotation.y +=  deltaTime * 0.12
    }


    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

二、知识点

1.原始代码

html代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>20 - Scroll base animation</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <canvas class="webgl"></canvas>

    <section class="section">
        <h1>My Portfolio</h1>
    </section>
    <section class="section">
        <h2>My projects</h2>
    </section>
    <section class="section">
        <h2>Contact me</h2>
    </section>

    <script type="module" src="./script.js"></script>
</body>
</html>

script.js

import * as THREE from 'three'
import * as dat from 'lil-gui'

/**
 * Debug
 */
const gui = new dat.GUI()

const parameters = {
    materialColor: '#ffeded'
}

gui
    .addColor(parameters, 'materialColor')


/**
 * Base
 */
// Canvas
const canvas = document.querySelector('canvas.webgl')

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

/* 
    cube
*/
const cube = new THREE.Mesh(
    new THREE.BoxGeometry(1,1,1),
    new THREE.MeshBasicMaterial({color:'red'})
)
scene.add(cube)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})


/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(35, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 6
// cameraGroup.add(camera)
scene.add(camera)

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))



/**
 * Animate
 */
const clock = new THREE.Clock()
let previousTime = 0
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()


    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

css代码

*
{
    margin: 0;
    padding: 0;
}

/* html,
body
{
    overflow: hidden;
} */

html{
    background: #1e1a20;
}
.webgl
{
    position: fixed;
    top: 0;
    left: 0;
    outline: none;
}


.section
{
    display: flex;
    align-items: center;
    height: 100vh;
    position: relative;
    font-family: 'Cabin', sans-serif;
    color: #ffeded;
    text-transform: uppercase;
    font-size: 7vmin;
    padding-left: 10%;
    padding-right: 10%;
}

section:nth-child(odd)
{
    justify-content: flex-end;
}

2.添加圆环,锥型,圆环扭曲 几何体

// Mesh
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),
    new THREE.MeshBasicMaterial({color:'red'})
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32),
    new THREE.MeshBasicMaterial({color:'red'})
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16),
    new THREE.MeshBasicMaterial({color:'red'})
)
scene.add(mesh1,mesh2,mesh3)

参数可以在three.js文档中查看,挤在一起有点丑,更改位置,添加纹理和卡通材质,由于卡通材质在光下显示 所以要在添加一个定向光 ,

// material
const material = new THREE.MeshToonMaterial({
    color:'#ffffff'
})

// Mesh
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),
    material
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32),
    material
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16),
    material
)
scene.add(mesh1,mesh2,mesh3)


/* 
    Lights
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',1)
directionalLight.position.set(1,1,0)
scene.add(directionalLight)

可以看到明暗变化,但是对比官网中 显示的颜色有三种

如何实现 ?

通过纹理设置实现,通过设置这种贴图实现光的变化

不过还需要设置最近过滤器 ,这样能有明显的渐变
对比一下设置 和没设置的图

const textureLoad = new THREE.TextureLoader()
const gradientTexture = textureLoad.load('/textures/gradients/3.jpg')
gradientTexture.magFilter = THREE.NearestFilter // 设置最近过滤器 牵扯到WebGL原理


// material
const material = new THREE.MeshToonMaterial({
    color:parameters.materialColor,
    gradientMap:gradientTexture  // 卡通色渐变贴图 需要设置这个
})

// Mesh
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),
    material
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32),
    material
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16),
    material
)
scene.add(mesh1,mesh2,mesh3)


/* 
    Lights
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',1)
directionalLight.position.set(1,1,0)
scene.add(directionalLight)

设置位置,同时让几何体转动,并且 相机随滚动条视角移动,观测不同的几何体状态

import * as THREE from 'three'
import * as dat from 'lil-gui'

/**
 * Debug
 */
const gui = new dat.GUI()

const parameters = {
    materialColor: '#ffeded'
}

gui
    .addColor(parameters, 'materialColor')


/**
 * Base
 */
// Canvas
const canvas = document.querySelector('canvas.webgl')

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

/* 
    Objects
*/
// Texture
const textureLoad = new THREE.TextureLoader()
const gradientTexture = textureLoad.load('/textures/gradients/3.jpg')
gradientTexture.magFilter = THREE.NearestFilter // 设置最近过滤器 牵扯到WebGL原理


// material
const material = new THREE.MeshToonMaterial({
    color:parameters.materialColor,
    gradientMap:gradientTexture  // 卡通色渐变贴图 需要设置这个
})

// Mesh
const objectsDistance = 4
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),
    material
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32),
    material
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16),
    material
)

const sectionMeshes = [mesh1,mesh2,mesh3]

mesh1.position.y = - objectsDistance * 0
mesh2.position.y = - objectsDistance * 1
mesh3.position.y = - objectsDistance * 2

mesh1.position.x = 2
mesh2.position.x = -2
mesh3.position.x = 2

scene.add(mesh1,mesh2,mesh3)


/* 
    Lights
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',1)
directionalLight.position.set(1,1,0)
scene.add(directionalLight)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})


/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(35, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 6
// cameraGroup.add(camera)
scene.add(camera)

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    alpha:true,
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/* 
    Scroll
*/
let scrollY = window.scrollY // 获取滚动条y的数据
let currentSection = 0
window.addEventListener('scroll',()=>{
    scrollY = window.scrollY
})

/**
 * Animate
 */
const clock = new THREE.Clock()
let previousTime = 0
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()


    // Aniamte Camera   
    // 由于几何体 沿着y轴移动 objectsDistance 单位 所以 scrollY / sizes.height 是一比一的,* objectsDistance 就有了四个单位 
    camera.position.y = - scrollY / sizes.height * objectsDistance

    // Aniamte meshes
    for(const mesh of sectionMeshes){ // 每一帧变化时,应该改变
        mesh.rotation.x =  elapsedTime
        mesh.rotation.y =  elapsedTime + 0.1
    }


    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

camera视角

视差 :通过不同观察点看到一个物体的行为

            希望能有深度 ,在鼠标移动时,相机视角能有适当的强度变化

        实现: 由于滚动 和 移动鼠标都是 移动camera视角 导致 滚动不生效

            1. 创建组 ,在视差时候 移动组, 而camera在组中, 滚动时 移动相机 解决

       实现平滑 缓慢 移动 速度不要太快    

实现在不同电脑中 不同屏幕频率相同的移动速度

        let previousTime = 0

        const deltaTime = elapsedTime - previousTime

        previousTime = elapsedTime

import * as THREE from 'three'
import * as dat from 'lil-gui'

/**
 * Debug
 */
const gui = new dat.GUI()

const parameters = {
    materialColor: '#ffeded'
}

gui
    .addColor(parameters, 'materialColor')


/**
 * Base
 */
// Canvas
const canvas = document.querySelector('canvas.webgl')

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

/* 
    Objects
*/
// Texture
const textureLoad = new THREE.TextureLoader()
const gradientTexture = textureLoad.load('/textures/gradients/3.jpg')
gradientTexture.magFilter = THREE.NearestFilter // 设置最近过滤器 牵扯到WebGL原理


// material
const material = new THREE.MeshToonMaterial({
    color:parameters.materialColor,
    gradientMap:gradientTexture  // 卡通色渐变贴图 需要设置这个
})

// Mesh
const objectsDistance = 4
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),
    material
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32),
    material
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16),
    material
)

const sectionMeshes = [mesh1,mesh2,mesh3]

mesh1.position.y = - objectsDistance * 0
mesh2.position.y = - objectsDistance * 1
mesh3.position.y = - objectsDistance * 2

mesh1.position.x = 2
mesh2.position.x = -2
mesh3.position.x = 2

scene.add(mesh1,mesh2,mesh3)


/* 
    Lights
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',1)
directionalLight.position.set(1,1,0)
scene.add(directionalLight)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

/* 
    Group
*/
const cameraGroup = new THREE.Group()
scene.add(cameraGroup)

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(35, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 6
cameraGroup.add(camera)

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    alpha:true,
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/* 
    Scroll
*/
let scrollY = window.scrollY // 获取滚动条y的数据
let currentSection = 0
window.addEventListener('scroll',()=>{
    scrollY = window.scrollY
})

/* 
    Cursor
*/
const cursor = {}
cursor.x = 0
cursor.y = 0

window.addEventListener('mousemove',(event)=>{
    cursor.x = event.clientX / sizes.width - 0.5
    cursor.y = event.clientY / sizes.height - 0.5
})


/**
 * Animate
 */
const clock = new THREE.Clock()
let previousTime = 0
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    const deltaTime = elapsedTime - previousTime
    previousTime = elapsedTime

    // Aniamte Camera   
    // 由于几何体 沿着y轴移动 objectsDistance 单位 所以 scrollY / sizes.height 是一比一的,* objectsDistance 就有了四个单位 
    camera.position.y = - scrollY / sizes.height * objectsDistance

    const parallaxX = cursor.x * 0.5
    const parallaxY = -cursor.y * 0.5
    cameraGroup.position.x += (parallaxX - cameraGroup.position.x) * 5 * deltaTime
    cameraGroup.position.y += (parallaxY - cameraGroup.position.y) * 5 * deltaTime

    // Aniamte meshes
    for(const mesh of sectionMeshes){ // 每一帧变化时,应该改变
        mesh.rotation.x +=  deltaTime * 0.1
        mesh.rotation.y +=  deltaTime * 0.12
    }


    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

camera 视角移动

添加粒子特效

/* 
    Particles
*/
const particleCount = 200
const positions = new Float32Array(particleCount * 3)
// 设置粒子位置
for(let i = 0; i< particleCount;i++){
    positions[i * 3 + 0] = (Math.random() - 0.5) * 10
    positions[i * 3 + 1] = objectsDistance * 0.5 - Math.random() * objectsDistance * 3
    positions[i * 3 + 2] = (Math.random() - 0.5) * 10
}
const particleGeometry = new THREE.BufferGeometry()
particleGeometry.setAttribute('position',new THREE.BufferAttribute(positions,3))

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.03
particleMaterial.color = new THREE.Color(parameters.materialColor)
particleMaterial.sizeAttenuation = true // 衰减

const particle = new THREE.Points(
    particleGeometry,
    particleMaterial
)
scene.add(particle)

3.gsap

实现动画效果,在到达某一个几何体时 进行旋转

npm i gasp@3.5.1

import * as THREE from 'three'
import * as dat from 'lil-gui'
import gsap from 'gsap'

/**
 * Debug
 */
const gui = new dat.GUI()

const parameters = {
    materialColor: '#ffeded'
}

gui
    .addColor(parameters, 'materialColor')
    .onChange(()=>{
        material.color.set(parameters.materialColor)
        particleMaterial.color.set(parameters.materialColor)
    })

/**
 * Base
 */
// Canvas
const canvas = document.querySelector('canvas.webgl')

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

/* 
    Objects
*/
// Texture
const textureLoad = new THREE.TextureLoader()
const gradientTexture = textureLoad.load('/textures/gradients/3.jpg')
gradientTexture.magFilter = THREE.NearestFilter // 设置最近过滤器 牵扯到WebGL原理


// material
const material = new THREE.MeshToonMaterial({
    color:parameters.materialColor,
    gradientMap:gradientTexture  // 卡通色渐变贴图 需要设置这个
})

// Mesh
const objectsDistance = 4
const mesh1 = new THREE.Mesh(
    new THREE.TorusGeometry(1,0.4,16,60),
    material
)
const mesh2 = new THREE.Mesh(
    new THREE.ConeGeometry(1,2,32),
    material
)
const mesh3 = new THREE.Mesh(
    new THREE.TorusKnotGeometry(0.8, 0.35, 100, 16),
    material
)

const sectionMeshes = [mesh1,mesh2,mesh3]

mesh1.position.y = - objectsDistance * 0
mesh2.position.y = - objectsDistance * 1
mesh3.position.y = - objectsDistance * 2

mesh1.position.x = 2
mesh2.position.x = -2
mesh3.position.x = 2

scene.add(mesh1,mesh2,mesh3)

/* 
    Particles
*/
const particleCount = 200
const positions = new Float32Array(particleCount * 3)
// 设置粒子位置
for(let i = 0; i< particleCount;i++){
    positions[i * 3 + 0] = (Math.random() - 0.5) * 10
    positions[i * 3 + 1] = objectsDistance * 0.5 - Math.random() * objectsDistance * 3
    positions[i * 3 + 2] = (Math.random() - 0.5) * 10
}
const particleGeometry = new THREE.BufferGeometry()
particleGeometry.setAttribute('position',new THREE.BufferAttribute(positions,3))

const particleMaterial = new THREE.PointsMaterial()
particleMaterial.size = 0.03
particleMaterial.color = new THREE.Color(parameters.materialColor)
particleMaterial.sizeAttenuation = true // 衰减

const particle = new THREE.Points(
    particleGeometry,
    particleMaterial
)
scene.add(particle)


/* 
    Lights
*/
const directionalLight = new THREE.DirectionalLight('#ffffff',1)
directionalLight.position.set(1,1,0)
scene.add(directionalLight)

/**
 * Sizes
 */
const sizes = {
    width: window.innerWidth,
    height: window.innerHeight
}

window.addEventListener('resize', () =>
{
    // Update sizes
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    // Update camera
    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    // Update renderer
    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
})

/* 
    Group
*/
const cameraGroup = new THREE.Group()
scene.add(cameraGroup)

/**
 * Camera
 */
// Base camera
const camera = new THREE.PerspectiveCamera(35, sizes.width / sizes.height, 0.1, 100)
camera.position.z = 6
cameraGroup.add(camera)

/**
 * Renderer
 */
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    alpha:true,
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

/* 
    Scroll
*/
let scrollY = window.scrollY // 获取滚动条y的数据
let currentSection = 0
window.addEventListener('scroll',()=>{
    scrollY = window.scrollY

    const newSection = Math.round(scrollY / sizes.height) // 四舍五入判断 几何体旋转的时机
    if(newSection != currentSection){
        currentSection = newSection
        console.log(sectionMeshes[currentSection].rotation)
        gsap.to(
            sectionMeshes[currentSection].rotation, // 设置动画 0,1,2的动画效果
            {
                duration:1.5, // 时间
                ease:'power2.inOut', // 进出
                x:'+=6',
                y:'+=3',
                z:'+=1.5',
            }
        )
    }
})

/* 
    Cursor
*/
const cursor = {}
cursor.x = 0
cursor.y = 0

window.addEventListener('mousemove',(event)=>{
    cursor.x = event.clientX / sizes.width - 0.5
    cursor.y = event.clientY / sizes.height - 0.5
})


/**
 * Animate
 */
const clock = new THREE.Clock()
let previousTime = 0
const tick = () =>
{
    const elapsedTime = clock.getElapsedTime()

    const deltaTime = elapsedTime - previousTime
    previousTime = elapsedTime

    // Aniamte Camera   
    // 由于几何体 沿着y轴移动 objectsDistance 单位 所以 scrollY / sizes.height 是一比一的,* objectsDistance 就有了四个单位 
    camera.position.y = - scrollY / sizes.height * objectsDistance

    const parallaxX = cursor.x * 0.5
    const parallaxY = -cursor.y * 0.5
    cameraGroup.position.x += (parallaxX - cameraGroup.position.x) * 5 * deltaTime
    cameraGroup.position.y += (parallaxY - cameraGroup.position.y) * 5 * deltaTime

    // Aniamte meshes
    for(const mesh of sectionMeshes){ // 每一帧变化时,应该改变
        mesh.rotation.x +=  deltaTime * 0.1
        mesh.rotation.y +=  deltaTime * 0.12
    }


    // Render
    renderer.render(scene, camera)

    // Call tick again on the next frame
    window.requestAnimationFrame(tick)
}

tick()

camera 视角移动 几何体动画


总结

数学不要记,主要看他怎么用, 在哪里用的!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/600479.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【网络编程】http协议

预备知识 什么是http协议 HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一个应用层的协议&#xff0c;用于在网络中传输超文本&#xff08;如HTML文档&#xff09;。HTTP协议建立在TCP/IP协议之上&#xff0c;是Web浏览器和Web服务器…

「JavaEE」线程安全2:内存可见性问题 wait、notify

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;JavaEE &#x1f387;欢迎点赞收藏加关注哦&#xff01; 内存可见性问题& wait、notify &#x1f349;Java 标准库的线程安全类&#x1f349;内存可见性问题&#x1f34c;volatile 关键字 …

自动化运维管理工具----------Ansible模块详细解读

目录 一、自动化运维工具有哪些&#xff1f; 1.1Chef 1.2puppet 1.3Saltstack 二、Ansible介绍 2.1Ansible简介 2.2Ansible特点 2.3Ansible工作原理及流程 2.3.1内部流程 2.3.2外部流程 三、Ansible部署 3.1环境准备 3.2管理端安装 ansible 3.3Ansible相关文件 …

DirClass

DirClass 通过分析&#xff0c;发现当接收到DirClass远控指令后&#xff0c;样本将返回指定目录的目录信息&#xff0c;返回数据中的远控指令为0x2。 相关代码截图如下&#xff1a; DelDir 通过分析&#xff0c;发现当接收到DelDir远控指令后&#xff0c;样本将删除指定目录…

java入门详细教程——day01

目录 1. Java入门 1.1 Java是什么&#xff1f; 1.2 Java语言的历史 1.3 Java语言的分类 1.4 Java语言的特点 1.4.1 先编译再解释运行 1.4.2 跨平台 1.5 JRE和JDK&#xff08;记忆&#xff09; 1.6 JDK的下载和安装&#xff08;应用&#xff09; 1.6.1 下载 1.6.2 安…

Redis(持久化)

文章目录 1.RDB1.介绍2.RDB执行流程3.持久化配置1.Redis持久化的文件是dbfilename指定的文件2.配置基本介绍1.进入redis配置文件2.搜索dbfilename&#xff0c;此时的dump.rdb就是redis持久化的文件3.搜索dir&#xff0c;每次持久化文件&#xff0c;都会在启动redis的当前目录下…

智能实训-wheeltec小车-抓取(源代码)

语言 :C 源代码&#xff1a; #include <ros/ros.h> #include <image_transport/image_transport.h> #include <cv_bridge/cv_bridge.h> #include <sensor_msgs/image_encodings.h> #include <sensor_msgs/JointState.h> #include <geometry…

leetcode17. 电话号码的字母组合

题目描述&#xff1a; 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 示例 1&#xff1a; 输入&#xff1a;digits "…

产品需求文档怎么写?超详细的产品需求文档PRD模板来了!

产品需求文档怎么写&#xff1f;如何写一份简洁明了、外行人看了就能秒懂的产品需求文档呢&#xff1f;今天这篇文章&#xff0c;就来和大家分享如何编写一份高质量的产品需求文档 PRD&#xff01; 下图是来自 boardmix 模板社区的「产品需求文档」模板&#xff0c;它给出了一…

Verilog刷题笔记47

题目&#xff1a; From a 1000 Hz clock, derive a 1 Hz signal, called OneHertz, that could be used to drive an Enable signal for a set of hour/minute/second counters to create a digital wall clock. Since we want the clock to count once per second, the OneHer…

SpringBoot+Vue+Element-UI实现在线外卖系统

前言介绍 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势社会的发展和科学技术的进步&#xff0c;互联网技术越来越受欢迎。网络计算机的生活方式逐渐受到广大人民群众的喜爱&#xff0c;也逐渐进入了每个用户的…

【计算机科学速成课】笔记三——操作系统

文章目录 18.操作系统问题引出——批处理设备驱动程序多任务处理虚拟内存内存保护Unix 18.操作系统 问题引出—— Computers in the 1940s and early 50s ran one program at a time. 1940,1950 年代的电脑&#xff0c;每次只能运行一个程序 A programmer would write one at…

北京大学-知存科技存算一体联合实验室揭牌,开启知存科技产学研融合战略新升级

5月5日&#xff0c;“北京大学-知存科技存算一体技术联合实验室”在北京大学微纳电子大厦正式揭牌&#xff0c;北京大学集成电路学院院长蔡一茂、北京大学集成电路学院副院长鲁文高及学院相关负责人、知存科技创始人兼CEO王绍迪、知存科技首席科学家郭昕婕博士及企业研发相关负…

vivado Versal ACAP 可编程器件镜像 (PDI) 设置

Versal ACAP 可编程器件镜像 (PDI) 设置 下表所示 Versal ACAP 器件的器件配置设置可搭配 set_property <Setting> <Value> [current_design] Vivado 工具 Tcl 命令一起使用。 注释 &#xff1a; 在 Versal ACAP 架构上 &#xff0c; 原先支持将可编程器…

408算法题专项-2009年

题目&#xff1a; 分析&#xff1a;09年的链表题目比较简单&#xff0c;直接构建链表&#xff0c;然后根据不同思路模拟即可。 思路一&#xff1a;循环遍历 思考&#xff1a;最容易想到的思路&#xff0c;直接暴力循环。偷了一下懒&#xff0c;变量名称没用题目的&#xff0c;…

力扣每日一题105:从前序与中序序列构造二叉树

题目 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,20,null,null,1…

语音识别--kNN语音指令识别

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

硬盘惊魂!文件夹无法访问怎么办?

在数字时代&#xff0c;数据的重要性不言而喻。然而&#xff0c;有时我们会遇到一个令人头疼的问题——文件夹提示无法访问。当你急需某个文件夹中的文件时&#xff0c;却被告知无法打开&#xff0c;这种感受真是难以言表。今天&#xff0c;我们就来深入探讨这个问题&#xff0…

第六代移动通信介绍、无线网络类型、白皮书

关于6G 即第六代移动通信的介绍&#xff0c; 图解通信原理与案例分析-30&#xff1a;6G-天地互联、陆海空一体、全空间覆盖的超宽带移动通信系统_6g原理-CSDN博客文章浏览阅读1.7w次&#xff0c;点赞34次&#xff0c;收藏165次。6G 即第六代移动通信&#xff0c;6G 将在5G 的基…

VTK —— 三、简单操作 - 示例3 - 将点投影到平面上(附完整源码)

代码效果 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用Vs2017代码测试&#xff08;附编译流程、附编译好的库、vtk测试源码&#xff09; 教程描述 本…
最新文章