【幻妙音符——HTML 实现(效果+代码)】

const ry = -THREE.Math.mapLinear(nx, -1, 1, cameraPanRange * -0.5, cameraPanRange * 0.5);

const rx = THREE.Math.mapLinear(ny, -1, 1, cameraYawRange * -0.5, cameraYawRange * 0.5);

TweenMax.to(root.camera.rotation, 1, {

x: rx,

y: ry,

ease: Power2.easeOut,

});

});

const tweenCamera = () => {

cameraTween = TweenMax.to(root.camera.position, SCENE_CONFIG.cameraSpeed, {

z: -=${SCENE_CONFIG.pathRadius * 2},

ease: Power0.easeIn,

onComplete: tweenCamera

});

};

tweenCamera();

cameraTween.timeScale(0);

const proxy = {

rx: 0,

ry: 0,

rz: 0,

cz: 0,

};

const camTL = new TimelineMax();

camTL.to(proxy, 4, {rz: 1, cz: 1, ease: Power2.easeIn, onUpdate: () => {

cameraTween.timeScale(proxy.cz);

}}, 0);

root.addUpdateCallback(() => {

root.scene.rotation.z -= proxy.rz * 0.003;

});

tubes.forEach((tube) => {

root.add(tube);

advanceTube(tube);

});

}

function advanceTube(tube) {

const tl = new TimelineMax();

const firstCompleteTime = tube.geometry.firstCompleteTime * tube.__config.duration;

tl.add(tube.animate(tube.__config.duration, {ease: Power0.easeInOut}));

tl.add(() => {

const transformMatrix = new THREE.Matrix4().multiplyMatrices(

tube.__pathMatrix,

NEXT_PATH_MATRIX

);

const nextTube = createPathMesh(transformMatrix, {

tubeCount: tube.__config.tubeCount,

tubeArcLength: tube.__config.tubeArcLength,

tubeStagger: tube.__config.tubeStagger

});

root.add(nextTube);

advanceTube(nextTube);

}, firstCompleteTime);

tl.add(() => {

root.remove(tube);

tube.geometry.dispose();

tube.material.dispose();

});

}

function createPath() {

let length = 16;

let path = [];

let point = new THREE.Vector3();

for (let i = 0; i < length; i++) {

let angle = i / (length - 1) * Math.PI - Math.PI * 1.5;

let radius = SCENE_CONFIG.pathRadius;

let scaleX = THREE.Math.mapLinear(i, 0, length - 1, 0.75, 0.25) * THREE.Math.randFloat(0.6, 1.0);

point.x = Math.cos(angle) * radius * scaleX;

point.z = Math.sin(angle) * radius - radius;

point.y = (i === 0 || i === length - 1) ? 0 : THREE.Math.randFloatSpread(2) * (i / length);

// point.y = 0;

let twistOffset = (i === 0 || i === length - 1) ? 0 : THREE.Math.randFloatSpread(2);

// let twistOffset = 0;

path.push(new THREE.Vector4(point.x, point.y, point.z, twistOffset));

}

return path;

}

function createPathMesh(matrix, cfg) {

const config = Object.assign(

{},

cfg,

{

tubeSegments: 128,

// tubeCount: THREE.Math.randInt(32, 48),

// tubeArcLength: THREE.Math.randFloat(0.15, 0.3),

// tubeStagger: THREE.Math.randFloat(0.0002, 0.002),

tubeCount: 32,

tubeArcLength: 0.25,

tubeStagger: 0.001,

tubeRadius: THREE.Math.randFloat(0.005, 0.01),

twistDistance: THREE.Math.randFloat(0.1, 1.5),

twistAngle: Math.PI * THREE.Math.randFloat(2, 16),

duration: SCENE_CONFIG.pathAnimationDuration,

path: createPath()

}

);

const mesh = new Tubes(config);

mesh.applyMatrix(matrix);

mesh.__pathMatrix = matrix.clone();

mesh.__config = config;

return mesh;

}

