该项目由React + vite 搭建 基于GSAP和ScrollTrigger实现炫酷3D动画网页
本期带大家手把手实现一个基于GSAP和ScrollTrigger的炫酷3D动画网页,但是考虑到完全讲解篇幅过长,打算分几篇文章来更加细致讲解
这篇文章主要讲解以下部分
- 项目搭建 React + Vite
- 主要定义:root(根伪类)和一些全局的css样式和规则
- 检测浏览器是否支持CSS变量
- 加载图片资源loading效果
- 封面文字实现效果
项目搭建
该项目小而精,就选vite来极速开发 而且配置也比较简单 开发体验也不错(热更新快)
npm create vite@latest project-name
本项目没啥可维护性 主要求快 也就不需要ts
选SWC提高编译速度
启动project!
cd project-name
npm i
npm run dev
定义全局规则
定义规范
我们可以创建一个style文件夹,再创建base.less(css/scss)文件来储存全局定义的css样式,之后可以用index.less文件将这些style文件夹的样式统一导出 像这样
根伪类
定义根伪类(:root)的好处在于它允许创建全局的CSS变量(也称为自定义属性),这些变量可以在整个样式表中使用 同时也易于修改和定制,增强可读性
//base.less
*,
*::after,
*::before {
box-sizing: border-box;
}
:root {
font-size: 16px;
--color-text: #fff;
--color-bg: #000;
--color-link: #fff;
--color-link-hover: #907030;
--color-title: #907030;
--perspective: 1500px;
--grid-item-ratio: 1.5;
--grid-width: 100%;
--grid-height: auto;
--grid-gap: 2vw;
--grid-columns: 4;
--grid-inner-scale: 1;
}
如果要定义一个背景颜色我们只需要
background: var(--color-bg);
全局使用标准盒模型 box-sizing: border-box;
使元素的宽度和高度包括边框和内边距
检测浏览器是否支持CSS变量
为什么要做这一步呢? 其实现代大多数浏览器基本的css属性都支持,但不乏有些,,, 不同的浏览器对CSS特性的支持程度不同,也可以通过这个网站这个细致查看Can I use ___?
为了检测浏览器是否支持CSS变量 以便在不同浏览器上提供一致的用户体验、动态调整样式和优化性能 可以通过supportsCssVars
检测
主要做了一下操作
- 设置页面支持 JavaScript 的标志:document.documentElement.className = 'js' 将网页的根元素的 className 属性设置为 'js',这通常用于标识页面已加载 JavaScript,以便在 CSS 中可以根据是否支持 JavaScript 来应用不同的样式
- temp储存元素用于动态添加 CSS 规则
- 给这个temp添加一个innerHTML属性并赋值一个css规则
- isSupport储存是否支持(boolean) 判断window.CSS是否支持这个css规则并转化为boolean (!!)
export const supportsCssVars = function () {
document.documentElement.className = 'js'
let temp = document.createElement('style')
temp.innerHTML = 'root: { --tmp-var: bold; }'
document.head.appendChild(temp)
const isSupport = !!(
window.CSS &&
window.CSS.supports &&
window.CSS.supports('font-weight', 'var(--tmp-var)')
)
temp.parentNode.removeChild(temp)
return isSupport
}
使用
useEffect(() => {
supportsCssVars() || alert('该浏览器不支持css变量 请换个')
}, [])
加载图片资源loading效果
平时开发中比较常见的写法,请求数据的时候加个loading,待资源加载后再把这个loading隐藏掉 显示应有的内容
我们图片资源也比较多,所以还是简单写一个呼吸loading
效果类似
主要是俩个地方
- 关键帧loaderAnim
- opacity 元素从不透明度 0 —> 1
- scale3d 水平、垂直方向大小变为原来的一半
- 动画 animation: loaderAnim 0.7s linear infinite alternate forwards;
- loaderAnim动画持续0.7s 速度匀速变化 无限循环 每次循环后反向来回运动
- 具体动画属性可查看mdn animation - MDN Web Docs
/* 页面loading */
.loading::before,
.loading::after {
content: '';
position: fixed;
z-index: 1000;
}
.loading::before {
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--color-bg);
}
.loading::after {
top: 50%;
left: 50%;
width: 60px;
height: 60px;
margin: -30px 0 0 -30px;
border-radius: 50%;
opacity: 0.4;
background: var(--color-link);
animation: loaderAnim 0.7s linear infinite alternate forwards;
}
@keyframes loaderAnim {
to {
opacity: 1;
transform: scale3d(0.5, 0.5, 1);
}
}
loading!!
function App() {
const [loading, setLoading] = useState(true)
useEffect(() => {
supportsCssVars() || alert('该浏览器不支持css变量 请换个')
doSomething(()=>{
/* ... */
setLoading(false)
})
}, [])
if (loading) return <div className="loading"></div>
return <></>
封面文字实现效果
原型图是这样的:
这个实现比较简单 主要有两点
- 布局用grid
place-items: center; display: grid;
俩行实现垂直居中
- font-size用clamp兼容性处理
- 属性
font-size: clamp( , , )
有三个值,分别是最小值、首选值和最大值
- 属性
.intro {
height: calc(100vh - 3rem);
text-align: center;
place-items: center;
display: grid;
margin-bottom: 30vh;
}
.intro__title {
place-items: center;
margin: 0;
line-height: 0.9;
display: grid;
margin-top: 15vh;
font-weight: 400;
}
.intro__title-pre {
font-size: clamp(2rem, 8vw, 5rem);
color: var(--color-title);
text-transform: uppercase;
}
.intro__title-sub {
font-size: clamp(1.5rem, 20vw, 8rem); /* clamp分别是最小值、首选值和最大值 */
max-width: 15ch;
margin: 0 auto;
}
.intro__info {
max-width: 20ch;
opacity: 0.6;
margin-bottom: 4rem;
padding-bottom: 1rem;
line-height: 1.2;
align-self: end;
}
<div>
<main>
<div className="intro">
<h1 className="intro__title">
<span className="intro__title-pre">On-Scroll</span>
<span className="intro__title-sub">Perspective Grid Animations</span>
</h1>
<span className="intro__info">Scroll moderately to fully experience the animations</span>
</div>
</main>
</div>
今天的任务就圆满结束啦 有收获可以点赞收藏哦~