1000粉!使用Three.js实现一个创意纪念页面 🏆
声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。
背景
不知不觉,掘金关注者人数已经超过 1000
人,因此特地做了这个页面纪念一下,感谢大家关注
,希望博客园的粉丝也涨涨。后续也将继续努力,持续输出一些有价值的文章。本文内容涉及的技术栈为 React + Three.js + Stulus
,本文中主要包含的知识点包括:圆锥几何体 ConeGeometry
、圆柱几何体 CylinderGeometry
、材质捕捉纹理材质 MeshMatcapMaterial
、文字创建和修饰的 FontLoader
和 TextGeometry
、使用 Gsap
和它的插件 Physics2DPlugin
创建一些动画、rotateOnAxis
方法实现绕轴自转等。
对了,后续我专门新建了一个专门针对 Three.js
系列的专栏【Three.js 奥德赛进阶之旅】,是掘金签约的专栏 。从基础入门开始,全方位了解
Three.js
的各种特性,并结合和应用对应特性,实现令人眼前一亮的 Web
创意页面,进而逐步挖掘 Three.js
和 WebGL
深层次的知识。在这里推广一下,大家感兴趣的话可以关注一波 。
效果
页面 主体内容主要由四部分组成,分别是:文字
1000!
、文字 THANK YOU
、掘金三维 Logo
、以及 纸片礼花
。其中文字各自具有翻转动画,掘金
Logo
有自转动画效果,当用 鼠标点击屏幕时,会出现
*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。
撒花效果。
打开以下链接中的任意一个可以在线预览效果。本页面适配PC端和移动端,大屏访问效果更佳。
在线预览地址1:https://3d-eosin.vercel.app/#/fans
在线预览地址2:https://dragonir.github.io/3d/#/fans
实现
资源引入
首先在顶部引入开发必备的资源,除了基础的 React
和样式表之外,THREE
是 Three.js
库;OrbitControls
用于镜头轨道控制,可以使用鼠标移动或旋转模型;Text
是用于创建文字模型的一个类;Confetti
是一个用于创建礼花效果的类,在后面内容中会做详细介绍。
import './index.styl';
import React from 'react';
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import Text from '@/assets/utils/Text';
import Confetti from '@/assets/utils/Confetti';
页面结构
页面主要结构非常简单,其中 .webgl
用于渲染 3D
元素;.logo
是页面上的一些图标装饰,.github
是存放本项目的 Github
链接地址。
<div className='fans_page'>
<canvas className='webgl'></canvas>
<i className='logo'></i>
<i className='logo click'></i>
<a className='github' href='https://github.com/dragonir/3d' target='_blank'></a>
</div>
创建 Logo
创建 Logo
时,先创建一个 Group
,然后将 Logo
的各个部分添加到 Group
中,这样有利于对 Logo
整体调整位置和添加动画,也有利于页面加载性能。接着,通过以下步骤创建 Logo
模型的三部分:
- 创建通用的材质
MeshMatcapMaterial
,Logo
模型的所有组成网格都将使用这种材质; - 使用
ConeGeometry
创建顶部的四棱锥
,并应用材质; - 使用
CylinderGeometry
创建中间的四棱台
,并应用材质; - 使用
CylinderGeometry
创建底部的四棱台
,并应用材质; - 将上述网格模型添加到
Group
中,并调整整体的位置、大小,并设置倾斜角度以便获得更好的页面视觉效果。
在实际开发中,应用了
ConeBufferGeometry
、CylinderBufferGeometry
代替ConeGeometry
和CylinderGeometry
,以便获得更好的性能。
本示例中模型的计算参数如上图所示,顶部四棱柱的四个面都是边长为 4
的等边三角形,其余两个棱台的侧边长也是 4
,其他边的长度参数都可以通过勾股定理以及三角函数计算得出,本文中不做详细计算。(PS
:模型示意图是用 Windows
画图工具画的,有点丑 )
const logo = new THREE.Group();
// 材质捕捉纹理材质
const logoMaterial = new THREE.MeshMatcapMaterial({
matcap: this.matcaps.logoMatcap,
side: THREE.DoubleSide,
});
// 顶部四棱锥
const cone = new THREE.Mesh(new THREE.ConeGeometry(4, 4, 4), logoMaterial);
logo.add(cone);
// 中间四棱台
const cylinder = new THREE.Mesh(new THREE.CylinderGeometry(6, 10, 4, 4, 1), logoMaterial);
cylinder.position.y = -6
logo.add(cylinder);
// 底部四棱台
const cylinder2 = new THREE.Mesh(new THREE.CylinderGeometry(12, 16, 4, 4, 1), logoMaterial);
cylinder2.position.y = -12
logo.add(cylinder2);
logo.position.set(0, 0, 0);
logo.scale.set(11, 11, 11);
// 设置倾斜角度
logo.rotateY(Math.PI * 0.2);
logo.rotateZ(Math.PI * 0.1);
scene.add(logo);
知识点
圆锥几何体ConeGeometry
圆锥几何体 ConeGeometry
,是一个用于生成圆锥几何体的类,侧面分段数越多则越圆,本例中分段数为 4
,所以看起来是个四棱锥。
构造函数:
ConeGeometry(radius: Float, height: Float, radialSegments: Integer, heightSegments: Integer, openEnded: Boolean, thetaStart: Float, thetaLength: Float);
参数说明:
radius
:圆锥底部的半径,默认值为1
。height
:圆锥的高度,默认值为1
。radialSegments
:圆锥侧面周围的分段数,默认为8
。heightSegments
:圆锥侧面沿着其高度的分段数,默认值为1
。openEnded
:一个Boolean
值,指明该圆锥的底面是开放的还是封顶的。默认值为false
,即其底面默认是封顶的。thetaStart
:第一个分段的起始角度,默认为0
。thetaLength
:圆锥底面圆扇区的中心角,通常被称为θ
。默认值是2*PI
,这使其成为一个完整的圆锥。
知识点
圆柱几何体CylinderGeometry
圆柱几何体 CylinderGeometry
,是一个用于生成圆柱几何体的类。本文中 Logo
的中间和底部就由此类生成。
构造函数:
CylinderGeometry(radiusTop: Float, radiusBottom: Float, height: Float, radialSegments: Integer, heightSegments: Integer, openEnded : Boolean, thetaStart: Float, thetaLength: Float)
参数说明:
radiusTop
:圆柱的顶部半径,默认值是1
。radiusBottom
:圆柱的底部半径,默认值是1
。height
:圆柱的高度,默认值是1
。radialSegments
:圆柱侧面周围的分段数,默认为8
。heightSegments
:圆柱侧面沿着其高度的分段数,默认值为1
。openEnded
:一个Boolean
值,指明该圆锥的底面是开放的还是封顶的。默认值为false
,即其底面默认是封顶的。thetaStart
:第一个分段的起始角度,默认为0
。thetaLength
:圆柱底面圆扇区的中心角,通常被称为θ
。默认值是2*PI
,这使其成为一个完整的圆柱。
知识点
材质捕捉纹理材质MeshMatcapMaterial
MeshMatcapMaterial
由一个材质捕捉 MatCap或光照球
纹理所定义,其编码了材质的颜色与明暗。由于 mapcap
图像文件编码了烘焙过的光照,因此MeshMatcapMaterial不对灯光作出反应。它可以投射阴影到一个接受阴影的物体上,但不会产生自身阴影或是接收阴影。
构造函数:
MeshMatcapMaterial(parameters: Object)
parameters
:可选,用于定义材质外观的对象,具有一个或多个属性,材质的任何属性都可以从此处传入,包括从 Material
继承的任何属性。
.color[Color]
:材质的颜色,默认值为白色0xffffff
。.matcap[Texture]
:matcap
贴图,默认为null
。- 其他Material基类的共有属性等。
MeshMatcapMaterial
是一种非常好用的材质,简单使用这种材质就能实现复杂的纹理效果,如本文中 Logo
的光泽效果,以及后续文字的金属效果以及透明玻璃效果,选择合适的材质,可以实现各种各样的神奇效果。下面这张图就是本文中所有元素的材质贴图,可以看出它们是一个个光照球体样式。
除了在 Blender
、Photoshop
等设计软件中生成 MeshMatcapMaterial
之外,下面几个网站可以免费下载各种好看的材质,并且具有在线实时预览功能,大家可以根据页面元素内容和自身需求找到合适的材质图片,感兴趣的话可以亲手试试看 。
https://observablehq.com/@makio135/matcaps?ui=classic
https://github.com/nidorx/matcaps
http://jeanmoreno.com/unity/matcap/http://jeanmoreno.com/unity/matcap/
创建文字 1000!
接着,来创建文字,此时需要引入 FontLoader
,用于加载字体文件,它返回一个字体实例,然后使用 TextGeometry
创建文字网格,将它添加到场景中就可以了。
import { FontLoader } from 'three/examples/jsm/loaders/FontLoader';
import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry';
fontLoader.load('fontface.json', font => {
textMesh.geometry = new TextGeometry('1000!', {
font: font,
size: 100,
height: 40
});
scene.add(textMesh);
});
看起来非常普通对不对,此时可以对 TextGeometry
进行对字符的厚度、斜角大小等参数的调整,我们可以按类似下面这种稍微优化一下,直至调整到自己满意的结果为止。
textMesh.geometry = new TextGeometry('1000!', {
font: font,
size: 100,
height: 40,
curveSegments: 100,
bevelEnabled: true,
bevelThickness: 10,
bevelSize: 10,
bevelOffset: 2,
bevelSegments: 10
});
看看优化后的效果,瞬间高大上了有木有 !
创建文字 THANK YOU
使用同样的方法添加 THANK YOU
文字网格到场景中,并为它设置半透明玻璃效果的 MeshMatcapMaterial
和文字厚度斜角样式。
关于文字网格的详细应用可以看看我的这篇文章 《使用Three.js实现神奇的3D文字悬浮效果》,本文中不再赘述了。
创建文字动画
文字创建完成后,可以给它们添加一些文字翻转动画效果。动画效果是通过 Gsap
实现的,本文中给1000!
文字添加了一个缩放并翻转的动画效果,给 THANK YOU
添加了一个上下翻转的动画效果,可以参考如下方法来实现。
import gsap from 'gsap';
// 上下翻转动画
zoomAndFlip() {
gsap.timeline({
repeat: -1,
defaults: {
duration: 2,
ease: 'elastic.out(1.2, 1)',
stagger: 0.1,
},
})
.to(this.meshesPosition, { z: this.meshesPosition[0].z + 100 }, 'start')
.to(this.meshesRotation, { duration: 2, y: Math.PI * 2 }, 'start')
.to(this.meshesRotation, { duration: 2, y: Math.PI * 4 }, 'end')
.to(this.meshesPosition, { z: this.meshesPosition[0].z }, 'end');
}
创建礼花
页面每次打开以及点击屏幕时,可以产生礼花效果。其中礼花中的每个小碎片使用了面基础缓冲模型 PlaneBufferGeometry
以及 MeshBasicMaterial
基础材质构成,在场景中创建三束礼花,每束礼花内的碎片位置和大小随机,并在一段时间后自动消失。同样,礼花的动画效果也是使用了 Gsap
,并且使用了它的插件 Physics2DPlugin
来实现,Physics2DPlugin
插件可以模拟物理动画效果包括重力、速度、加速度、摩擦力动画等,有了它就能更好地实现礼花爆炸和散落效果。可以像本文中这样使用它们:
import gsap from 'gsap';
const physics2D = require('./physics2D');
gsap.registerPlugin(physics2D.Physics2DPlugin);
// 对每一片礼花应用动画效果
gsap.to(this.confettiSprites[id], DECAY, {
physics2D: {
velocity,
angle,
gravity,
friction,
},
ease: 'power4.easeIn',
onComplete: () => {
_.pull(this.confettiSpriteIds, id);
this.parent.remove(this.meshes[id]);
this.meshes[id].material.dispose();
delete this.confettiSprites[id];
},
});
知识点
Physics2DPlugin
Physics2DPlugin
设置二维物理动画抛物线效果可选参数:
velocity
:初始速度angle
:角度gravity
:重力acceleration
:加速度accelerationAngle
:加速度角度friction
:摩擦力
点击页面时触发动画
window.addEventListener('pointerdown', e => {
e.preventDefault();
this.confetti && this.confetti.pop();
});
文字和礼花效果在实际中实现,其实是分别封装了两个类,方便从外部调用,具体实现详细代码可以访问文末提供的源码链接。
缩放监听及重绘动画
添加页面缩放适配和重绘动画来更新相机和轨道控制器等。在重绘动画中,给 Logo
添加了一个绕自身 Y轴
旋转的效果,可以通过 rotateOnAxis
实现。
window.addEventListener('resize', () => {
this.width = window.innerWidth;
this.height = window.innerHeight;
this.camera.aspect = this.width / this.height;
this.camera.updateProjectionMatrix();
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
this.renderer.setSize(this.width, this.height);
}, {
passive: true
});
const animate = () => {
requestAnimationFrame(animate);
controls && controls.update();
// 旋转动画
logo && logo.rotateOnAxis(axis, Math.PI / 400);
renderer.render(scene, camera);
}
大家可以亲自动手试试
rotateOnAxis
和rotateY
实现的旋转效果有何不同来区分两者。
知识点
rotateOnAxis
.rotateOnAxis
是 Three.js
中三维物体基类 Object3D
的一个方法,它可以在局部空间中绕着该物体的轴来旋转一个物体,假设这个轴已被标准化,它的使用方法如下所示。
.rotateOnAxis(axis: Vector3, angle: Float)
axis
:一个在局部空间中的标准化向量。angle
:角度,以弧度来表示。
样式细节优化
到这一步,页面的功能已经全部完成了 ,最后可以装饰一下页面,如将
renderer
设置为透明,然后在 CSS
中使用一张好看的科技感图片作为页面背景,最后加上几个角落里的图片装饰物和 Github
图标链接,加一点点 CSS
动画,页面整体视觉效果就得到了不错的提升 。最后再次感谢大家关注!
谢谢、栓Q、阿里嘎多
源码地址:https://github.com/dragonir/3d/tree/master/src/containers/Fans
总结
本文包含的知识点主要包括:
- 圆锥几何体
ConeGeometry
- 圆柱几何体
CylinderGeometry
- 材质捕捉纹理材质
MeshMatcapMaterial
- 文字创建和修饰的
FontLoader
和TextGeometry
- 使用
Gsap
和它的插件Physics2DPlugin
创建一些动画 rotateOnAxis
方法实现绕轴自转
想了解其他前端知识或其他未在本文中详细描述的
Web 3D
开发技术相关知识,可阅读我往期的文章。转载请注明原文地址和作者。如果觉得文章对你有帮助,不要忘了一键三连哦 。
附录
- 新建【Three.js 进阶之旅】Three.js 系列专栏
- [1]. Three.js 打造缤纷夏日3D梦中情岛
- [2]. Three.js 实现炫酷的赛博朋克风格3D数字地球大屏
- [3]. Three.js 实现2022冬奥主题3D趣味页面,含冰墩墩
- 更多往期【3D】专栏访问
...
- [1]. 前端实现很哇塞的浏览器端扫码功能
- [2]. 前端瓦片地图加载之塞尔达传说旷野之息
- [3]. 使用前端技术实现静态图片局部流动效果
- 更多往期【前端】专栏访问
...
本文作者:dragonir 本文地址:https://www.cnblogs.com/dragonir/p/16691845.html
1000粉!使用Three.js实现一个创意纪念页面 🏆的更多相关文章
- js实现一个砖头在页面拖拉效果
用js实现一个砖头在页面,但鼠标点击拖动时,砖头在页面上形成拖拉效果: 刚开始时: 鼠标点击拖动后: 实现代码: <html> <head> <meta ...
- JS制作一个创意数字时钟
通过js代码制作一个创意数字时钟 通过JS代码实现创意数字时钟效果如下:由数字化的卡通形象图片取代常规的数字显示当前实时北京时间.具体效果示例: 核心重点: (1)Date方法的初步了解 (2)构建模 ...
- Vue.js实现一个SPA登录页面的过程
技术栈 vue.js 主框架 vuex 状态管理 vue-router 路由管理 一般过程 在一般的登录过程中,一种前端方案是: 检查状态:进入页面时或者路由变化时检查是否有登录状态(保存在cooki ...
- Vue.js写一个SPA登录页面的过程
技术栈 vue.js 主框架 vuex 状态管理 vue-router 路由管理 一般过程 在一般的登录过程中,一种前端方案是: 检查状态:进入页面时或者路由变化时检查是否有登录状态(保存在cooki ...
- HTML/CSS+JS制作一个高考倒计时页面
2020-07-09更新 修复倒计时归零后出现负数的bug 自动切换至下一年日期 ##效果展示 前言 在B站上找视频学习的,勉强搞出来了,写下此篇文章作为笔记,也希望有更多感兴趣的人能够有所收获. ( ...
- Vue.js实现一个SPA登录页面的过程【推荐】
地址:https://www.jb51.net/article/112550.htm vue路由跳转时判断用户是否登录功能的实现 地址:https://www.jb51.net/article/126 ...
- 1000粉!使用Three.js制作一个专属3D奖牌🥇
背景 破防了 !突然发现 SegmentFault 平台的粉丝数量已经突破 1000 了,它是我的三个博客平台掘金.博客园.SegmentFault中首个粉丝突破 1000 的,于是设计开发这个页面, ...
- 分享:计算机图形学期末作业!!利用WebGL的第三方库three.js写一个简单的网页版“我的世界小游戏”
这几天一直在忙着期末考试,所以一直没有更新我的博客,今天刚把我的期末作业完成了,心情澎湃,所以晚上不管怎么样,我也要写一篇博客纪念一下我上课都没有听,还是通过强大的度娘完成了我的作业的经历.(当然作业 ...
- JS获得月最后一天和js得到一个月最大天数
<html xmlns="http://www.w3.org/1999/xhtml" > <head> <title>标题页</title ...
随机推荐
- 使用Karmada实现Helm应用的跨集群部署
摘要:借助Karmada原生API的支持能力,Karmada可以借助Flux轻松实现Helm应用的跨集群部署. 本文分享自华为云社区< 使用Karmada实现Helm应用的跨集群部署[云原生开源 ...
- 【RocketMQ】消息的存储
Broker对消息的处理 BrokerController初始化的过程中,调用registerProcessor方法注册了处理器,在注册处理器的代码中可以看到创建了处理消息发送的处理器对象SendMe ...
- NC19115 选择颜色
NC19115 选择颜色 题目 题目描述 \(n\) 个人排成一个环形,每个人要从 \(c\) 种颜色中选择一个. 牛牛希望相邻的人选择的颜色是不同的 问有多少种方案. 输出方案数对 \(10007\ ...
- NC50999 表达式计算4
NC50999 表达式计算4 题目 题目描述 给出一个表达式,其中运算符仅包含+,-,*,/,^(加 减 乘 整除 乘方)要求求出表达式的最终值 数据可能会出现括号情况,还有可能出现多余括号情况 数据 ...
- NC224938 加减
NC224938 加减 题目 题目描述 小红拿到了一个长度为 \(n\) 的数组.她每次操作可以让某个数加 \(1\) 或者某个数减 \(1\) . 小红最多能进行 \(k\) 次操作.她希望操作结束 ...
- 腾讯云原生数据库TDSQL-C入选信通院《云原生产品目录》
近日,中国信通院.云计算开源产业联盟正式对外发布<云原生产品目录>,腾讯云原生数据库TDSQL-C凭借其超强性能.极致效率的弹性伸缩和完善的产品化解决方案体系,成功入围目录. 全球数字经济 ...
- SQLZOO练习7--Using NULL
teacher表: id dept name phone mobile 101 1 Shrivell 2753 07986 555 1234 102 1 Throd 2754 07122 555 19 ...
- Python中使用 for 循环来拿遍历 List 的值
常规版本 简单的 for 循环遍历 x_n = ["x1","x2","x3"] for x in x_n: print(x) >&g ...
- linux系统中Nginx+FFmPeg+vlc实现网页视频播放配置过程
linux系统中Nginx+FFmPeg实现网页监控视频播放配置过程 1.安装好的nginx上添加模块nginx-http-fiv-module-master 此模块是rtmp模块的升级版,有它所有的 ...
- Powerful Number 筛法
我也不想学筛法了,可你考试时候出一个新筛法就不厚道了吧,我还开始以为这是杜教筛... $tips:$学完杜教筛立马学$Powerful \ Number$筛法,此筛法强悍如斯 $Powerful \ ...