Three.js 实现年会3D抽奖页面

2024-03-17 08:30

本文主要是介绍Three.js 实现年会3D抽奖页面,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 
 
 

大厂技术  高级前端  Node进阶

点击上方 程序员成长指北,关注公众号
回复1,加入高级Node交流群

突然翻到在之前公司写的抽奖软件(用于公司年会)。觉得挺感慨的,TM的一共30+人,抽15左右,代码还是我写的,就是抽不中我。(真的是,涨了人品,失了智)

一、效果

效果如下:

a0204b3b7cd84bfe560251ad9f882d11.gif

二、基础效果

元素周期表


照片墙?抽奖?写之前的那段时间,刚好逛博客,看到别个大神写的threejs版《元素周期表》,这效果大体有点近似。

ab4371f79de56cf7f2f5c87c3279f909.png

99dfb3d2e4b0a8b26d56ba5240401d80.png

最重要的2块有了。

然后再把元素、tip啥的改成抽奖用的照片和名字,使用CSS3DObject 进行渲染;

// tablefor ( var i = 0; i < table.length; i += 2 ) {// 每个图标的盒子var element = document.createElement( 'div' );element.className = 'element';element.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')';// 索引var number = document.createElement( 'div' );number.className = 'number';number.textContent = (i/2) + 1;element.appendChild( number );// 图片var symbolBox = document.createElement( 'div' );var symbol = document.createElement( 'img' );symbolBox.className = 'symbolBox';symbol.className = 'symbol';symbol.src = table[i];symbolBox.appendChild(symbol);element.appendChild( symbolBox );// 姓名var details = document.createElement( 'div' );details.className = 'details';details.innerHTML = table[ i + 1 ];element.appendChild( details );// 图标变成3d内的对象,放入场景中var object = new THREE.CSS3DObject( element );object.position.x = Math.random() * 3400 - 1700;object.position.y = Math.random() * 3400 - 1700;object.position.z = Math.random() * 3400 - 1700;object.name=table[ i + 1 ];scene.add( object );objects.push( object );// 根据索引,定制位置var object = new THREE.Object3D();var iy = Math.floor((i/2)/9);var ix = (i/2)%9;object.position.x = (ix * 140 ) -540;object.position.y = - ( iy * 180 ) + 480;targets.table.push( object );}

星空底图、星星动画

光是元素周期表的,太素了。背景肯定要选个星空啥的,最好再来一条银河!

高清的星空壁纸,太大了,如果是有点动效的gif,那体积更大。而像素低点的,效果根本不行。(适应1920屏幕的)

于是,我使用 低像素图 + canvas 星星动图来完善背景!

4e5851efaa795185202b0ea23236fa1e.png

e225b0c2798341261b67deb6141961cc.gif

星星canvas 动画。(也是网上找的,链接忘了)(感谢之前的大神 + 1)