// CLASSES

function Tubes(config) {

const geometry = new TubesGeometry(config);

const material = new THREE.BAS.StandardAnimationMaterial({

shading: THREE.FlatShading,

defines: {

ROBUST: false,

TUBE_LENGTH_SEGMENTS: config.tubeSegments.toFixed(1),

PATH_LENGTH: config.path.length,

PATH_MAX: (config.path.length - 1).toFixed(1)

},

uniforms: {

thickness: {value: config.tubeRadius},

uTwist: {

value: new THREE.Vector2(

config.twistDistance,

config.twistAngle

)

},

time: {value: 0.0},

uPath: {value: config.path}

},

uniformValues: {

diffuse: new THREE.Color(COLORS.yellow),

roughness: .75,

metalness: .0

},

vertexParameters: [

THREE.BAS.ShaderChunk[‘catmull_rom_spline’],

`

attribute vec2 aAngle;

attribute float aTwistOffset;

uniform float thickness;

uniform float time;

uniform vec2 uTwist;

uniform vec4 uPath[PATH_LENGTH];

varying float vProgress;

#ifdef ROBUST

const float MAX_NUMBER = 1.79769313e+32;

#endif

vec3 sample(float t) {

float pathProgress = t * PATH_MAX;

ivec4 indices = getCatmullRomSplineIndices(PATH_MAX, pathProgress);

vec4 p0 = uPath[indices[0]];

vec4 p1 = uPath[indices[1]];

vec4 p2 = uPath[indices[2]];

vec4 p3 = uPath[indices[3]];

float angle = t * uTwist.y;

float ca = cos(angle);

float sa = sin(angle);

vec3 offset = vec3(ca, sa * ca, sa) * aTwistOffset * uTwist.x;

return catmullRomSpline(

p0.xyz + offset * p0.w,

p1.xyz + offset * p1.w,

p2.xyz + offset * p2.w,

p3.xyz + offset * p3.w,

fract(pathProgress)

);

}

vec3 getTangent (vec3 a, vec3 b) {

return normalize(b - a);

}

void rotateByAxisAngle (inout vec3 normal, vec3 axis, float angle) {

float halfAngle = angle / 2.0;

float s = sin(halfAngle);

vec4 quat = vec4(axis * s, cos(halfAngle));

normal = normal + 2.0 * cross(quat.xyz, cross(quat.xyz, normal) + quat.w * normal);

}

void createTube (float t, vec2 volume, out vec3 outPosition) {

vec3 point0 = sample(0.0);

vec3 point1 = sample(1.0 / TUBE_LENGTH_SEGMENTS);

vec3 lastTangent = getTangent(point0, point1);

vec3 absTangent = abs(lastTangent);

vec3 tmpNormal = vec3(1.0, 0.0, 0.0);

vec3 tmpVec = normalize(cross(lastTangent, tmpNormal));

vec3 lastNormal = cross(lastTangent, tmpVec);

vec3 lastBinormal = cross(lastTangent, lastNormal);

vec3 lastPoint = point0;

vec3 normal;

vec3 tangent;

vec3 binormal;

vec3 point;

float maxLen = (TUBE_LENGTH_SEGMENTS - 1.0);

float epSq = EPSILON * EPSILON;

for (float i = 1.0; i < TUBE_LENGTH_SEGMENTS; i += 1.0) {

float u = i / maxLen;

point = sample(u);

tangent = getTangent(lastPoint, point);

normal = lastNormal;

binormal = lastBinormal;

tmpVec = cross(lastTangent, tangent);

if ((tmpVec.x * tmpVec.x + tmpVec.y * tmpVec.y + tmpVec.z * tmpVec.z) > epSq) {

tmpVec = normalize(tmpVec);

float tangentDot = dot(lastTangent, tangent);

float theta = acos(clamp(tangentDot, -1.0, 1.0)); // clamp for floating pt errors

rotateByAxisAngle(normal, tmpVec, theta);

}

binormal = cross(tangent, normal);

if (u >= t) break;

lastPoint = point;

lastTangent = tangent;

lastNormal = normal;

lastBinormal = binormal;

}

float circX = aAngle.x;

float circY = aAngle.y;

vec3 T = tangent;

vec3 B = binormal;

vec3 N = -normal;

outPosition.xyz = point + B * volume.x * circX + N * volume.y * circY;

}

#else

void createTube (float t, vec2 volume, out vec3 offset) {

// find next sample along curve

// float nextT = t + (1.0 / TUBE_LENGTH_SEGMENTS) * fract(time * TUBE_LENGTH_SEGMENTS);

float nextT = t + (1.0 / TUBE_LENGTH_SEGMENTS);

vec3 current = sample(t);

vec3 next = sample(nextT);

vec3 T = normalize(next - current);

vec3 B = normalize(cross(T, next + current));

vec3 N = -normalize(cross(B, T));

float circX = aAngle.x;

float circY = aAngle.y;

float a = length(cross(next, current));

volume *= 0.5 + a * a * 0.5;

offset.xyz = current + B * volume.x * circX + N * volume.y * circY;

}

#endif `

],

fragmentParameters: [

varying float vProgress;

],

vertexPosition: [

`

float t = position.x;

t = clamp(t + time, 0.0, 1.0);

vec2 volume = vec2(thickness);

vec3 tTransformed;

createTube(t, volume, tTransformed);

transformed = tTransformed;

vProgress = t;

`

],

fragmentInit: [

if (vProgress == 0.0 || vProgress == 1.0) discard;

]

});

THREE.Mesh.call(this, geometry, material);

this.frustumCulled = false;

}

Tubes.prototype = Object.create(THREE.Mesh.prototype);

Tubes.prototype.constructor = Tubes;

Object.defineProperty(Tubes.prototype, ‘time’, {

get: function () {

return this.material.uniforms[‘time’].value;

},

set: function (v) {

this.material.uniforms[‘time’].value = v;

}

});

Tubes.prototype.animate = function(duration, options) {

options = options || {};

options.time = this.geometry.totalDuration;

return TweenMax.fromTo(this, duration, {time: 0.0}, options);

};

function TubesGeometry(config) {

const radius = 1;

const length = config.tubeArcLength;

const sides = 6;

const segments = config.tubeSegments;

const openEnded = false;

const prefab = new THREE.CylinderGeometry(radius, radius, length, sides, segments, openEnded);

prefab.rotateZ(Math.PI / 2);

this.tubeLength = length;

this.tubeStagger = config.tubeStagger;

THREE.BAS.PrefabBufferGeometry.call(this, prefab, config.tubeCount);

let aAngle = this.createAttribute(‘aAngle’, 2);

let tmp = new THREE.Vector2();

for (let i = 0, offset = 0; i < config.tubeCount; i++) {

for (let j = 0; j < prefab.vertices.length; j++) {

let v = prefab.vertices[j];

tmp.set(v.y, v.z).normalize();

let angle = Math.atan2(tmp.y, tmp.x);

aAngle.array[offset++] = Math.cos(angle); // angle x

aAngle.array[offset++] = Math.sin(angle); // angle y

}

}

const offset = 0;

this.createAttribute(‘aTwistOffset’, 1, (data, i, count) => {

data[0] = THREE.Math.mapLinear(i, 0, count - 1, -1, 1) + offset;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
i, count) => {

data[0] = THREE.Math.mapLinear(i, 0, count - 1, -1, 1) + offset;

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-CfMDrlJN-1715047896060)]

[外链图片转存中…(img-UFCVyzK8-1715047896061)]

[外链图片转存中…(img-KE4K16Ry-1715047896061)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值