初始化模版
拉取项目
Starter CODE: https://github.com/codebucks27/Apple-iphone-3d-landing-page-starter-Code
Final CODE: https://github.com/codebucks27/3D-Landing-page-for-Apple-iPhone
编写
编写全局文件
1)编写GlobalStyle.js
定义全局样式,并在App.js
引入
编写Quete.js
1)新建sections/Quote.js
文件,并且安装插件ES7+ React/Redux/React-Native/JS snippets
即可在js
文件中使用 rafce
生成
1 2 3 4 5 6 7
| import React from 'react' const Quote = () => { return ( <div>Quote</div> ) } export default Quote
|
并且在APP.js
引入
2)编写Quote.js,添加文字动画
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
| import React from 'react' import styled, { keyframes } from "styled-components";
const Section = styled.section` width: 100vw; height: 100vh; position: relative;
display: flex; justify-content: center; align-items: center; `;
const TextContainer = styled.div` width: 100%; height: 100%;
display: flex; // 设置主轴方向为从上到下。 flex-direction: column; // 在主轴上居中内容。 justify-content: center; // 在交叉轴上居中内容。 align-items: center;
background-color: var(--dark); color: var(--white); `;
const moveUp = keyframes` 100%{ transform: translateY(0); } `;
const Text = styled.p` width: 50%; font-size: var(--fontlg); position: relative; height: var(--fontmd); overflow: hidden;
span { position: absolute; transform: translateY(3rem); animation-name: ${moveUp}; animation-duration: 2.5s; animation-timing-function: ease; // 在动画完成后,保持动画的最终状态 animation-fill-mode: forwards; animation-delay: ${(props) => props.delay}; font-family: var(--fontL);
// 通过背景渐变设置背景图片 background-image: linear-gradient(-45deg, var(--gradient)); // 通过背景剪切设置背景图片 background-clip: text; // 针对webkit浏览器设置背景剪切 -webkit-background-clip: text; // 针对webkit浏览器设置文本填充颜色 -webkit-text-fill-color: transparent; }
// 作者部分 .author { width: 100%; text-align: end; background-image: linear-gradient(-180deg, var(--gradient)); font-family: var(--fontR); }
@media screen and (max-width: 70em) { width: 70%; }
@media screen and (max-width: 48em) { font-size: var(--fontmd); height: var(--fontsm); } @media screen and (max-width: 40em) { width: 90%; } @media screen and (max-width: 30em) { font-size: var(--fontxs); } `;
const Quote = () => { return ( <Section> <TextContainer> <Text delay="0s"> {" "} <span>“ You can't connect the dots looking forward;</span> {" "} </Text> <Text delay="0.4s"> {" "} <span> you can only connect them looking backward. </span> {" "} </Text> <Text delay="0.8s"> {" "} <span> so you have to trust that the dots</span> {" "} </Text> <Text delay="1.2s"> {" "} <span> will somehow connect in your future. ” </span> {" "} </Text> <Text delay="1.6s"> {" "} <span className="author">⎯ Steve Jobs</span> {" "} </Text> </TextContainer> </Section> ) }
export default Quote
|
编写HeroSection.js
1)新建section/HeroSection.js文件,输入rafce
生成
1 2 3 4 5 6 7 8 9
| import React from 'react'
const HeroSection = () => { return ( <div>HeroSection</div> ) }
export default HeroSection
|
2)在App.js
引入HeroSection组件
1 2 3 4 5 6 7 8 9 10 11
| import HeroSection from './sections/HeroSection';
function App() { return ( <> <HeroSection /> </> ); }
export default App;
|
3)编写HeroSection.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
| import React from 'react' import styled from "styled-components"; import backgroundVideo from "../assets/video/Ink - 21536.mp4";
const Section = styled.section` width: 100vw; height: 100vh; position: relative;
display: flex; justify-content: flex-end; align-items: center;
background-color: var(--dark); overflow: hidden; `;
const Title = styled.h1` position: absolute; top: 2rem; left: 2rem;
font-size: var(--fontlg); font-family: var(--fontL); color: var(--greyLight);
@media screen and (max-width: 48em) { font-size: var(--fontmd); left: 1rem; }
@media screen and (max-width: 30em) { width: 70%; color: var(--white); } `; const TextContainer = styled.div` width: 100%; height: 100vh;
display: flex; justify-content: space-between; align-items: center;
background-image: linear-gradient(45deg, var(--gradient)); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; z-index: 1;
span { font-size: var(--fontxxxl); text-transform: uppercase; font-weight: 600; padding: 2rem;
@media screen and (max-width: 64em) { font-size: var(--fontxxl); padding: 0; } @media screen and (max-width: 48em) { font-size: var(--fontxl); } }
@media screen and (max-width: 48em) { flex-direction: column; background-image: linear-gradient(90deg, var(--gradient)); align-items: flex-start; filter: brightness(1.1);
& > *:last-child { align-self: flex-end; }
height: 80vh; padding: 0 1rem; } `;
const VideoContainer = styled.div` width: 100vw; min-height: 100vh;
position: absolute; top: 0; left: 0; z-index: 0;
video { width: 100%; height: 100vh; object-fit: cover; object-position: bottom; } `;
const HeroSection = () => { return ( <Section> <VideoContainer> <video src={backgroundVideo} type="video/mp4" autoPlay muted loop /> </VideoContainer> <Title>iPhone 14 Pro Max</Title> <TextContainer> <span>So.Cold.</span> <span>So.Bold.</span> </TextContainer> </Section> ) }
export default HeroSection
|
模型格式转化
1)gltf格式的3D模型需要格式转化
使用github项目:https://github.com/pmndrs/gltfjsx
使用
1 2
| npx gltfjsx model.gltf --transform // 指定model.gltf,生成model.jsx文件
|
编写HeroSection.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import React from 'react' import styled from "styled-components"; import { Canvas } from "@react-three/fiber"; import { OrbitControls } from "@react-three/drei"; import { Model } from "../assets/3D-Model/Scene" import { Environment } from "@react-three/drei";
const Container = styled.div` width: 100vw; height: 100vh; position: fixed; top: 0; z-index: 1; background-color: transparent; transition: all 0.3s ease; `;
const PhoneModel = () => { return ( <Container id="phone-model"> <Canvas> <ambientLight intensity={1.25} /> <directionalLight intensity={0.4} /> <Model /> <Environment preset="night" /> {/* <mesh> <boxGeometry /> <meshStandardMaterial color="red" /> </mesh> */} <OrbitControls /> </Canvas> </Container> ) }
export default PhoneModel
|
2)环境光可以参考:
1
| <Environment preset="night" />
|
https://docs.pmnd.rs/