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

news2024/12/26 23:47:55

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


前言

 课程要点

        结合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.coloradmin.cn/o/1649761.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么是多模态大模型,有了大模型,为什么还要多模态大模型?

随着人工智能技术的愈演愈烈&#xff0c;其技术可以说是日新月异&#xff0c;每隔一段时间就会有新的技术和理念被创造出来&#xff1b;而多模态大模型也是其中之一。 什么是多模态 想弄明白什么是多模态大模型&#xff0c;那么首先就要弄明白什么是多模态。 简单来说&#x…

shell常用文件处理命令

1. 解压 1.1 tar 和 gz 文件 如果你有一个 .tar 文件,你可以使用以下命令来解压: tar -xvf your_file.tar在这个命令中,-x 表示解压缩,-v 表示详细输出(可选),-f 后面跟着要解压的文件名。 如果你的 .tar 文件同时被 gzip 压缩了(即 .tar.gz 文件),你可以使用以下…

PHP 匿名函数和闭包在数据结构中的应用

匿名函数和闭包在数据结构处理中的应用php 中的匿名函数和闭包可用于处理数组、链表和队列等数据结构。针对数组&#xff0c;匿名函数可用于过滤元素&#xff1b;针对链表&#xff0c;闭包可用于创建节点&#xff1b;针对队列&#xff0c;匿名函数和闭包可实现 fifo 队列操作。…

2005-2021年全国各地级市生态环境注意力/环保注意力数据(根据政府报告文本词频统计)

2005-2021年全国各地级市生态环境注意力/环保注意力数据&#xff08;根据政府报告文本词频统计&#xff09; 2005-2021年全国各地级市生态环境注意力/环保注意力数据&#xff08;根据政府报告文本词频统计&#xff09; 1、时间&#xff1a;2005-2021年 2、范围&#xff1a;2…

初始Linux(基础命令)

前言&#xff1a; 我们不能总沉浸在编程语言中&#xff0c;虽然代码能力提升了&#xff0c;但是也只是开胃小菜。我们要朝着更高的方向发展。 最近小编一直在刷力扣&#xff0c;以至于博客更新的比较少。今天就带各位开始学习全新的知识——Linux.至于为啥要学&#xff1f; Lin…

基于FPGA的多路彩灯控制器VHDL代码Quartus仿真

名称&#xff1a;基于FPGA的多路彩灯控制器VHDL代码Quartus仿真&#xff08;文末获取&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; 多路彩灯控制器 综合训练内容要求 设计一台基于FPGA的多路彩灯控制器的设计。要求如下 1.彩灯从左…

IOS自动化—将WDA打包ipa批量安装驱动

前言 CSDN&#xff1a; ios自动化-Xcode、WebDriverAgent环境部署 ios获取原生系统应用的包 如果Mac电脑没有配置好Xcode相关环境,可以参考以上文章。 必要条件 Mac电脑&#xff0c;OS版本在12.4及以上&#xff08;低于这个版本无法安装Xcode14&#xff0c;装不了Xcode14就…

20230507,LIST容器

学了又忘学了又忘&#xff0c;明知道会忘又不想复习又还得学 LIST容器 1.1 基本概念 链表是一种物理存储单元上非连续的存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的指针链接实现的&#xff1b;链表由一系列结点组成 结点&#xff1a;一个是存储数据元素的数据域&a…

《ESP8266通信指南》12-Lua 固件烧录

往期 《ESP8266通信指南》11-Lua开发环境配置-CSDN博客 《ESP8266通信指南》10-MQTT通信&#xff08;Arduino开发&#xff09;-CSDN博客 《ESP8266通信指南》9-TCP通信&#xff08;Arudino开发&#xff09;-CSDN博客 《ESP8266通信指南》8-连接WIFI&#xff08;Arduino开发…

循环链表 -- c语言实现

