3D波动点网
程序员文章站
2022-04-02 10:03:30
...
更多有趣示例 尽在小红砖社区
示例
HTML
<a href="https://github.com/gnauhca/F3.js" target="_blank">view code on github</a>
<div class="controls">
<div class="functions btn-group"></div>
<div class="rotate btn-group">
<div class="btn rotatex" data-rotate="x">rotatex</div>
<div class="btn rotatey" data-rotate="y">rotatey</div>
<div class="btn rotatez" data-rotate="z">rotatez</div>
</div>
</div>
<canvas width="2560" height="1000" style="min-width: 1000px; width: 100%; position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%)"></canvas>
CSS
* {
margin: 0;
padding: 0;
}
body, html {
width: 100%;
height: 100%;
position: relative;
}
body {
background: radial-gradient(circle, rgb(109, 39, 23), #1E90FF);
}
canvas {
pointer-events: none;
}
a,a:focus {
position: fixed;
top: 0;
left: 0;
margin: 25px;
color : #ddd;
}
.controls {
width: 500px;
display: inline-block;
position: absolute;
top: 20px;
left: 50%;
transform: translate(-50%, 0);
}
.btn-group {
display: inline-block;
}
.btn {
display: inline-block;
margin-bottom: 0;
font-weight: 500;
text-align: center;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
white-space: nowrap;
line-height: 1.5;
padding: 4px 15px;
font-size: 12px;
border-radius: 0px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transition: all .3s cubic-bezier(.645,.045,.355,1);
transition: all .3s cubic-bezier(.645,.045,.355,1);
position: relative;
color: rgba(0,0,0,.65);
background-color: #fff;
border-color: #d9d9d9;
}
.btn:first-child {
border-radius: 4px 0 0 4px;
}
.btn:last-child {
border-radius: 0 4px 4px 0;
}
.btn.active {
color: #fff;
background-color: #dc7953;
border-color: #dc7953;
}
.rotate {
margin-left: 20px;
}
.rotate .btn {
border: none;
border-radius: 20px;
}
JS
class Point extends F3.Obj {
constructor(radius=5) {
super();
this.radius = radius;
this.color = 'rgba('+[Math.random()*255|0,Math.random()*255|0,Math.random()*255|0, Math.random()].join(',')+')';
this.prevCrood = null;
}
render(ctx) {
ctx.fillStyle = '#fff';
ctx.fillRect(
this.croods2D.position.x,
this.croods2D.position.y,
this.radius * this.croods2D.scale * this.yScale,
this.radius * this.croods2D.scale * this.yScale
);
}
}
let planeFunctions = {
'sin(sqrt(x^2+z^2))': function(x, z, offset) {
return Math.sin(Math.sqrt(Math.pow(x/2, 2)+Math.pow(z/2, 2)) - offset);
},
'cos(x)*sin(z)': function(x, z, offset) {
return Math.cos(x/4 + offset)*Math.sin(z/4 + offset) * 1;
},
};
class Effect extends F3.Time {
constructor(renderer, scene, camera, cvs) {
super();
this.renderer = renderer;
this.scene = scene;
this.camera = camera;
this.cvs = cvs;
this.xOffset = 0;
this.waveHeight = 0.4; // 波高
this.waveWidth = 8; // 波长
this.col = 33;
this.colPointNum = 33;
this.flyTime = 2000;
this.timePass = 0;
this.scale = 1;
this.scaleStep = 0.01;
this.planeFunction = function() {return 0};
this.rotate = {x: false, y: false, z:false}
this.pointGroup = new F3.Obj();
this.scene.add(this.pointGroup);
this.resize(cvs.width, cvs.height);
this.init();
}
resize(width, height) {
this.cvs.width = width;
this.cvs.height = height;
// this.pointGroup.position.set(this.cvs.width/2, this.cvs.height, 0);
this.stepWidth = width * 1.8 / this.col;
this.pointGroup.setPosition(this.cvs.width/2, this.cvs.height * 1.2, -this.col * this.stepWidth/2);
this.pointGroup.setRotation(0.1, 0, 0);
// this.waveHeight = height/2;
// this.waveWidth = this.waveHeight * 4;
// console.log(this.stepWidth);
}
init() {
// create point
var point;
for (let x = -(this.col - 1) / 2, count = 0; x <= (this.col - 1) / 2; x++) {
for (let z = -(this.colPointNum-1) / 2; z <= (this.colPointNum-1) / 2 ; z++ ) {
point = new Point(10);
this.pointGroup.add(point);
/*point.initPos = new F3.Vector3(
x + Math.random() * -2 + 1,
-30 + -10 * Math.random(),
z + Math.random() * -2 + 1
);*/
point.initPos = new F3.Vector3(0,0,0);
point.flyDelay = 0//Math.random() * 1000 | 0;
}
}
}
update(delta) {
this.timePass += delta;
this.xOffset = this.timePass / 500;
let point;
let flyPecent;
let x,y,z;
let count = 0;
// if (this.timePass < 100)
for (x = -(this.col - 1) / 2; x <= (this.col - 1) / 2; x++) {
for (z = -(this.colPointNum-1) / 2; z <= (this.colPointNum-1) / 2 ; z++ ) {
// let y = Math.cos(x*Math.PI/this.waveWidth + this.xOffset)*Math.sin(z*Math.PI/this.waveWidth + this.xOffset) * this.waveHeight;
y = this.planeFunction(x,z,this.xOffset);
// let y = Math.sin(Math.sqrt(Math.pow(x/v, 2)+Math.pow(z/v, 2)) - this.xOffset) * 1
// console.log(y);
point = this.pointGroup.children[count]
point.yScale = 1;//(-y + 0.6)/(this.waveHeight) * 1.5;
flyPecent = (this.timePass-point.flyDelay) / this.flyTime;
flyPecent = flyPecent > 1 ? 1: (flyPecent < 0? 0: flyPecent);
point.setPosition(
x * this.stepWidth,
y * this.stepWidth,
z * this.stepWidth
);
count++;
}
}
if (this.rotate.x || this.rotate.y || this.rotate.z) {
this.pointGroup.setRotation(
(this.rotate.x ? this.pointGroup.rotation.x + 0.001: 0),
(this.rotate.y ? this.pointGroup.rotation.y + 0.001: 0),
(this.rotate.z ? this.pointGroup.rotation.z + 0.001: 0)
);
}
}
setFunction(fun) {
this.planeFunction = fun;
}
toggleRotate(r) {
this.rotate[r] = !this.rotate[r];
if (!this.rotate[r]) {
this.pointGroup.rotation[r] = 0;
}
}
animate() {
this.addTick((delta)=>{
this.update(delta);
this.renderer.render(this.scene, this.camera);
});
}
}
function init(cvs) {
let ctx = cvs.getContext('2d');
let scene = new F3.Scene();
let camera = new F3.Camera();
camera.origin = new F3.Vector3(cvs.width/2, cvs.height/3);
camera.p = 1200;
let renderer = new F3.Renderer(ctx, cvs);
let effect = new Effect(renderer, scene, camera, cvs);
effect.animate();
let functions = document.querySelector('.functions');
let btnHTML = '';
for (let name in planeFunctions) {
btnHTML += `<div class="btn" data-function="${name}">${name}</div>`
}
functions.innerHTML = btnHTML;
let btns = functions.querySelectorAll('.btn');
function selectFunction(funName) {
btns.forEach(function(btn) {
let dataFunction = btn.dataset.function;
if (dataFunction === funName) {
btn.classList.add('active');
effect.setFunction(planeFunctions[funName]);
} else {
btn.classList.remove('active');
}
});
}
selectFunction(btns[0].dataset.function)
functions.addEventListener('click', function(e) {
if (e.target.dataset.function) {
selectFunction(e.target.dataset.function);
}
});
let rotate = document.querySelector('.rotate');
let rotateBtns = rotate.querySelectorAll('.btn');
function toggleRotate(_r) {
rotateBtns.forEach(function(rotateBtn) {
let r = rotateBtn.dataset.rotate;
if (r === _r) {
rotateBtn.classList.toggle('active');
effect.toggleRotate(r);
}
});
}
toggleRotate('y')
rotate.addEventListener('click', function(e) {
if (e.target.dataset.rotate) {
toggleRotate(e.target.dataset.rotate);
}
});
F3.TIME.start();
}
init(document.querySelector('canvas'));