codepens/mars-explorer-scrolltrigger.../dist/script.js

280 lines
8.4 KiB
JavaScript
Raw Normal View History

2023-10-06 23:12:53 +02:00
Math.degToRad = degrees => degrees * (Math.PI / 180);
class Scene {
constructor(urls) {
this._urls = urls;
}
init() {
return new Promise(resolve => {
let models = [];
const manager = new THREE.LoadingManager(() => {
this.setupCanvas(models);
resolve();
});
const loader = new THREE.GLTFLoader(manager);
this._urls.forEach(x => loader.load(x.url, gltf => {
gltf.scene.visible = x.visible;
models.push({ id: x.id, scene: gltf.scene });
}));
});
}
setupCanvas(models) {
// Camera
this.camera = new THREE.PerspectiveCamera(25, window.innerWidth / window.innerHeight, 1, 30000);
this.camera.position.set(0, 0, 4000);
// Scene
this.scene = new THREE.Scene();
// Renderer
this.renderer = new THREE.WebGLRenderer();
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.renderer.outputEncoding = THREE.sRGBEncoding;
this.renderer.setPixelRatio(window.devicePixelRatio);
// Light
const ambientLight = new THREE.AmbientLight(0x111111);
this.scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(2, 0, 1).normalize();
this.scene.add(directionalLight);
models.forEach(x => {
this[x.id] = x.scene;
this.scene.add(this[x.id]);
});
document.body.appendChild(this.renderer.domElement);
this.render();
this.animate();
window.addEventListener('resize', () => {
this.camera.aspect = window.innerWidth / window.innerHeight;
this.camera.updateProjectionMatrix();
this.renderer.setSize(window.innerWidth, window.innerHeight);
this.render();
}, false);
}
animate() {
this.render();
requestAnimationFrame(() => this.animate());
}
render() {
this.renderer.render(this.scene, this.camera);
}}
class Page {
constructor(scene) {
this._scene = scene;
}
get mars() {
return this._scene.mars;
}
get deimos() {
return this._scene.deimos;
}
get camera() {
return this._scene.camera;
}
get content() {
return [
{
title: 'Olympus Mons',
coords: {
Latitude: '18.65275889',
Longitude: '-133.8025067' },
selector: '.olympus' },
{
title: 'Gale Crater',
coords: {
Latitude: '136.783441',
Longitude: '-5.10837943' },
selector: '.curiosity' },
{
title: 'Valles Marineris',
coords: {
Latitude: '-14.00586857',
Longitude: '-58.5876741' },
selector: '.valles' },
{
title: 'Medusae Fossae',
coords: {
Latitude: '-2.166284249',
Longitude: '-164.1956286' },
selector: '.medusae' },
{
title: 'Cydonia',
coords: {
Latitude: '39.66724753',
Longitude: '0' },
selector: '.cydonia' },
{
title: 'Tharsis Volcanic Region',
coords: {
Latitude: '-2.623623306',
Longitude: '-116.7076259' },
selector: '.tharsis' },
{
title: 'Deimos',
coords: {
Distance: '23,460 km',
Radius: '6.2 km' },
selector: '.deimos' }];
}
async init() {
window.onbeforeunload = function () {
window.scrollTo(0, 0);
};
await this._scene.init();
document.querySelector('.loader').style.display = 'none';
this.setupPlugins();
this.setupAnimScrollTrigger();
this.setupContentScrollTrigger();
}
setupPlugins() {
gsap.registerPlugin(ScrollTrigger);
gsap.registerPlugin(TextPlugin);
gsap.registerPlugin(DrawSVGPlugin);
}
setupAnimScrollTrigger() {
const opening = new gsap.timeline().
fromTo(this.camera.position, { z: 2000 }, { z: 4000, ease: 'Circ.easeOut', duration: 1.5 }, 'opening').
fromTo(this.camera.rotation, { y: -Math.degToRad(30), x: -Math.degToRad(-5) }, { y: 0, x: 0, ease: 'Circ.easeOut', duration: 1.5 }, 'opening').
set(".scroller, .watermark", { visibility: "visible" });
const rotation = new gsap.timeline({
scrollTrigger: {
toggleActions: "play pause play pause",
trigger: ".welcome",
start: "top bottom",
end: "top -1" } }).
to(this.mars.rotation, { y: Math.degToRad(360), ease: 'none', repeat: -1, duration: 30 });
const animation = new gsap.timeline({
scrollTrigger: {
trigger: ".content",
scrub: true,
start: "top bottom",
end: "bottom bottom"
//markers: {startColor: "green", endColor: "red", fontSize: "12px"}
} }).
set(".nav svg, .circle svg", { visibility: "visible" }).
to(this.mars.rotation, { y: Math.degToRad(133.8025067 + 90), x: Math.degToRad(18.65275889), ease: 'power2.inOut', duration: 0.75 }, 0).
to(this.camera.position, { z: 3000, ease: 'linear', duration: 0.75 }, 0).
set(".scroller", { visibility: "hidden" }, 0.2).
to(this.mars.rotation, { y: -Math.degToRad(136.78344100200877 - 90), x: Math.degToRad(5.1083794384352), ease: 'power2.inOut', duration: 0.75 }, 1).
to(this.mars.rotation, { y: Math.degToRad(58.5876741 + 90), x: Math.degToRad(-14.00586857), ease: 'power2.inOut', duration: 0.75 }, 2).
to(this.mars.rotation, { y: Math.degToRad(164.1956286 + 90), x: Math.degToRad(-2.166284249), ease: 'power2.inOut', duration: 0.75 }, 3).
to(this.mars.rotation, { y: Math.degToRad(90), x: Math.degToRad(39.66724753), ease: 'power2.inOut', duration: 0.75 }, 4).
to(this.mars.rotation, { y: Math.degToRad(116.7076259 + 90), x: Math.degToRad(-2.623623306), ease: 'power2.inOut', duration: 0.75 }, 5).
to(this.camera.position, { z: 22000, ease: 'linear' }, 6).
to(this.camera.rotation, { z: Math.degToRad(10), y: Math.degToRad(10), ease: 'power2.inOut', duration: 0.75 }, 6).
set(this.deimos, { visible: true }, 6).
to(this.deimos.scale, { x: 7, y: 7, z: 7, ease: 'linear', duration: 0 }, 6).
fromTo(this.deimos.position, { x: -500, y: 0, z: 22000 }, { x: -170, y: 0, z: 21000, ease: 'power2.inOut', duration: 0.75 }, 6);
}
setupContentScrollTrigger() {
const onUpdate = function () {
const target = this.targets()[0];
const time = this.time();
const duration = this.duration();
if (time >= duration || time <= 0) {
target.classList.remove('editing');
return;
}
if (!target.classList.contains('editing')) {
target.classList.add('editing');
}
};
const getLines = obj => {
const lines = [];
Object.keys(obj).forEach(x => {
lines.push(`${x}: ${obj[x]}`);
});
return lines;
};
ScrollTrigger.addEventListener("scrollEnd", function (e) {
const target = document.querySelectorAll('.editing')[0];
if (target) {
target.classList.add('blink');
}
});
ScrollTrigger.addEventListener("scrollStart", function (e) {
const target = document.querySelectorAll('.editing')[0];
if (target) {
target.classList.remove('blink');
}
});
this.content.forEach((item, i, arr) => {
const timeline = new gsap.timeline({
scrollTrigger: {
trigger: item.selector,
scrub: true,
start: 'top 75%',
end: `bottom ${i < arr.length - 1 ? '75%' : 'bottom'}`
//markers: {startColor: "green", endColor: "red", fontSize: "12px"}
} }).
to(`${item.selector} .title`, { text: `${item.title}`, ease: 'linear', duration: 0.25, onUpdate }, 0).
to(`${item.selector} .lat`, { text: `${getLines(item.coords)[0]}`, ease: 'linear', duration: 0.125, onUpdate }, 0.25).
to(`${item.selector} .lon`, { text: `${getLines(item.coords)[1]}`, ease: 'linear', duration: 0.125, onUpdate }, 0.375).
fromTo(`${item.selector} polyline`, { drawSVG: 0 }, { drawSVG: '100%', duration: 0.125 }, 0.5).
fromTo('#circle', { drawSVG: 0 }, { drawSVG: '100%', duration: 0.125 }, 0.625).
set(`${item.selector} .image-container`, { visibility: 'visible' }).
fromTo(`${item.selector} .image-container`, { width: '0%' }, { width: '25vw', duration: 0.125 }, 0.75).
fromTo(`${item.selector} .image-container`, { height: '0%' }, { height: 'auto', duration: 0.125 }, 0.875);
if (i < arr.length - 1) {
timeline.yoyo(true).
repeat(1).
repeatDelay(0.5);
}
});
}}
const urls = [
{
id: 'mars',
url: 'https://assets.codepen.io/1443237/Mars_1_6792.glb',
visible: true },
{
id: 'deimos',
url: 'https://assets.codepen.io/1443237/Deimos_1_1000.glb',
visible: false }];
new Page(new Scene(urls)).init();