-
Notifications
You must be signed in to change notification settings - Fork 0
Implement Performance Section #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| import { Canvas } from '@react-three/fiber' | ||
| import StudioLights from './three/StudioLights.jsx' | ||
| import { features, featureSequence } from '../constants/index.js' | ||
| import clsx from 'clsx' | ||
| import { Suspense, useEffect, useRef } from 'react' | ||
| import { Html } from '@react-three/drei' | ||
| import MacbookModel from './models/Macbook.jsx' | ||
| import { useMediaQuery } from 'react-responsive' | ||
| import useMacbookStore from '../store/index.js' | ||
| import { useGSAP } from '@gsap/react' | ||
| import gsap from 'gsap' | ||
|
|
||
| const ModelScroll = () => { | ||
| const groupRef = useRef(null) | ||
| const isMobile = useMediaQuery({ query: '(max-width: 1024px)' }) | ||
| const { setTexture } = useMacbookStore() | ||
|
|
||
| useEffect(() => { | ||
| featureSequence.forEach((feature) => { | ||
| const v = document.createElement('video') | ||
| Object.assign(v, { | ||
| src: feature.videoPath, | ||
| muted: true, | ||
| playsInline: true, | ||
| preload: 'auto', | ||
| crossOrigen: 'anonymous' | ||
| }) | ||
| v.load() | ||
| }) | ||
| }, []) | ||
|
|
||
| useGSAP(() => { | ||
| const modelTimeLine = gsap.timeline({ | ||
| scrollTrigger: { | ||
| trigger: '#f-canvas', | ||
| start: 'top top', | ||
| end: 'bottom top', | ||
| scrub: 1, | ||
| pin: true | ||
| } | ||
| }) | ||
|
|
||
| const timeline = gsap.timeline({ | ||
| scrollTrigger: { | ||
| trigger: '#f-canvas', | ||
| start: 'top center', | ||
| end: 'bottom top', | ||
| scrub: 1 | ||
| } | ||
| }) | ||
|
|
||
| if (groupRef.current) { | ||
| modelTimeLine.to(groupRef.current.rotation, { | ||
| y: Math.PI * 2, | ||
| ease: 'power1.inOut' | ||
| }) | ||
| } | ||
|
|
||
| timeline | ||
| .call(() => setTexture('/videos/feature-1.mp4')) | ||
| .to('.box1', { opacity: 1, y: 0, delay: 1 }) | ||
|
|
||
| .call(() => setTexture('/videos/feature-2.mp4')) | ||
| .to('.box2', { opacity: 1, y: 0 }) | ||
|
|
||
| .call(() => setTexture('/videos/feature-3.mp4')) | ||
| .to('.box3', { opacity: 1, y: 0 }) | ||
|
|
||
| .call(() => setTexture('/videos/feature-4.mp4')) | ||
| .to('.box4', { opacity: 1, y: 0 }) | ||
|
|
||
| .call(() => setTexture('/videos/feature-5.mp4')) | ||
| .to('.box5', { opacity: 1, y: 0 }) | ||
| }, []) | ||
|
|
||
| return ( | ||
| <group ref={groupRef}> | ||
| <Suspense | ||
| fallback={ | ||
| <Html> | ||
| <h1 className="text-white text-3xl uppercase">Loading...</h1> | ||
| </Html> | ||
| } | ||
| > | ||
| <MacbookModel | ||
| scale={isMobile ? 0.05 : 0.08} | ||
| position={[0, -1, 0]} | ||
| /> | ||
| </Suspense> | ||
| </group> | ||
| ) | ||
| } | ||
|
|
||
| const Features = () => { | ||
| return ( | ||
| <section id="features"> | ||
| <h2>See it all in a new light.</h2> | ||
|
|
||
| <Canvas | ||
| id="f-canvas" | ||
| camera={{}} | ||
| > | ||
| <StudioLights /> | ||
| <ambientLight intensity={0.5} /> | ||
| <ModelScroll /> | ||
| </Canvas> | ||
|
|
||
| <div className="absolute inset-0"> | ||
| {features.map((feature, index) => ( | ||
| <div | ||
| key={feature.id} | ||
| className={clsx('box', `box${index + 1}`, feature.styles)} | ||
| > | ||
| <img | ||
| src={feature.icon} | ||
| alt={feature.highlight} | ||
| /> | ||
| <p> | ||
| <span className="text-white">{feature.highlight}</span> | ||
| {feature.text} | ||
| </p> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </section> | ||
| ) | ||
| } | ||
|
|
||
| export default Features | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,34 @@ | ||||||
| import { footerLinks } from "../constants" | ||||||
|
|
||||||
| const Footer = () => { | ||||||
| return ( | ||||||
| <footer> | ||||||
| <div className="'info"> | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo in className breaks styling. The className has an extra single quote: Proposed fix- <div className="'info">
+ <div className="info">📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| <p> | ||||||
| More ways to shop: Find an Apple Store or other retailer near you. Or | ||||||
| call 000800 040 1966. | ||||||
| </p> | ||||||
| <img | ||||||
| src="/logo.svg" | ||||||
| alt="Apple logo" | ||||||
| /> | ||||||
| </div> | ||||||
|
|
||||||
| <hr /> | ||||||
|
|
||||||
| <div className="links"> | ||||||
| <p>Copyright © 2024 Apple Inc. All rights reserved.</p> | ||||||
|
|
||||||
| <ul> | ||||||
| {footerLinks.map(({ label, link }) => ( | ||||||
| <li key={label}> | ||||||
| <a href={link}>{label}</a> | ||||||
| </li> | ||||||
| ))} | ||||||
| </ul> | ||||||
| </div> | ||||||
| </footer> | ||||||
| ) | ||||||
| } | ||||||
|
|
||||||
| export default Footer | ||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useGSAP } from '@gsap/react' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useMediaQuery } from 'react-responsive' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import gsap from 'gsap' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const Highlights = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isMobile = useMediaQuery({ query: '(max-width: 1024px)' }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| useGSAP(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gsap.to(['.left-column', '.right-column'], { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| scrollTrigger: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| trigger: '#highlights', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| start: isMobile ? 'bottom bottom ' : 'top top' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| y: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| opacity: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| stagger: 0.5, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| duration: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ease: 'power1.inOut' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing dependency array causes stale The Proposed fix useGSAP(() => {
gsap.to(['.left-column', '.right-column'], {
scrollTrigger: {
trigger: '#highlights',
- start: isMobile ? 'bottom bottom ' : 'top top'
+ start: isMobile ? 'bottom bottom' : 'top top'
},
y: 0,
opacity: 1,
stagger: 0.5,
duration: 1,
ease: 'power1.inOut'
})
- })
+ }, { dependencies: [isMobile] })📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <section id="highlights"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h2>There’s never been a better time to upgrade.</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h3>Here’s what you get with the new MacBook Pro.</h3> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="masonry"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="left-column"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src="/laptop.png" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt="Laptop" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>Fly through demanding tasks up to 9.8x faster.</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src="/sun.png" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt="Sun" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p>A stunning Liquid Retina XDR display.</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="right-column"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="apple-gradient"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src="/ai.png" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt="AI" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Built for <br /> <span>Apple Intelligence.</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| src="/battery.png" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| alt="Battery" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Up to <span className="green-gradient"> 14 more hours </span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| battery life.{' '} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <span className="text-dark-100">(Up to 24 hours total.)</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </section> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default Highlights | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| import { useRef } from 'react' | ||
| import { useGSAP } from '@gsap/react' | ||
| import { gsap } from 'gsap' | ||
| import { | ||
| performanceImages, | ||
| performanceImgPositions | ||
| } from '../constants/index.js' | ||
| import { useMediaQuery } from 'react-responsive' | ||
|
|
||
| const Performance = () => { | ||
| const isMobile = useMediaQuery({ query: '(max-width: 1024px)' }) | ||
| const sectionRef = useRef(null) | ||
|
|
||
| useGSAP( | ||
| () => { | ||
| const sectionEl = sectionRef.current | ||
| if (!sectionEl) return | ||
|
|
||
| gsap.fromTo( | ||
| '.content p', | ||
| { opacity: 0, y: 10 }, | ||
| { | ||
| opacity: 1, | ||
| y: 0, | ||
| ease: 'power1.out', | ||
| scrollTrigger: { | ||
| trigger: '.content p', | ||
| start: 'top bottom', | ||
| end: 'top center', | ||
| scrub: true, | ||
| invalidateOnRefresh: true | ||
| } | ||
| } | ||
| ) | ||
|
|
||
| if (isMobile) return | ||
|
|
||
| const tl = gsap.timeline({ | ||
| defaults: { duration: 2, ease: 'power1.inOut', overwrite: 'auto' }, | ||
| scrollTrigger: { | ||
| trigger: sectionEl, | ||
| start: 'top bottom', | ||
| end: 'bottom top', | ||
| scrub: 1, | ||
| invalidateOnRefresh: true | ||
| } | ||
| }) | ||
|
|
||
| performanceImgPositions.forEach((item) => { | ||
| if (item.id === 'p5') return | ||
|
|
||
| const selector = `.${item.id}` | ||
| const vars = {} | ||
|
|
||
| if (typeof item.left === 'number') vars.left = `${item.left}%` | ||
| if (typeof item.right === 'number') vars.right = `${item.right}%` | ||
| if (typeof item.bottom === 'number') vars.bottom = `${item.bottom}%` | ||
|
|
||
| if (item.transform) vars.transform = item.transform | ||
|
|
||
| tl.to(selector, vars, 0) | ||
| }) | ||
| }, | ||
| { scope: sectionRef, dependencies: [isMobile] } | ||
| ) | ||
|
|
||
| return ( | ||
| <section | ||
| id="performance" | ||
| ref={sectionRef} | ||
| > | ||
| <h2>Next-level graphics performance. Game on.</h2> | ||
|
|
||
| <div className="wrapper"> | ||
| {performanceImages.map((item, index) => ( | ||
| <img | ||
| key={index} | ||
| src={item.src} | ||
| className={item.id} | ||
| alt={item.alt || `Performance Image #${index + 1}`} | ||
| /> | ||
| ))} | ||
| </div> | ||
|
|
||
| <div className="content"> | ||
| <p> | ||
| Run graphics-intensive workflows with a responsiveness that keeps up | ||
| with your imagination. The M4 family of chips features a GPU with a | ||
| second-generation hardware-accelerated ray tracing engine that renders | ||
| images faster, so{' '} | ||
| <span className="text-white"> | ||
| gaming feels more immersive and realistic than ever. | ||
| </span>{' '} | ||
| And Dynamic Caching optimizes fast on-chip memory to dramatically | ||
| increase average GPU utilization — driving a huge performance boost | ||
| for the most demanding pro apps and games. | ||
| </p> | ||
| </div> | ||
| </section> | ||
| ) | ||
| } | ||
| export default Performance |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo
crossOrigenand missing cleanup for preloaded videos.crossOrigenshould becrossOrigin(HTML attribute spelling).Proposed fix
useEffect(() => { + const videos = [] featureSequence.forEach((feature) => { const v = document.createElement('video') Object.assign(v, { src: feature.videoPath, muted: true, playsInline: true, preload: 'auto', - crossOrigen: 'anonymous' + crossOrigin: 'anonymous' }) v.load() + videos.push(v) }) + return () => { + videos.forEach((v) => { + v.src = '' + v.load() + }) + } }, [])📝 Committable suggestion
🤖 Prompt for AI Agents