Created
May 4, 2021 11:03
-
-
Save jesperlandberg/6a0891308ba15afacb0649d4f87cc97c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gsap from 'gsap' | |
import { Renderer, Camera, Transform, Post, Vec2 } from '@/lib/ogl' | |
import { evt, u, store } from '@/app/index' | |
import Pool from './Pool' | |
import { Bg } from './components' | |
import fragment from './shaders/pass/frag.frag' | |
const { bounds, dom, flags } = store | |
const { qs } = u | |
export default new (class { | |
constructor() { | |
const { ww, wh } = bounds | |
this.renderer = new Renderer({ | |
dpr: gsap.utils.clamp(1, 1.5, window.devicePixelRatio), | |
alpha: true, | |
premultipliedAlpha: true, | |
}) | |
this.renderer.setSize(ww, wh) | |
this.gl = this.renderer.gl | |
this.gl.canvas.classList.add('gl', 'js-gl') | |
this.gl.clearColor(0, 0, 0, 0) | |
this.camera = new Camera(this.gl, { | |
fov: 45, | |
aspect: ww / wh, | |
near: 0.1, | |
far: 100 | |
}) | |
this.camera.position.z = 50 | |
this.post = new Post(this.gl) | |
this.res = { value: new Vec2() } | |
this.post.resize() | |
this.res.value.set(this.gl.canvas.width, this.gl.canvas.height) | |
this.pass = this.post.addPass({ | |
fragment, | |
uniforms: { | |
u_pinch: { value: 0 }, | |
u_res: this.res, | |
} | |
}) | |
this.scene = new Transform() | |
this.time = 0 | |
this.lastScroll = 0 | |
this.offsetScroll = 0 | |
} | |
init() { | |
this.initial = true | |
this.scene.addChild(Pool.planesGroup) | |
this.scene.addChild(Pool.gridGroup) | |
//this.bg = new Bg() | |
//this.bg.init() | |
dom.body.appendChild(this.gl.canvas) | |
this.addEvents() | |
} | |
addEvents() { | |
evt.on('resize:on-reset', this.resize) | |
} | |
run = ({ scroll, diff }) => { | |
this.planes = Pool.planesGroup.children | |
this.scroll = scroll | |
this.time += 1 | |
this.planes.length | |
&& this.transformPlanes(this.scroll, diff) | |
this.post.render({ | |
scene: this.scene, | |
camera: this.camera | |
}) | |
this.lastScroll = this.scroll | |
} | |
transformPlanes(scroll = this.scroll, diff = 0) { | |
if (flags.transition) return | |
this.planes.forEach((p) => { | |
if (!p.program | |
|| p.static | |
) return | |
const visible = this.visible(p.bounds, scroll) | |
if (visible || this.resizing) { | |
p.out && (p.out = false) | |
p.vid && !p.vid.active && (p.vid.play(), p.vid.active = true) | |
p.onRaf(scroll, diff) | |
} else if (!p.out) { | |
p.out = true | |
p.vid && p.vid.active && (p.vid.pause(), p.vid.active = false) | |
p.onRaf(scroll, diff) | |
} | |
}) | |
} | |
visible({ start, end }, current) { | |
return current > start && current <= end | |
} | |
resize = () => { | |
const { ww, wh } = bounds | |
this.resizing = true | |
this.camera.updateMatrixWorld() | |
this.renderer.setSize(ww, wh) | |
Pool.resize() | |
this.transformPlanes() | |
this.resizing = false | |
} | |
removeEvents() { | |
evt.off('tick', this.run) | |
evt.off('resize:on-reset', this.resize) | |
} | |
destroy = () => { | |
this.removeEvents() | |
Pool.removePlanes() | |
} | |
})() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import gsap from 'gsap' | |
import { store, u, p } from '@/app/index' | |
import { Transform } from '@/lib/ogl' | |
import Core from './Core' | |
const { dom, bounds } = store | |
const { qs, rect } = u | |
export default class extends Transform { | |
init(args = {}) { | |
this.args = args | |
this.el = this.args.el | |
this.pid = this.setPid() | |
this.name = this.args.name | |
this.progress = { | |
x: 0, | |
y: 0, | |
} | |
this.static = false | |
this.out = true | |
this.setBounds() | |
} | |
update(parent = dom.body) { | |
this.el = qs(`[data-gl-id="${this.name}"]`, parent) | |
this.pid = this.setPid() | |
this.resize() | |
} | |
setPid(el = this.el) { | |
return el.closest('[data-id]') | |
&& el.closest('[data-id]').dataset.id | |
} | |
setBounds() { | |
const { wh } = bounds | |
const { | |
left, right, | |
top, bottom, | |
width, height | |
} = rect(this.el) | |
this.bounds = { | |
left, right, width, height, top, | |
start: top - wh, | |
end: bottom, | |
} | |
this.updateSize() | |
this.updateY() | |
this.updateX() | |
} | |
resize() { | |
if (!this.visible) return | |
this.setBounds() | |
this.updateX(0) | |
} | |
calculateUnitSize(distance = this.position.z) { | |
const vFov = (Core.camera.fov * Math.PI) / 180 | |
const height = 2 * Math.tan(vFov / 2) * distance | |
const width = height * Core.camera.aspect | |
return { | |
width, | |
height, | |
} | |
} | |
updateSize() { | |
this.camUnit = this.calculateUnitSize( | |
Core.camera.position.z - this.position.z, | |
) | |
const { ww, wh } = bounds | |
const x = this.bounds.width / ww | |
const y = this.bounds.height / wh | |
if (!x || !y) return | |
this.scale.x = this.camUnit.width * x | |
this.scale.y = this.camUnit.height * y | |
} | |
updateY(y = 0) { | |
const { wh } = bounds | |
const { top } = this.bounds | |
this.position.y = this.camUnit.height / 2 - this.scale.y / 2 | |
this.position.y -= ((top - y) / wh) * this.camUnit.height | |
} | |
updateX(x = 0) { | |
const { ww } = bounds | |
const { left } = this.bounds | |
this.position.x = -(this.camUnit.width / 2) + this.scale.x / 2 | |
this.position.x += ((left + x) / ww) * this.camUnit.width | |
} | |
getSize(bounding, oX = 0, oY = 0) { | |
this.camUnit = this.calculateUnitSize( | |
Core.camera.position.z - this.position.z, | |
) | |
let scale = {}, pX, pY | |
const { ww, wh } = bounds | |
const { width, height, top, left } = bounding | |
const x = width / ww | |
const y = height / wh | |
scale.x = this.camUnit.width * x | |
scale.y = this.camUnit.height * y | |
pY = this.camUnit.height / 2 - scale.y / 2 | |
pY -= ((top - oY) / wh) * this.camUnit.height | |
pX = -(this.camUnit.width / 2) + scale.x / 2 | |
pX += ((left + oX) / ww) * this.camUnit.width | |
return { | |
scale, pX, pY | |
} | |
} | |
onRaf(y = 0, diff = 0) { | |
!this.static && this.updateY(y, diff) | |
} | |
destroy() { | |
this.parent && this.parent.removeChild(this) | |
this.visible = this.static = false | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Core from './Core' | |
import Elem from './Elem' | |
import Pool from './Pool' | |
export { | |
Core, | |
Pool, | |
Elem, | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
Mesh, | |
Plane, | |
Program, | |
Texture, | |
Vec2 | |
} from '@/lib/ogl' | |
import gsap from 'gsap' | |
import Core from '../Core' | |
import Elem from '../Elem' | |
import Pool from '../Pool' | |
import vertex from '@/gl/shaders/plane/vert.vert' | |
import fragment from '@/gl/shaders/plane/frag.frag' | |
import { evt, store, u, p } from '@/app/index' | |
const { bounds, dom, flags } = store | |
const { qs, rect } = u | |
export default class extends Elem { | |
init(args) { | |
const { gl } = Core | |
super.init(args) | |
this.geometry = new Plane(gl, {widthSegments: 25, heightSegments: 25}) | |
this.texture = new Texture(gl, { | |
generateMipmaps: false, | |
minFilter: gl.LINEAR, | |
}) | |
const src = this.el.dataset.src || this.el.dataset.glSrc | |
if (!src) return | |
const type = src.split('.').pop() | |
if (type !== 'mp4') { | |
const img = new Image() | |
img.crossOrigin = '*' | |
img.src = src | |
img.decode().then(() => { | |
const { u_size } = this.program.uniforms | |
this.texture.image = img | |
u_size.value.x = img.naturalWidth | |
u_size.value.y = img.naturalHeight | |
}) | |
} else { | |
this.vid = document.createElement('video') | |
this.vid.src = src | |
this.vid.loop = true | |
this.vid.muted = true | |
this.vid.play() | |
this.vid.oncanplay = () => { | |
const { u_size } = this.program.uniforms | |
u_size.value.x = this.vid.videoWidth | |
u_size.value.y = this.vid.videoHeight | |
} | |
} | |
this.program = new Program(gl, { | |
vertex, | |
fragment, | |
uniforms: { | |
u_texture: { value: this.texture }, | |
u_toRes: { value: new Vec2(0, 0) }, | |
u_toPos: { value: new Vec2(0, 0) }, | |
u_res: { value: new Vec2(this.bounds.width, this.bounds.height) }, | |
u_pos: { value: new Vec2(this.position.x, this.position.y) }, | |
u_offset: { value: new Vec2(this.camUnit.width, this.camUnit.height) }, | |
u_size: { value: new Vec2(1, 1) }, | |
u_progress: { value: 0 }, | |
u_time: { value: 0 }, | |
u_alpha: { value: 1 }, | |
}, | |
cullFace: null, | |
depthTest: false | |
}) | |
this.mesh = new Mesh(gl, { | |
geometry: this.geometry, | |
program: this.program, | |
}) | |
this.addChild(this.mesh) | |
Pool.planesGroup.addChild(this) | |
Pool.planes[this.name] = this | |
this.onAdd() | |
} | |
onAdd() { | |
} | |
updateVid() { | |
if (this.vid.readyState >= this.vid.HAVE_ENOUGH_DATA) { | |
!this.texture.image && (this.texture.image = this.vid) | |
this.texture.needsUpdate = true | |
} | |
} | |
updateY(y = 0, diff = 0) { | |
super.updateY(y) | |
this.program && (this.program.uniforms.u_time.value = Core.time) | |
this.vid && this.updateVid() | |
} | |
transition = (to) => { | |
const { u_progress, u_toPos, u_toRes, u_pos } = this.program.uniforms | |
const nBounds = rect(qs('.js-gl-hero', to)) | |
const nSize = this.getSize( | |
nBounds, | |
0, | |
0 | |
) | |
u_toPos.value.x = nSize.pX | |
u_toPos.value.y = nSize.pY | |
u_toRes.value.x = nBounds.width | |
u_toRes.value.y = nBounds.height | |
gsap.timeline() | |
.set(u_pos.value, { | |
x: this.position.x, | |
y: this.position.y, | |
}) | |
.to(u_progress, { | |
value: 1, | |
duration: 1.25, | |
ease: 'power3.inOut' | |
}, 0) | |
.add(() => { | |
this.update() | |
this.keep = false | |
this.static = false | |
}) | |
} | |
resize() { | |
super.resize() | |
const { u_res, u_pos, u_progress } = this.program.uniforms | |
const { width, height } = this.bounds | |
const { x, y } = this.position | |
u_progress.value = 0 | |
u_res.value.x = width | |
u_res.value.y = height | |
u_pos.value.x = x | |
u_pos.value.y = y | |
} | |
destroy() { | |
super.destroy() | |
this.mesh.renderOrder = 1 | |
this.out = true | |
this.animated = false | |
} | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Transform } from '@/lib/ogl' | |
import gsap from 'gsap' | |
import Elem from './Elem' | |
import * as Component from './components' | |
import { store, u, evt } from '@/app/index' | |
const { bounds, flags } = store | |
const { qs, qsa } = u | |
export default new (class { | |
constructor() { | |
this.planes = {} | |
this.planesGroup = new Transform() | |
this.gridGroup = new Transform() | |
evt.on('gl:removePlanes', this.removePlanes) | |
} | |
fromDom(el, parent) { | |
const name = el.dataset.glId | |
const component = el.dataset.glComponent | |
const plane = this.planes[name] | |
if (plane && !plane.keep) { | |
this.add(plane, parent) | |
} else if (!plane) { | |
if (component) { | |
new Component[component]().init({ el, name }) | |
} else { | |
new Component['Plane']().init({ el, name }) | |
} | |
} | |
} | |
addPlanes({ el, elems }) { | |
elems && elems.forEach(elem => this.fromDom(elem, el)) | |
} | |
add(p, parent) { | |
if (p.visible) return | |
this.planesGroup.addChild(p) | |
p.visible = true | |
p.program.uniforms.u_alpha.value = 1 | |
p.update(parent) | |
p.onAdd && p.onAdd() | |
} | |
resize(el = null) { | |
for (const name in this.planes) this.planes[name].resize(el) | |
} | |
setStatic() { | |
this.planesGroup.children.forEach( | |
(p) => (p.static = true), | |
) | |
} | |
removePlanes = (pid) => { | |
for (const name in this.planes) this.remove(this.planes[name], pid) | |
} | |
removePlane(plane) { | |
(!plane.out && flags.case) ? gsap.to(plane.program.uniforms.u_alpha, { | |
value: 0, | |
duration: .5, | |
ease: 'power1', | |
onComplete: () => plane.destroy() | |
}) : plane.destroy() | |
} | |
remove(plane, pid) { | |
!pid | |
? this.removePlane(plane) | |
: (!plane.keep && (pid === plane.pid) && this.removePlane(plane)) | |
} | |
})() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment