let cameraPanRange = 1.0, cameraYawRange = cameraPanRange * 1.125;
window.addEventListener(‘mousemove’, (e) => {
const nx = e.clientX / window.innerWidth * 2 - 1;
const ny = -e.clientY / window.innerHeight * 2 + 1;
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
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
Math.cos(angle); // angle x
aAngle.array[offset++] = Math.sin(angle); // angle y
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-i60TFe3Y-1715047863967)]
[外链图片转存中…(img-NsVfhYlG-1715047863967)]
[外链图片转存中…(img-7J5MPB1L-1715047863968)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!