var canvas = document.getElementById('canvas'), ctx = canvas.getContext('2d'), w = canvas.width = window.innerWidth, h = canvas.height = window.innerHeight, hue = 217, stars = [], count = 0, maxStars = 1300;//星星数量 var canvas2 = document.createElement('canvas'), ctx2 = canvas2.getContext('2d'); 
canvas2.width = 100; 
canvas2.height = 100; 
var half = canvas2.width / 2, gradient2 = ctx2.createRadialGradient(half, half, 0, half, half, half); 
gradient2.addColorStop(0.025, '#CCC'); 
gradient2.addColorStop(0.1, 'hsl(' + hue + ', 61%, 33%)'); 
gradient2.addColorStop(0.25, 'hsl(' + hue + ', 64%, 6%)'); 
gradient2.addColorStop(1, 'transparent'); ctx2.fillStyle = gradient2; 
ctx2.beginPath(); 
ctx2.arc(half, half, half, 0, Math.PI * 2); 
ctx2.fill(); // End cache function random(min, max) { if (arguments.length < 2) { max = min; min = 0; } if (min > max) { var hold = max; max = min; min = hold; } return Math.floor(Math.random() * (max - min + 1)) + min; 
} function maxOrbit(x, y) { var max = Math.max(x, y), diameter = Math.round(Math.sqrt(max * max + max * max)); return diameter / 2; //星星移动范围,值越大范围越小, 
} var Star = function() { this.orbitRadius = random(maxOrbit(w, h)); this.radius = random(60, this.orbitRadius) / 8;  //星星大小 this.orbitX = w / 2; this.orbitY = h / 2; this.timePassed = random(0, maxStars); this.speed = random(this.orbitRadius) / 50000;  //星星移动速度 this.alpha = random(2, 10) / 10; count++; stars[count] = this; 
} Star.prototype.draw = function() { var x = Math.sin(this.timePassed) * this.orbitRadius + this.orbitX, y = Math.cos(this.timePassed) * this.orbitRadius + this.orbitY, twinkle = random(10); if (twinkle === 1 && this.alpha > 0) { this.alpha -= 0.05; } else if (twinkle === 2 && this.alpha < 1) { this.alpha += 0.05; } ctx.globalAlpha = this.alpha; ctx.drawImage(canvas2, x - this.radius / 2, y - this.radius / 2, this.radius, this.radius); this.timePassed += this.speed; 
} for (var i = 0; i < maxStars; i++) { new Star(); 
} function animation() { ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 0.5; //尾巴 ctx.fillStyle = 'hsla(' + hue + ', 64%, 6%, 2)'; ctx.fillRect(0, 0, w, h) ctx.globalCompositeOperation = 'lighter'; for (var i = 1, l = stars.length; i < l; i++) { stars[i].draw(); }; window.requestAnimationFrame(animation); 
} animation();

抽奖的照片

抽奖的照片,还得在照片球的中间,位置。(也是在3d内)

同样的方式,使用CSS3DObject进行渲染

// 创建切换图片
var elements = document.createElement( 'div' );
elements.className = 'changeImgBoxs';
// element.style.backgroundImage = "url(./img/pic.png)";
elements.style.backgroundColor = 'rgba(0,127,127,' + ( Math.random() * 0.5 + 0.25 ) + ')';var symbolBox = document.createElement( 'div' );
var symbol = document.createElement( 'img' );
symbol.setAttribute("id", "changeImg");
symbolBox.className = 'symbolBox2';
symbol.className = 'symbol2';
symbol.src = table[0];
symbolBox.appendChild(symbol);
elements.appendChild( symbolBox );var details = document.createElement( 'div' );
details.setAttribute("id", "detailss");
details.className = 'details';
details.innerHTML = table[1];
elements.appendChild( details );objectsss = new THREE.CSS3DObject( elements );
objectsss.position.x = 0;
objectsss.position.y = 20000;
objectsss.position.z = 0;
scene.add( objectsss );

三、动画

一切基础准备就绪,就差动画了。

照片墙、照片球、照片散乱状态的补间动画

点击抽奖:照片墙--->照片球

点击停止:照片球--->照片散乱   (感觉就像,照片球爆炸了一样)

threejs里面,补间动画,一般使用 tween.js

先存下照片墙、球、散乱三种状态的坐标点,然后切换抽奖状态时,同时切换,通过tween切换照片的位置,这样就实现了动画。

