首页 > 其他分享 >Three.js 实现年会3D抽奖页面

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

时间:2024-02-07 09:00:10浏览次数:29  
标签:random object Three js new var position Math 3D

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

一、效果效果如下:

二、基础效果

元素周期表

 

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

最重要的2块有了。

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

// table  for ( 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 星星动图来完善背景!星星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

标签:random,object,Three,js,new,var,position,Math,3D
From: https://www.cnblogs.com/testzcy/p/18010546

相关文章

  • js XML 命名空间
    Node的变化在DOM2中,Node类型包含以下特定于命名空间的属性:namespaceURI,节点的命名空间URL,如果未指定则为null;prefix,命名空间前缀,如果未指定则为null。在节点使用命名空间前缀的情况下,nodeName等于prefix+":"+localName。比如下面这个例子:<head><title>Example......
  • js DOM2 和 DOM3
    DOM2(DOMLevel2)和DOM3(DOMLevel3)在这些结构之上加入更多交互能力,提供了更高级的XML特性。实际上,DOM2和DOM3是按照模块化的思路来制定标准的,每个模块之间有一定关联,但分别针对某个DOM子集。这些模式如下所示。DOMCore:在DOM1核心部分的基础上,为节点增加方法和属性。......
  • js 插入标记
    HTML5将IE发明的innerHTML和outerHTML纳入了标准,但还有两个属性没有入选。这两个剩下的属性是innerText和outerText。innerText属性innerText属性对应元素中包含的所有文本内容,无论文本在子树中哪个层级。在用于读取值时,innerText会按照深度优先的顺序将子树中所有文......
  • js scrollIntoView()
    DOM规范中没有涉及的一个问题是如何滚动页面中的某个区域。为填充这方面的缺失,不同浏览器实现了不同的控制滚动的方式。在所有这些专有方法中,HTML5选择了标准化scrollIntoView()。scrollIntoView()方法存在于所有HTML元素上,可以滚动浏览器窗口或容器元素以便包含元素进入视口......
  • js 字符集属性
    HTML5增加了几个与文档字符集有关的新属性。其中,characterSet属性表示文档实际使用的字符集,也可以用来指定新字符集。这个属性的默认值是"UTF-16",但可以通过元素或响应头,以及新增的characterSeet属性来修改。下面是一个例子:document.characterSet="UTF-8";自定义数据属性HTM......
  • js 焦点管理
    HTML5增加了辅助DOM焦点管理的功能。首先是document.activeElement,始终包含当前拥有焦点的DOM元素。页面加载时,可以通过用户输入(按Tab键或代码中使用focus()方法)让某个元素自动获得焦点。例如:```letbutton=document.getElementById("myButton");button.focus();conso......
  • 【nw.js】使用nw.js将html页面打包成exe免安装程序
    @[TOC]一、批处理zip命令(已有可跳过此步骤)下载zip,你可以到该网址下载zip执行文件,如下图:将文件路径配置到环境变量中,具体操作如下:右键计算机——>属性——>高级系统设置——>高级——>环境变量——>系统变量——>找到path,双击——>新建——>将所在路径添加进去(如:“F:\zip”包含进环......
  • spring boot controller设置返回json
    在SpringBoot中,Controller通常会返回JSON格式的数据,这得益于SpringBoot的自动配置能力以及内嵌的Jackson库。以下是如何设置Controller返回JSON数据的基本步骤:添加依赖:首先,确保你的项目中包含了SpringBoot的WebStarter依赖,它已经包括了Jackson库,用于处理JSON序列化。<dependen......
  • app.json 中未定义自定义编译中指定的启动页面
    换了启动页,这里得手动改。。。。。。。。。。。[app.json或自定义编译条件错误]app.json中未定义自定义编译中指定的启动页面./pages/index/index(env:Windows),......
  • nodejs学习计划--(三)http协议和IP介绍
    一、HTPP协议1、概念HTTP(hypertexttransportprotocol)协议;中文叫超文本传输协议是一种基于TCP/IP的应用层通信协议这个协议详细规定了浏览器和万维网服务器之间互相通信的规则。协议中主要规定了两个方面的内容客户端:用来向服务器发送数据,可以被称之为请求报文服务端......