#pragma once // 带头双向循环链表增删查改实现 #include<stdlib.h> #include<stdio.h> #include<assert.h>typedef int LTDataType;typedef struct ListNode {LTDataType data;struct ListNode* next;struct ListNode* prev; }ListNode;//双链表申请一个新节…

【Python】PTA 查验身份

知识点&#xff1a; 1.这里的加权求和就是指每一位乘以题目给的对应位置上的数字 在python中&#xff0c;对于int(10)这样的转换而来的直接是整数10&#xff0c;但是在c语言中会转换成ASCII值&#xff0c;所以要特别注意 2.本题中有两种情况是错误的&#xff0c;就是要直接输…

DES加密解密算法(简单、易懂、超级详细)

目录 一、基础补充 二、什么是DES算法 &#xff08;1&#xff09;对称加密算法 &#xff08;2&#xff09;非对称加密算法 &#xff08;3&#xff09;对称加密算法的应用 三、DES算法的基础操作步骤 1.明文的加密整体过程 2.F轮函数解析 3.密钥的形成过程 四、AC代码 五、D…

自然语言(NLP)

It’s time for us to learn how to analyse natural language documents, using Natural Language Processing (NLP). We’ll be focusing on the Hugging Face ecosystem, especially the Transformers library, and the vast collection of pretrained NLP models. Our proj…

JuiceFS v1.2-beta1,Gateway 升级,多用户场景权限管理更灵活

JuiceFS v1.2-beta1 今天正式发布。在这个版本中&#xff0c;除了进行了大量使用体验优化和 bug 修复外&#xff0c;新增三个特性&#xff1a; Gateway 功能扩展&#xff1a;新增了“身份和访问管理&#xff08;Identity and Access Management&#xff0c;IAM&#xff09;” 与…

WHM中如何查看磁盘使用情况

今日看到有用户在论坛留言反馈他买了Hostease 独立服务器并购买cPanel面板&#xff0c;想要通过面板查看当前服务器使用的磁盘情况&#xff0c;但是不知道如何查看。因为这边也是对于cPanel即WHM面板有是有所了解的&#xff0c;对于这个用户的问题&#xff0c; 操做步骤如下&am…

【Linux】Docker 安装部署 Nacos

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【Linux】Docker 安装部署 Nacos docker搜索na…

看完这篇文章我奶奶都懂Opentracing了(一)

前言 如果要基于Opentracing开发分布式链路追踪Java客户端工具包&#xff0c;首先肯定需要了解Opentracing中的各种概念&#xff0c;包括但不限于Span和Scope等&#xff0c;其实这些概念在Opentracing的官方文档中是有比较详尽的说明的&#xff0c;英文不好也能靠着机器翻译读…

【linux-IMX6ULL中断配置流程】

目录 1. Cortex-A7和GIC中断概述1. 1 Cortex-A7中断系统&#xff1a;1. 2 GIC中断控制器简介&#xff1a; 2. 中断配置概述3. 底层中断文件配置3.1 对启动文件.s的配置思路3.2 对中断函数配置思路 4. 上层中断配置流程 1. Cortex-A7和GIC中断概述 学习IMX6UL的中断处理系统&…

毕业就业信息|基于Springboot+vue的毕业就业信息管理系统的设计与实现(源码+数据库+文档)

毕业就业信息管理系统 目录 基于Springboot&#xff0b;vue的毕业就业信息管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1学生信息管理 2 公司信息管理 3公告类型管理 4公告信息管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设…

自动化运维管理工具 Ansible-----【inventory 主机清单和playbook剧本】

目录 一、inventory 主机清单 1.1inventory 中的变量 1.1.1主机变量 1.1.2组变量 1.1.3组嵌套 二、Ansible 的脚本 ------ playbook&#xff08;剧本&#xff09; 2.1 playbook介绍 2.2playbook格式 2.3playbooks 的组成 2.4playbook编写 2.5运行playbook 2.5.1ans…