// 表格坐标 (在初始化)
object.position.x = Math.random() * 3400 - 1700;object.position.y = Math.random() * 3400 - 1700;object.position.z = Math.random() * 3400 - 1700;object.name=table[ i + 1 ];scene.add( object );objects.push( object );// 根据索引,定制位置var object = new THREE.Object3D();var iy = Math.floor((i/2)/9);var ix = (i/2)%9;object.position.x = (ix * 140 ) -540;object.position.y = - ( iy * 180 ) + 480;targets.table.push( object );// 球的 照片坐标
var vector = new THREE.Vector3();
for ( var i = 0, l = objects.length; i < l; i ++ ) {
var phi = Math.acos( -1 + ( 2 * i ) / l );
var theta = Math.sqrt( l * Math.PI ) * phi;
var object = new THREE.Object3D();
object.position.x = 750 * Math.cos( theta ) * Math.sin( phi );
object.position.y = 750 * Math.sin( theta ) * Math.sin( phi );
object.position.z = 750 * Math.cos( phi );
vector.copy( object.position ).multiplyScalar( 2 );
object.lookAt( vector );
targets.sphere.push( object );
}
// 散乱随机位置
var vector = new THREE.Vector3();
for ( var i = 0, l = objects.length; i < l; i ++ ) {
var phi = Math.acos( -1 + ( 2 * i ) / l );
var theta = Math.sqrt( l * Math.PI ) * phi;
var object = new THREE.Object3D();
var py = Math.random() * 3400 - 1700;
if(py<400&&py>-400){    //防止停止时,图片位置不好挡住中央的图片中间空出点位置
if(py<0){
py-=400;
}else{
py+=400;
}
}
object.position.x = Math.random() * 3400 - 1700;
object.position.y = py;
object.position.z = Math.random() * 3400 - 1700;
object.lookAt( vector );
targets.chaos.push( object );
}

tweenjs 补件动画

// 切换状态时动画
function transform( targets, duration ,type) {var scale = 1;if(type==undefined){type=0;}TWEEN.removeAll();for ( var i = 0; i < objects.length; i ++ ) {var object = objects[ i ];var target = targets[ i ];new TWEEN.Tween( object.position ).to( { x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration ).easing( TWEEN.Easing.Exponential.InOut ).start();new TWEEN.Tween( object.rotation ).to( { x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration ).easing( TWEEN.Easing.Exponential.InOut ).start();                                 }new TWEEN.Tween( this ).to( {}, duration * 2 ).onUpdate( render ).start();
}

抽奖时动画

这个时候的动画,主要是两个:照片切换、照片球的转动

照片切换,中间图片src  随机切换就好了

照片球转动?何必呢,转动相机不是更轻松?

于是使用:TrackballControls.js轨道控制器。转相机就好了嘛

var numsss =0,srcss='',txtsss='';
function movings(){// 相机旋转ang += Math.PI/50;camera.position.x = Math.cos(ang)*2000;camera.position.z = Math.sin(ang)*2000; camera.position.y = 0;// 相机方向重置camera.up.x = 0;camera.up.y = 1;camera.up.z = 0;// 图片方向固定objectsss.rotation.y =-ang+Math.PI/2;//中间图片切换numsss = Math.floor(Math.random()*tableLens);if(numsss==tableLens){numsss = tableLens-1;}srcss = table[numsss*2];txtsss =  table[numsss*2+1];changeImg.src = srcss;detailss.innerHTML = txtsss;
}

抽奖结束后中奖图片放大

抽奖结束,停止转动,中奖图片放大

// 停止时,图片放大动画
function objDeal(obj,nums){var option = {x: obj.scale.x,  y: obj.scale.y, z: obj.scale.z,                 };var tween = new TWEEN.Tween(option).to({x:nums,y:nums,z:nums,},300).delay(100).onUpdate(function() {obj.scale.x = this.x;obj.scale.y = this.y;obj.scale.z = this.z;isMoving = true;}).onComplete(function(){isMoving = false;                }).start();
}

四、完善细节、添加音乐

抽奖嘛,光效果是不够的,还得有点音乐才好。(音乐是当时人事姐姐提供的)

目前添加2个音乐:点击抽奖后,激动人心的音乐《猪突猛进》。中奖时的音乐,一段布灵布灵的铃声。

<!--抽奖音乐标签-->
<audio id="music" src="./music/04-抽奖音乐 猪突猛進.mp3" loop></audio>
<!--抽奖音乐标签-->
<audio id="music2" src="./music/9629.mp3"></audio>//开始
start:function(){if(vm.spic.img!=''){alert("抽完咯~~~完咯~~~咯~~~,在中奖名单中清空,再来一次?");return;}moving = true;              objectsss.position.y = 0;transform( targets.sphere, 1000 );objDeal(objectsss,1);music.play(); // 开始音乐this.ckPrice();           
},
//结束
closes:function(){music.pause(); // 关闭音乐if(!moving){return;} music2.play(); // 播放中奖音乐moving = false;this.choosePerson();transform( targets.chaos, 250 ,1);objDeal(objectsss,1.8);   
},

五、结束语

代码几年前写的,挺垃圾的,没考虑性能,也没有做啥优化。只是当时做的思路还算比较清晰。给大家看下,提供些相关业务的思路。

源码:

https://github.com/baiDog/something/tree/master/test

作者:狗贼
链接:https://juejin.cn/post/7057759297811251231

Node 社群

 

我组建了一个氛围特别好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你对Node.js学习感兴趣的话(后续有计划也可以),我们可以一起进行Node.js相关的交流、学习、共建。下方加 考拉 好友回复「Node」即可。

8e4be9e5ee41318676ecc199fc54a177.png

“分享、点赞、在看” 支持一下

d58f7de2f2cf557d273c68bba3ec08ad.gif

这篇关于Three.js 实现年会3D抽奖页面的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/818398

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

轻量级在线服装3D定制引擎Myway简介

我写的面向web元宇宙轻量级系列引擎中的另外一个,在线3D定制引擎Myway 3D。 用于在线商品定制,比如个性化服装的定制、日常用品(如杯子)、家装(被套)等物品的在线定制。 特性列表: 可更换衣服款式,按需定制更换模型可实时更改材质颜色可实时添加文本,并可实时修改大小、颜色和角度,支持自定义字体可实时添加艺术图标,并可实时修改大小、颜色和角度,支持翻转、各种对齐可更改衣服图案,按需求定制

JAVA读取MongoDB中的二进制图片并显示在页面上

1:Jsp页面: <td><img src="${ctx}/mongoImg/show"></td> 2:xml配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001

通过SSH隧道实现通过远程服务器上外网

搭建隧道 autossh -M 0 -f -D 1080 -C -N user1@remotehost##验证隧道是否生效,查看1080端口是否启动netstat -tuln | grep 1080## 测试ssh 隧道是否生效curl -x socks5h://127.0.0.1:1080 -I http://www.github.com 将autossh 设置为服务,隧道开机启动

创新、引领、发展——SAMPE中国2024年会在京盛大开幕

绿树阴浓夏日长,在这个色彩缤纷的季节,SAMPE中国2024年会暨第十九届国际先进复合材料制品原材料、工装及工程应用展览会在中国国际展览中心(北京朝阳馆)隆重开幕。新老朋友共聚一堂,把酒话桑麻。 为期4天的国际学术会议以“先进复合材料,引领产业创新与可持续化发展”为主题,设立了34个主题分会场,其中包括了可持续化会场、国际大学生会场、中法复合材料制造技术峰会三个国际会场和女科技工作者委员会沙龙,

JavaScript全屏,监听页面是否全屏

在JavaScript中,直接监听浏览器是否进入全屏模式并不直接支持,因为全屏API主要是关于请求和退出全屏模式的,而没有直接的监听器可以告知页面何时进入或退出全屏模式。但是,你可以通过在你的代码中跟踪全屏状态的改变来模拟这个功能。 以下是一个基本的示例,展示了如何使用全屏API来请求全屏模式,并在请求成功或失败时更新一个状态变量: javascriptlet isInFullscreen =

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测

时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测 目录 时序预测 | MATLAB实现LSTM时间序列未来多步预测-递归预测基本介绍程序设计参考资料 基本介绍 MATLAB实现LSTM时间序列未来多步预测-递归预测。LSTM是一种含有LSTM区块(blocks)或其他的一种类神经网络,文献或其他资料中LSTM区块可能被描述成智能网络单元,因为

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

js+css二级导航

效果 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Con