T r a v i s ',
25 | index: '
0 2 ',
26 | detailHTML: `
27 |
28 |
29 |
30 |
31 |
32 | `,
33 | 'dF': 'Typography',
34 | 'dN': 'Travis'
35 | },
36 | {
37 | bg: '#1F3D77', c: '#B5CBF3', image1: 'collections/Jesperish1.jpg', image2: 'collections/Jesperish2.jpg',
38 | flavour: '
D I G I T A L P A I N T I N G ',
39 | name: '
J e s p e r i s h ',
40 | index: '
0 3 ',
41 | detailHTML: `
42 |
43 |
44 |
45 |
46 |
47 |
48 |
`,
49 | 'dF': 'Digital Painting',
50 | 'dN': 'Jesperish'
51 | },
52 | {
53 | bg: '#B2E8D7', c: '#1B795F', image1: 'collections/cherifkid1.jpg', image2: 'collections/cherifkid2.png',
54 | flavour: '
D I G I T A L P A I N T I N G ',
55 | name: '
C H E R I F K I D ',
56 | index: '
0 4 ',
57 | detailHTML: `
58 |
59 |
60 |
61 |
62 |
63 |
`,
64 | 'dF': 'Digital Painting',
65 | 'dN': 'CherifKid'
66 | },
67 | {
68 | bg: '#FAE27C', c: '#1C1B1D', image1: 'collections/raegular1.jpg', image2: 'collections/raegular2.jpg',
69 | flavour: '
G r a p h i s t ',
70 | name: '
r a e g u l a r ',
71 | index: '
0 5 ',
72 | detailHTML: `
73 |
74 |
75 |
76 |
77 |
78 |
`,
79 | 'dF': 'Graphist',
80 | 'dN': 'Raegular'
81 | },
82 | {
83 | bg: '#131721', c: '#DA2607', image1: 'collections/wrath1.png', image2: 'collections/wrath2.png',
84 | flavour: '
I A ',
85 | name: '
M i d j o u r n e y ',
86 | index: '
0 6 ',
87 | detailHTML: `
88 |
89 |
90 |
91 |
92 |
93 |
`,
94 | 'dF': 'IA',
95 | 'dN': 'Midjourney'
96 | }, {
97 | bg: '#023025', c: '#5DAB98', image1: 'collections/jardel1.jpg', image2: 'collections/jardel2.jpg',
98 | flavour: '
t y p e f a c e d e s i g n ',
99 | name: '
j a r d e 1 l ',
100 | index: '
0 7 ',
101 | detailHTML: `
102 |
103 |
104 |
105 |
106 |
107 |
`,
108 | 'dF': 'Typface design',
109 | 'dN': 'Jarde1l'
110 | },
111 | ]
112 | }
113 | increaseCounter() {
114 | this.currentPage++
115 | if (this.currentPage === this.collectionsInfo.length) this.currentPage = 0
116 | }
117 | decreaseCounter() {
118 | this.currentPage--
119 | if (this.currentPage === -1) this.currentPage = this.collectionsInfo.length - 1
120 | }
121 |
122 | getBufferImg(media, i) {
123 | const src = this.collectionsInfo[this.currentPage][`image${+ i + 1}`]
124 | media.texture.image = TEXTURE.get(src).image
125 | }
126 |
127 | getInfo() {
128 | return this.collectionsInfo[this.currentPage]
129 | }
130 | }
131 | export const collectionsService = new CollectionsService();
132 |
133 |
--------------------------------------------------------------------------------
/src/animation/collectionsNav.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | export default class collectionsNav {
4 | constructor() {
5 | let link = N.get('a[href="home"]', this.element)
6 | if (N.Ga(link, 'href')) {
7 | link.click()
8 | }
9 | }
10 |
11 | play() {
12 | }
13 |
14 |
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/src/animation/collectionsNext.js:
--------------------------------------------------------------------------------
1 | import { canvas } from "../Canvas/canvas";
2 | import collectionsAnime from "./collectionsAnimation";
3 | import { collectionsService } from "./collectionsAnimationService";
4 | import { N } from "../utils/namhai";
5 |
6 | export default class collectionsNext extends collectionsAnime {
7 | constructor() {
8 | collectionsService.increaseCounter()
9 | super()
10 | }
11 |
12 | canvasAnimation() {
13 | const d = 400, delay = 1000, e = 'io5', zF = -0.5
14 | const force = -.7
15 | this.canvas = canvas
16 | Object.entries(this.canvas.collections.mediasBuffer).forEach(([index, mB]) => {
17 | collectionsService.getBufferImg(mB, index)
18 | })
19 |
20 | this.canvas.collections.mousemoveON = false
21 |
22 |
23 | let mediasBuffer = this.canvas.collections.mediasBuffer
24 | const mB = mediasBuffer[1], mB2 = mediasBuffer[0]
25 | let medias = this.canvas.collections.medias
26 |
27 | let m = medias[1]
28 | let m2 = medias[0]
29 |
30 | const hwRatio = m.bounds.height / m.bounds.width
31 | const collectionBounds = this.canvas.collections.collectionBounds
32 | this.tl.from({
33 | d: d,
34 | e: 'i2',
35 | update: (t) => {
36 | if (t.prog < .3) {
37 |
38 | m.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
39 | mB.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
40 | m2.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
41 | mB2.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
42 | }
43 | [...medias, ...mediasBuffer].forEach(m => {
44 | m.program.uniforms.angle.value = [0.3]
45 | m.program.uniforms.target.value = m.canvasSize.width * (.5 - t.progE / 2)
46 | })
47 | let nT = N.Clamp(N.iLerp(t.progE, 0.25, 1), 0, 1)
48 | m.mesh.scale.x = m.bounds.width * (1 - nT)
49 | m.mesh.position.x = (m.boundsPixel.x - m.canvasSizePixel.width / 2) * m.canvasSize.width / m.canvasSizePixel.width + m.bounds.width * (1 - nT) / 2
50 |
51 | m.program.uniforms.s.value = [hwRatio / (1 - nT), 1]
52 | m.program.uniforms.t.value = [- nT / 2 / hwRatio, 0]
53 |
54 | mB.mesh.scale.x = mB.bounds.width * nT
55 | mB.mesh.position.x = (mB.boundsPixel.x - mB.canvasSizePixel.width / 2) * mB.canvasSize.width / mB.canvasSizePixel.width + mB.bounds.width * (2 - nT) / 2
56 |
57 | mB.program.uniforms.s.value = [hwRatio / nT, 1]
58 | mB.program.uniforms.t.value = [(.5 - nT / 2) / hwRatio, 0]
59 |
60 | this.canvas.collections.velo.needsUpdate = true
61 | this.canvas.collections.velo.set(-1)
62 | this.canvas.collections.bufferRenderObject.mesh.program.uniforms.x.value = N.Lerp(collectionBounds.x + collectionBounds.width, collectionBounds.x + collectionBounds.width / 2, t.progE) / this.canvas.sizePixel.width
63 |
64 | },
65 | })
66 | this.tl.from({
67 | delay: d,
68 | d: d,
69 | e: 'o2',
70 | update: (t) => {
71 | [...medias, ...mediasBuffer].forEach(m => {
72 | m.program.uniforms.target.value = m.canvasSize.width * (.5 - (1 + t.progE) / 2)
73 | })
74 | let nT = N.Clamp(N.iLerp(t.progE, 0, 0.8), 0, 1)
75 | m2.mesh.scale.x = m2.bounds.width * (1 - nT)
76 | m2.mesh.position.x = (m2.boundsPixel.x - m2.canvasSizePixel.width / 2) * m2.canvasSize.width / m2.canvasSizePixel.width + m2.bounds.width * (1 - nT) / 2
77 |
78 | m2.program.uniforms.s.value = [hwRatio / (1 - nT), 1]
79 | m2.program.uniforms.t.value = [-nT / 2 / hwRatio, 0]
80 | mB2.mesh.scale.x = mB2.bounds.width * nT
81 | mB2.mesh.position.x = (mB2.boundsPixel.x - mB2.canvasSizePixel.width / 2) * mB2.canvasSize.width / mB2.canvasSizePixel.width + mB2.bounds.width * (2 - nT) / 2
82 |
83 | mB2.program.uniforms.s.value = [hwRatio / nT, 1]
84 | mB2.program.uniforms.t.value = [(0.5 - nT / 2) / hwRatio, 0]
85 |
86 | this.canvas.collections.velo.needsUpdate = true
87 | this.canvas.collections.velo.set(-0.8)
88 | this.canvas.collections.bufferRenderObject.mesh.program.uniforms.x.value = N.Lerp(collectionBounds.x + collectionBounds.width / 2, collectionBounds.x, t.progE) / this.canvas.sizePixel.width
89 |
90 | if (t.prog > .7) {
91 |
92 | m.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
93 | mB.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
94 | m2.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
95 | mB2.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
96 | }
97 | },
98 | cb: _ => {
99 | m.texture.image = mB.texture.image
100 | m2.texture.image = mB2.texture.image
101 | m.setScale()
102 | m2.setScale()
103 | mB.mesh.scale.x = 0
104 | mB2.mesh.scale.x = 0
105 |
106 |
107 | this.canvas.collections.mousemoveON = true
108 |
109 | N.PE.all(this.button)
110 | }
111 | })
112 |
113 | }
114 |
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/src/animation/collectionsPrevious.js:
--------------------------------------------------------------------------------
1 | import { canvas } from "../Canvas/canvas";
2 | import { N } from "../utils/namhai";
3 | import collectionsAnime from "./collectionsAnimation";
4 | import { collectionsService } from "./collectionsAnimationService";
5 |
6 | export default class collectionsPrevious extends collectionsAnime {
7 | constructor() {
8 | collectionsService.decreaseCounter()
9 | super()
10 | }
11 |
12 | canvasAnimation() {
13 | const d = 400, delay = 1000, e = 'io5', zF = -0.5
14 | const force = .9
15 | this.canvas = canvas
16 | Object.entries(this.canvas.collections.mediasBuffer).forEach(([index, mB]) => {
17 | collectionsService.getBufferImg(mB, index)
18 | })
19 | let mediasBuffer = this.canvas.collections.mediasBuffer
20 | const mB = mediasBuffer[0], mB2 = mediasBuffer[1]
21 | let medias = this.canvas.collections.medias
22 |
23 | let m2 = medias[1]
24 | let m = medias[0]
25 | const hwRatio = m.bounds.height / m.bounds.width
26 |
27 | mB.mesh.scale.x = 0
28 | mB2.mesh.scale.x = 0
29 |
30 | this.canvas.collections.mousemoveON = false
31 | const collectionBounds = this.canvas.collections.collectionBounds
32 |
33 | this.tl.from({
34 | d: d,
35 | e: 'i2',
36 | update: (t) => {
37 |
38 | if (t.prog < .3) {
39 |
40 | m.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
41 | mB.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
42 | m2.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
43 | mB2.program.uniforms.force.value = N.map(t.prog, 0, 0.3, 0, force)
44 | }
45 | [...medias, ...mediasBuffer].forEach(m => {
46 | m.program.uniforms.angle.value = N.Lerp(.4, 0, t.progE)
47 | m.program.uniforms.target.value = m.canvasSize.width * (-.5 + t.progE / 2)
48 | })
49 | let nT = N.Clamp(N.iLerp(t.progE, 0.1, 1), 0, 1)
50 | m.mesh.scale.x = m.bounds.width * (1 - nT)
51 | m.mesh.position.x = (m.boundsPixel.x - m.canvasSizePixel.width / 2) * m.canvasSize.width / m.canvasSizePixel.width + m.bounds.width * (nT + 1) / 2
52 |
53 | m.program.uniforms.s.value = [hwRatio / (1 - nT), 1]
54 | m.program.uniforms.t.value = [nT / 2 / hwRatio, 0]
55 |
56 | mB.mesh.scale.x = mB.bounds.width * nT
57 | mB.mesh.position.x = (mB.boundsPixel.x - mB.canvasSizePixel.width / 2) * mB.canvasSize.width / mB.canvasSizePixel.width + mB.bounds.width * (nT) / 2
58 |
59 | mB.program.uniforms.s.value = [(hwRatio) / nT, 1]
60 | mB.program.uniforms.t.value = [(-.5 + nT / 2) / hwRatio, 0]
61 |
62 | this.canvas.collections.velo.needsUpdate = true
63 | this.canvas.collections.velo.set(1)
64 | this.canvas.collections.bufferRenderObject.mesh.program.uniforms.x.value = N.Lerp(collectionBounds.x, collectionBounds.x + collectionBounds.width / 2, t.progE) / this.canvas.sizePixel.width
65 | },
66 | })
67 | this.tl.from({
68 | delay: d,
69 | d: d,
70 | e: 'o2',
71 | update: (t) => {
72 | [...medias, ...mediasBuffer].forEach(m => {
73 | m.program.uniforms.angle.value = N.Lerp(0, -.4, t.progE)
74 | m.program.uniforms.target.value = m.canvasSize.width * (-.5 + (1 + t.progE) / 2)
75 | })
76 | let nT = N.Clamp(N.iLerp(t.progE, 0, 0.8), 0, 1)
77 | m2.mesh.scale.x = m2.bounds.width * (1 - nT)
78 | m2.mesh.position.x = (m2.boundsPixel.x - m2.canvasSizePixel.width / 2) * m2.canvasSize.width / m2.canvasSizePixel.width + m2.bounds.width * (1 + nT) / 2
79 |
80 | m2.program.uniforms.s.value = [hwRatio / (1 - nT), 1]
81 | m2.program.uniforms.t.value = [nT / 2 / hwRatio, 0]
82 | mB2.mesh.scale.x = mB2.bounds.width * nT
83 | mB2.mesh.position.x = (mB2.boundsPixel.x - mB2.canvasSizePixel.width / 2) * mB2.canvasSize.width / mB2.canvasSizePixel.width + mB2.bounds.width * (nT) / 2
84 |
85 | mB2.program.uniforms.s.value = [hwRatio / nT, 1]
86 | mB2.program.uniforms.t.value = [(-0.5 + nT / 2) / hwRatio, 0]
87 |
88 |
89 | this.canvas.collections.velo.needsUpdate = true
90 | this.canvas.collections.velo.set(0.8)
91 | this.canvas.collections.bufferRenderObject.mesh.program.uniforms.x.value = N.Lerp(collectionBounds.x + collectionBounds.width / 2, collectionBounds.x + collectionBounds.width, t.progE) / this.canvas.sizePixel.width
92 | if (t.prog > .7) {
93 |
94 | m.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
95 | mB.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
96 | m2.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
97 | mB2.program.uniforms.force.value = N.map(t.prog, 0.7, 1, force, 0)
98 | }
99 | },
100 | cb: _ => {
101 | m.texture.image = mB.texture.image
102 | m2.texture.image = mB2.texture.image
103 | m.setScale()
104 | m2.setScale()
105 | mB.mesh.scale.x = 0
106 | mB2.mesh.scale.x = 0
107 |
108 | this.canvas.collections.mousemoveON = true
109 | N.PE.all(this.button)
110 | }
111 | })
112 |
113 | }
114 |
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/src/animation/collectionsTooltipBack.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai"
2 |
3 | export default class collectionsTooltipBack {
4 | constructor(b) {
5 | this.tl = new N.TL
6 | let toolTip = N.getAll('.back__tooltip span span')
7 | const i = b ? 105 : 0,
8 | f = b ? 0 : 105
9 |
10 | this.tl.from({
11 | d: 450,
12 | el: [...toolTip],
13 | p: {
14 | x: [i, f]
15 | },
16 | e: 'o5'
17 | })
18 | }
19 |
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/src/animation/contactAnimation.js:
--------------------------------------------------------------------------------
1 | import { canvas } from "../Canvas/canvas";
2 | import { TEXTURE } from "../Canvas/Preloader/preloaderCanvas";
3 | import { Texture } from 'ogl'
4 | import { N } from "../utils/namhai";
5 |
6 | class ContactService {
7 | constructor() {
8 | this.index = 0
9 | this.textures = ['contact/contact_1.png', 'contact/contact_2.png']
10 | this.info = [
11 | {
12 | texture: 'contact/contact_1.png',
13 | c: '#DF7863',
14 | bg: '#BD513A',
15 | },
16 | {
17 | texture: 'contact/contact_2.png',
18 | bg: "#2B2454",
19 | c: "#372F67"
20 | },
21 | {
22 | texture: 'contact/contact_3.png',
23 | bg: '#84133B',
24 | c: '#AE3560'
25 | }
26 |
27 | ]
28 | }
29 |
30 | next() {
31 | if (this.index + 1 == this.info.length) {
32 | this.index = -1
33 | }
34 | this.index++
35 | }
36 |
37 | getTexture() {
38 | return TEXTURE.get(this.textures[this.index])
39 | }
40 |
41 | getInfo() {
42 | return this.info[this.index]
43 | }
44 | getNextInfo() {
45 | if (this.index + 1 == this.info.length) {
46 | return this.info[0]
47 | }
48 | return this.info[this.index + 1]
49 | }
50 |
51 | }
52 | export const contactService = new ContactService()
53 | export default class ContactAnimation {
54 | constructor(cb, wrapper, bgBuffer, contactTitle, contactTitleSpans, linkSpans, linkWrapper, backButton, gridFixation) {
55 | contactService.next()
56 | const nextColor = contactService.getInfo().c,
57 | nextBg = contactService.getInfo().bg
58 |
59 | const r = N.Rand.range(0, 20, 0.01)
60 | canvas.contact.program.uniforms.u_rand.value = r
61 | canvas.contact.program.uniforms.u_time.value = 0;
62 | canvas.contact.program.uniforms.u_force.value = 0;
63 | this.tl = new N.TL
64 | this.tl.from({
65 | d: 700,
66 | e: 'o2',
67 | update: (t) => {
68 | canvas.contact.program.uniforms.u_ftime.value = t.progE
69 | }
70 | })
71 | this.tl.from({
72 | d: 400,
73 | e: 'i6',
74 | update: (t) => {
75 | canvas.contact.program.uniforms.u_ftime.value = 1 - t.progE
76 | },
77 | delay: 700
78 | })
79 | this.tl.from({
80 | el: [...contactTitleSpans, ...linkSpans],
81 | p: {
82 | x: [0, -105]
83 | },
84 | d: 450,
85 | e: 'o5',
86 | cb: _ => {
87 | linkWrapper.style.color = nextColor
88 | contactTitle.style.color = nextColor
89 | }
90 | })
91 | this.tl.from({
92 | el: [...contactTitleSpans, ...linkSpans],
93 | p: {
94 | x: [-105, 0]
95 | },
96 | d: 450,
97 | e: 'o5',
98 | delay: 1050
99 | })
100 |
101 | this.tl.from({
102 | d: 1,
103 | update: _ => { },
104 | delay: 700,
105 | cb: _ => {
106 | backButton.style.color = nextColor
107 | gridFixation.style.color = nextColor
108 | }
109 | })
110 |
111 | this.tl.from({
112 | el: bgBuffer,
113 | d: 400,
114 | p: {
115 | o: [0, 1]
116 | },
117 | e: 'i6',
118 | delay: 700,
119 | cb: _ => {
120 |
121 | wrapper.style.backgroundColor = nextBg
122 | bgBuffer.style.backgroundColor = contactService.getNextInfo().bg
123 |
124 | N.O(bgBuffer, 0)
125 | }
126 | })
127 |
128 | this.tl.from({
129 | d: 1500,
130 | update: t => {
131 | canvas.contact.program.uniforms.u_time.value = t.progE
132 | },
133 | cb: _ => {
134 | canvas.contact.program.uniforms.u_time.value = 0;
135 | canvas.contact.program.uniforms.u_force.value = 0;
136 | canvas.contact.texture.image = TEXTURE.get(contactService.getInfo().texture).image
137 | canvas.contact.textureBuffer.image = TEXTURE.get(contactService.getNextInfo().texture).image
138 |
139 | canvas.contact.program.uniforms.tMap.value = canvas.contact.texture;
140 | canvas.contact.program.uniforms.tMapBuffer.value = canvas.contact.textureBuffer;
141 |
142 | cb()
143 | }
144 | })
145 | this.tl.from({
146 | d: 500,
147 | delay: 1000,
148 | e: 'linear',
149 | update: t => {
150 | canvas.contact.program.uniforms.u_force.value = t.progE
151 | }
152 | })
153 | }
154 | play() {
155 | this.tl.play()
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/src/animation/demoComplete.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | export default class DemoComplete {
4 | constructor() {
5 | let link = N.get('a[href="home"]', this.element)
6 | if (N.Ga(link, 'href')) {
7 | link.click()
8 | }
9 | let button = N.get('.ressortButton')
10 | this.tl = new N.TL
11 | this.tl.from({
12 | d: 400,
13 | e: 'io5',
14 | update: t => {
15 | button.style.borderRadius = N.map(t.prog, 0, 1, 5, 0) + 'px'
16 | },
17 | cb: () => {
18 | }
19 | })
20 | }
21 |
22 | play() {
23 | this.tl.play()
24 | }
25 |
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/animation/demoTooltipAnimation.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | export default class PreloaderTooltipAnimation {
4 | constructor(b) {
5 |
6 | let tooltip = N.get('.ressort__demo__tooltip')
7 | let spanTooltip = N.getAll('span span', tooltip)
8 | let succes = N.getAll('.ressort__demo__success span span')
9 | this.tl = new N.TL
10 |
11 | let tooltipO = {
12 | x: b ? [0, 105] : [105, 0],
13 | delay: b ? 0 : 200
14 | }
15 |
16 | let successO = {
17 | o: b ? [105, 0] : [0, 105],
18 | delay: b ? 200 : 0
19 | }
20 | this.tl.from({
21 | el: spanTooltip,
22 | p: {
23 | x: tooltipO.x
24 | },
25 | d: 450,
26 | e: 'o5',
27 | delay: tooltipO.delay
28 | })
29 |
30 | this.tl.from({
31 | el: succes,
32 | p: {
33 | x: successO.o
34 | },
35 | d: 450,
36 | e: 'o5',
37 | delay: successO.delay
38 | })
39 |
40 |
41 | }
42 |
43 | play() {
44 | this.tl.play()
45 | }
46 |
47 |
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/src/animation/homeFixation.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | const indexToDelay = [0, 1, 2, 3, 3, 2, 1, 0]
4 | let fixationState = [true]
5 | let tl = new N.TL
6 | export default class homeFixation {
7 | constructor(b) {
8 | tl.pause()
9 | tl = new N.TL
10 | if (b === undefined) {
11 | b = false
12 | }
13 | let fixations = N.getAll('.main grid-fixation div')
14 |
15 | Object.entries(fixations).forEach(([index, fixation]) => {
16 | const initX = -(8 + 7 * 21) + 42 * index + 2 * (index - 1),
17 | endX = -(8 + 7 * 48) + 96 * index + 2 * (index - 1);
18 |
19 |
20 | tl.from({
21 | d: 500,
22 | p: {
23 | x: [fixationState[0] == b ? initX : b ? initX : endX, b ? endX : initX, 'px']
24 | },
25 | el: fixation,
26 | e: 'o5',
27 | })
28 | })
29 | fixationState[0] = b
30 | }
31 |
32 | play() {
33 | tl.play()
34 | }
35 |
36 |
37 | }
38 |
39 | export { fixationState };
40 |
--------------------------------------------------------------------------------
/src/animation/homeFixationInit.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | const indexToDelay = [0, 1, 2, 3, 3, 2, 1, 0]
4 | export var fixationInit = false
5 | export default class homeFixationInit {
6 | constructor(fixations) {
7 |
8 | this.tl = new N.TL
9 | if (!fixationInit) {
10 |
11 | Object.entries(fixations).forEach(([index, fixation]) => {
12 | this.tl.from({
13 | d: 800,
14 | p: {
15 | // x: [0, -(8 + 7 * 21) + 42 * index + 2 * (index - 1), 'px']
16 | x: [0, -(8 + 7 * 48) + 96 * index + 2 * (index - 1), 'px']
17 | },
18 | cb: _ => fixationInit = true,
19 | el: fixation,
20 | delay: indexToDelay[index] * 10 + 1600,
21 | e: 'io5',
22 | })
23 | })
24 | } else {
25 | Object.entries(fixations).forEach(([index, fixation]) => {
26 | N.T(fixation, -(8 + 7 * 48) + 96 * index + 2 * (index - 1), 0, 'px')
27 | })
28 | }
29 | }
30 |
31 | play() {
32 | this.tl.play()
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/src/animation/homeToCollections.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | export default class homeToCollections {
4 | constructor(b) {
5 | let link = N.get('a[href="collections"]', this.element)
6 | if (N.Ga(link, 'href')) {
7 | link.click()
8 | }
9 |
10 | }
11 | play() {
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/animation/homeToContact.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | export default class homeToContact {
4 | constructor(b) {
5 | let link = N.get('a[href="contact"]', this.element)
6 | if (N.Ga(link, 'href')) {
7 | link.click()
8 | }
9 |
10 | }
11 | play() {
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/animation/homeTooltipCollections.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai";
2 |
3 | export default class homeTooltipCollections {
4 | constructor(b) {
5 | this.tl = new N.TL
6 | let toolTip = N.getAll('.htI span span')
7 | const i = b ? 100 : 0,
8 | f = b ? 0 : 100
9 |
10 | this.tl.from({
11 | d: 450,
12 | el: [...toolTip],
13 | p: {
14 | x: [i, f]
15 | },
16 | e: 'o5'
17 | })
18 | }
19 |
20 | play() {
21 | this.tl.play()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/animation/homeTooltipContact.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai"
2 |
3 | export default class homeTooltipContact {
4 | constructor(b) {
5 | this.tl = new N.TL
6 | let toolTip = N.getAll('.htC span span')
7 | const i = b ? 100 : 0,
8 | f = b ? 0 : 100
9 |
10 | this.tl.from({
11 | d: 450,
12 | el: [...toolTip],
13 | p: {
14 | x: [i, f]
15 | },
16 | e: 'o5'
17 | })
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/animation/shaders/BasicFrag.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | uniform sampler2D tMap;
3 | varying vec2 vUv;
4 | uniform vec2 resolution;
5 |
6 | void main() {
7 | vec4 texture = texture2D(tMap,vUv );
8 |
9 | gl_FragColor = texture;
10 | }
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/animation/shaders/BasicVer.glsl:
--------------------------------------------------------------------------------
1 | #define PI 3.1415926535
2 | attribute vec2 uv;
3 | attribute vec3 position;
4 |
5 | uniform mat4 modelViewMatrix;
6 | uniform mat4 projectionMatrix;
7 | varying vec2 vUv;
8 | uniform float f;
9 |
10 | void main() {
11 | vUv = uv;
12 |
13 | vec4 newPos = modelViewMatrix* vec4(position, 1.0);
14 | vec4 projectedPos = projectionMatrix * newPos;
15 |
16 | newPos.z += (sin((newPos.x + newPos.y) * PI) * .5 + 1. ) * f * 3.;
17 | newPos.x -= (sin((newPos.x + newPos.y) * PI) * .5 + 1. ) * f * 0.3;
18 | gl_Position =projectionMatrix * newPos;
19 | }
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/animation/shaders/WhiteFrag.glsl:
--------------------------------------------------------------------------------
1 | precision mediump float;
2 | varying vec2 vUv;
3 | uniform vec2 resolution;
4 |
5 | void main() {
6 |
7 | gl_FragColor = vec4(1.);
8 | }
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/animation/transitionFallBack.js:
--------------------------------------------------------------------------------
1 | export default class TransitionFallBack {
2 | constructor({ r, cb, canvas, oldRoute, route }) {
3 | canvas.hide(oldRoute)
4 | canvas.onChange(route)
5 |
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | import './styles/index.scss'
2 | import Router from './classes/Router'
3 | import { N } from './utils/namhai'
4 | import { canvas } from './Canvas/canvas'
5 | import Preloader from './Pages/Preloader/preloader'
6 |
7 | class App {
8 | constructor() {
9 | this.main = N.get('.main')
10 | this.router = new Router()
11 |
12 | this.onResize()
13 | this.createPreloader()
14 | // this.initPage()
15 | }
16 |
17 | initPage() {
18 | const url = 'collections'
19 | window.history.pushState('', 'Nam Hai portfolio', url)
20 | this.page = this.createPage(url)
21 |
22 | this.page.content.remove()
23 |
24 | this.router.path = url
25 | this.page.render(this.main)
26 | this.page.renderComponents(this.main)
27 | this.addLinkLinstener(this.main)
28 | this.addEventListener()
29 | this.createCanvas()
30 | this.canvas.show()
31 | }
32 |
33 | async createPreloader() {
34 | this.router.resetPath()
35 | this.preloader = new Preloader()
36 | this.page = this.preloader
37 | this.preloader.render(this.main)
38 | this.page.renderComponents(this.main)
39 | this.addLinkLinstener(this.main)
40 | this.addEventListener()
41 | this.createCanvas()
42 | this.canvas.show()
43 | await new Promise(s => {
44 | this.canvas.preloader.loadTexture(s)
45 | })
46 | this.onChange({ url: 'demo' })
47 | }
48 |
49 | createCanvas() {
50 | this.canvas = canvas
51 | this.canvas.onChange(this.router.getRoute())
52 | }
53 |
54 | createPage(route) {
55 | return new (this.router.getPage(route))()
56 | }
57 |
58 | addLinkLinstener(context) {
59 | let links = N.getAll('.link__spa', context)
60 | if (!links) return
61 | if (!(links instanceof window.NodeList)) links = [links]
62 | for (const link of links) {
63 | link.addEventListener('click', (e) => {
64 | const href = N.Ga(link, 'href')
65 | N.PD(e)
66 | this.onChange({ url: href, button: link })
67 | })
68 | }
69 | }
70 |
71 | addEventListener() {
72 | window.addEventListener('mousedown', this.onMouseDown.bind(this))
73 | window.addEventListener('touchstart', this.onTouchDown.bind(this))
74 | window.addEventListener('touchmove', this.onTouchMove.bind(this))
75 | window.addEventListener('touchend', this.onMouseUp.bind(this))
76 | window.addEventListener('mousemove', this.onMouseMove.bind(this))
77 | window.addEventListener('mouseup', this.onMouseUp.bind(this))
78 | window.addEventListener('popstate', this.onPopState.bind(this))
79 | window.addEventListener('resize', this.onResize.bind(this))
80 | }
81 |
82 | onResize(e) {
83 | if (window.innerWidth < 800) {
84 | if (!this.mobileState) {
85 | this.mobile = N.Cr('div')
86 | this.mobile.classList.add('mobile')
87 | this.mobile.innerHTML = 'this portfolio is meant for desktop experience
'
88 |
89 |
90 | document.body.appendChild(this.mobile)
91 | }
92 | this.mobileState = true
93 | } else if (this.mobileState) {
94 | this.mobile.remove()
95 | this.mobileState = false
96 | }
97 | canvas.onResize(this.router.path)
98 | }
99 |
100 | onTouchDown(e) {
101 | // let event = { x: e.touches[0].clientX, y: e.touches[0].clientY }
102 | }
103 |
104 | onTouchMove(e) {
105 | let event = { x: e.touches[0].clientX, y: e.touches[0].clientY }
106 | this.onMouseMove(event)
107 | }
108 | onMouseDown(e) {
109 |
110 | }
111 |
112 | onMouseMove(e) {
113 | if (this.canvas) {
114 | this.canvas.onMouseMove(e)
115 | }
116 | if (this.page) {
117 | this.page.onMouseMove(e)
118 | }
119 | }
120 |
121 | onMouseUp(e) {
122 | if (this.page) {
123 | this.page.onMouseUp(e)
124 | }
125 | }
126 |
127 | async onChange({ url, button, push = true }) {
128 |
129 | this.pageBuffer = this.createPage(url)
130 | this.pageBuffer.render(N.get('.buffer-main'))
131 | this.pageBuffer.renderComponents(N.get('.buffer-main'))
132 | await this.page.hide()
133 | await this.router.transitionOnChange(url, this.canvas, this.pageBuffer.content)
134 | this.page = null
135 |
136 | this.page = this.pageBuffer
137 | this.main.setAttribute('style', '')
138 | this.main.setAttribute('data-template', url)
139 | this.main.innerHTML = this.page.nodeParent.innerHTML
140 |
141 | this.page.content.classList = 'main'
142 | this.page.content.style = ''
143 | this.main.remove()
144 |
145 | this.main = this.page.content
146 |
147 | this.addLinkLinstener(this.main)
148 | this.main.setAttribute('data-init', 'false')
149 |
150 | this.router.path = url
151 |
152 | if (push) window.history.pushState('', 'Nam Hai portfolio', url)
153 |
154 | }
155 |
156 |
157 |
158 | onPopState() {
159 | this.onChange({
160 | url: window.location.pathname.slice(1),
161 | push: false
162 | })
163 | }
164 | }
165 |
166 | new App()
167 |
--------------------------------------------------------------------------------
/src/classes/Component.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai"
2 |
3 | let id = 0
4 | export default class Component {
5 | constructor({ name, content, node, input }) {
6 |
7 |
8 | this.input = input
9 |
10 | this.selector = `[data-id="${id}"]`
11 |
12 |
13 | this.element = N.Cr(name)
14 | this.element.innerHTML = content
15 | this.content = content
16 |
17 | this.element.setAttribute('data-id', id)
18 | node.setAttribute('data-id', id)
19 | this.computeInput(node)
20 |
21 | id++
22 | }
23 |
24 |
25 | computeInput(node) {
26 | Object.entries(this.input).forEach(([attribute, value]) => {
27 | this[attribute] = node.getAttribute(attribute) || value
28 | })
29 | }
30 |
31 | create(selector) {
32 | this.element = selector instanceof window.HTMLElement ? selector : N.Select(selector);
33 | }
34 |
35 | render(node) {
36 | let proscedHTML = this.element.innerHTML
37 | this.element = N.get(this.selector, node)
38 | this.element.innerHTML = proscedHTML
39 |
40 |
41 | }
42 |
43 | addEventListener() {
44 |
45 | }
46 |
47 | removeEventListener() {
48 |
49 | }
50 |
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/classes/Page.js:
--------------------------------------------------------------------------------
1 | import { N } from "../utils/namhai"
2 |
3 | export default class Page {
4 | constructor({ elements, components, content, name }) {
5 | this.selectorChildren = elements
6 | this.components = components
7 |
8 | this.content = N.Cr('div')
9 | this.content.classList.add('buffer-main')
10 | this.name = name
11 | this.content.innerHTML = content
12 |
13 | document.body.appendChild(this.content)
14 | this.create()
15 | }
16 |
17 |
18 | create() {
19 | this.elements = {};
20 |
21 | if (!this.components) return
22 | Object.entries(this.components).forEach(([key, componentType]) => {
23 |
24 | this.components[key] = [...N.getAll(key, this.content)].map(component => {
25 | return new componentType({ name: key, node: component })
26 | })
27 | })
28 | }
29 |
30 | render(nodeParent) {
31 |
32 |
33 | nodeParent.innerHTML = this.content.innerHTML
34 | nodeParent.setAttribute('style', '')
35 | nodeParent.setAttribute('data-template', this.name)
36 |
37 | this.nodeParent = nodeParent
38 |
39 | }
40 | renderComponents(nodeParent) {
41 | if (this.components) {
42 | Object.values(this.components).forEach((components) => {
43 | components.forEach(component => {
44 | component.render(nodeParent)
45 | component.addEventListener()
46 | })
47 | })
48 | }
49 | this.nodeParent = nodeParent
50 | }
51 |
52 | onMouseDown(e) {
53 |
54 | }
55 |
56 | onMouseMove(e) {
57 |
58 | }
59 | onMouseUp(e) {
60 |
61 | }
62 |
63 | async hide() {
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/classes/Router.js:
--------------------------------------------------------------------------------
1 | import Preloader from '../Pages/Preloader/preloader';
2 | import Home from '../Pages/Home/home';
3 | import Collections from '../Pages/Collections/collections';
4 | import Demo from '../Pages/Demo/demo';
5 | import TransitionHomeCollections from '../animation/TransitionHomeCollections';
6 | import TransitionDemoHome from '../animation/TransitionDemoHome';
7 | import TransitionCollectionsHome from '../animation/TransitionCollectionsHome';
8 | import Contact from '../Pages/Contact/contact';
9 | import TransitionHomeContact from '../animation/TransitionHomeContact';
10 | import TransitionContactHome from '../animation/TransitionContactHome';
11 | import { N } from '../utils/namhai';
12 | import TransitionPreloaderDemo from '../animation/TransitionPreloaderDemo';
13 | import Detail from '../Pages/Details/detail';
14 | import TransitionDetailCollections from '../animation/TransitionDetailCollections'
15 |
16 | const transitionMap = new Map([
17 | ['home => collections', TransitionHomeCollections],
18 | ['collections => home', TransitionCollectionsHome],
19 | ['detail => collections', TransitionDetailCollections],
20 | ['demo => home', TransitionDemoHome],
21 | ['home => contact', TransitionHomeContact],
22 | ['contact => home', TransitionContactHome],
23 | ['preloader => demo', TransitionPreloaderDemo]
24 | ])
25 |
26 | export default class Router {
27 |
28 | constructor() {
29 | this.pageManager = {
30 | 'home': Home,
31 | 'collections': Collections,
32 | 'contact': Contact,
33 | 'demo': Demo,
34 | 'preloader': Preloader,
35 | 'detail': Detail
36 | }
37 |
38 | }
39 |
40 | getPage(route) {
41 | return this.pageManager[route] || Home
42 | }
43 |
44 | getRoute() {
45 | return this.path
46 | }
47 |
48 | resetPath() {
49 | window.history.pushState('', 'Nam Hai portfolio', '/')
50 | this.path = 'preloader'
51 | }
52 |
53 | async transitionOnChange(url, canvas, pageBufferContent) {
54 | const key = this.path + ' => ' + url
55 | let t = transitionMap.get(key)
56 | if (t) {
57 | N.PE.none(document.body)
58 | await new Promise(s => {
59 | t = new t({ cb: s, canvas, oldRoute: this.path, route: url, pageBufferContent })
60 | t.play()
61 | })
62 | N.PE.all(document.body)
63 | } else {
64 | pageBufferContent.classList.add('buffer-main__cover')
65 | canvas.hide(this.path)
66 | canvas.onChange(url)
67 | }
68 | }
69 | }
70 |
71 |
--------------------------------------------------------------------------------
/src/classes/Timer.js:
--------------------------------------------------------------------------------
1 | class Clock {
2 |
3 | constructor(autoStart = true) {
4 |
5 | this.autoStart = autoStart;
6 |
7 | this.startTime = 0;
8 | this.oldTime = 0;
9 | this.elapsedTime = 0;
10 |
11 | this.running = false;
12 |
13 | }
14 |
15 | start() {
16 |
17 | this.startTime = now();
18 |
19 | this.oldTime = this.startTime;
20 | this.elapsedTime = 0;
21 | this.running = true;
22 |
23 | }
24 |
25 | stop() {
26 |
27 | this.getElapsedTime();
28 | this.running = false;
29 | this.autoStart = false;
30 |
31 | }
32 |
33 | getElapsedTime() {
34 |
35 | this.getDelta();
36 | return this.elapsedTime;
37 |
38 | }
39 |
40 | getDelta() {
41 |
42 | let diff = 0;
43 |
44 | if (this.autoStart && !this.running) {
45 |
46 | this.start();
47 | return 0;
48 |
49 | }
50 |
51 | if (this.running) {
52 |
53 | const newTime = now();
54 |
55 | diff = (newTime - this.oldTime) / 1000;
56 | this.oldTime = newTime;
57 |
58 | this.elapsedTime += diff;
59 |
60 | }
61 |
62 | return diff;
63 |
64 | }
65 |
66 | }
67 |
68 | function now() {
69 |
70 | return (typeof performance === 'undefined' ? Date : performance).now(); // see #10732
71 |
72 | }
73 |
74 | export { Clock };
75 |
--------------------------------------------------------------------------------
/src/components/components.scss:
--------------------------------------------------------------------------------
1 | @forward './ressortButton/ressortButton.scss';
2 | @forward './gridFixation/gridFixation.scss';
3 |
--------------------------------------------------------------------------------
/src/components/gridFixation/gridFixation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/gridFixation/gridFixation.js:
--------------------------------------------------------------------------------
1 | import content from './gridFixation.html?raw';
2 | import Component from "../../classes/Component";
3 | import { N } from "../../utils/namhai";
4 | import homeFixationInit from "../../animation/homeFixationInit";
5 |
6 | export default class gridFixation extends Component {
7 | constructor({ name, node }) {
8 | super({
9 | name, content, node,
10 | input: {
11 |
12 | }
13 | })
14 |
15 | }
16 |
17 | render(node) {
18 | super.render()
19 | this.fixations = N.getAll('.fixation', node)
20 | let initAnimation = new homeFixationInit(this.fixations)
21 | initAnimation.play()
22 | }
23 | onMouseUp(e) {
24 | e.preventDefault()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/components/gridFixation/gridFixation.scss:
--------------------------------------------------------------------------------
1 | grid-fixation {
2 | position: absolute;
3 | top: 35px;
4 | left: calc(50% + 2px);
5 | transform: translateX(-50%);
6 |
7 | .fixation {
8 | position: absolute;
9 | width: 2px;
10 | height: 30rem;
11 | background-color: currentColor;
12 | }
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/src/components/ressortButton/ressortButton.html:
--------------------------------------------------------------------------------
1 |
button
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/ressortButton/ressortButton.js:
--------------------------------------------------------------------------------
1 | import Component from "../../classes/Component";
2 | import { N } from "../../utils/namhai";
3 | import content from './ressortButton.html?raw'
4 | import demoTooltipAnimation from "../../animation/demoTooltipAnimation";
5 | import demoComplete from "../../animation/demoComplete";
6 | import collectionsNext from "../../animation/collectionsNext";
7 | import collectionsNav from "../../animation/collectionsNav";
8 | import collectionsPrevious from "../../animation/collectionsPrevious";
9 | import homeTooltipContact from "../../animation/homeTooltipContact";
10 | import homeTooltipCollections from "../../animation/homeTooltipCollections";
11 | import collectionsTooltipBack from "../../animation/collectionsTooltipBack";
12 | import homeToCollections from "../../animation/homeToCollections";
13 | import homeToContact from "../../animation/homeToContact";
14 | import homeFixation from "../../animation/homeFixation";
15 |
16 | const k = 0.05
17 | const c = 0.2
18 | const l0 = 0;
19 |
20 | const rForce = 1.5
21 |
22 | const animeCompletionMap = new Map([
23 | ['demoComplete', demoComplete],
24 | ['homeToCollections', homeToCollections],
25 | ['collectionsNext', collectionsNext],
26 | ['collectionsNav', collectionsNav]
27 | ])
28 |
29 | const animeCompletionMap2 = new Map([
30 | ['collectionsPrevious', collectionsPrevious],
31 | ['homeToContact', homeToContact]
32 | ])
33 |
34 | const animeOnMarkerMap = new Map([
35 | ['demoTooltip', demoTooltipAnimation],
36 | ['homeTooltipCollections', homeTooltipCollections],
37 | ['collectionsTooltipBack', collectionsTooltipBack]
38 | ])
39 | const animeOnMarkerMap2 = new Map([
40 | ['homeTooltipContact', homeTooltipContact],
41 | ])
42 | export default class ressortButton extends Component {
43 | constructor({ name, node }) {
44 |
45 | super({
46 | name, content, node,
47 | input: {
48 | axis: null,
49 | distance: '96',
50 | both: null,
51 | animeOnCompletion: null,
52 | animeOnCompletion2: null,
53 | animeOnMarker: null,
54 | animeOnMarker2: null,
55 | link: null,
56 | link2: null
57 | }
58 | });
59 |
60 | this.distance = +this.distance
61 |
62 | this.end = false
63 | this.markerOn = false
64 | this.markerRot = 0
65 |
66 | this.clicked = false
67 | this.mouseEnter = false
68 | this.currentOffsetClick = 0
69 |
70 | this.coor = {
71 | pos: 0,
72 | velo: 0,
73 | acc: 0
74 | }
75 |
76 | N.BM(this, ['update', 'onMouseDown', 'onMouseMove', 'onMouseUp', 'turnMarker', 'onMouseEnter', 'onMouseLeave'])
77 | this.raf = new N.RafR(this.update)
78 |
79 | this.timeline = new N.TL
80 | this.timeline2 = new N.TL
81 | this.timeline3 = new N.TL
82 |
83 | this.crossTemplate = N.get('.cross__wrapper', this.element)
84 |
85 | this.createFixation(this.distance, 1)
86 | if (this.both == '1') {
87 | this.createFixation(-this.distance, 2)
88 | }
89 |
90 | }
91 |
92 | createFixation(d, n = 1) {
93 | let w = N.Cr('a')
94 | if (n) {
95 | let link = n === 1 ? this.link : this.link2
96 | if (this.link) {
97 | w.setAttribute('href', link)
98 | w.setAttribute('aria-label', link)
99 | w.classList.add('link__spa')
100 | }
101 | }
102 | w.innerHTML = this.crossTemplate.innerHTML
103 | w.classList.add('cross__wrapper')
104 | const x = this.axis === 'x' ? d : 0,
105 | y = this.axis === 'y' ? d : 0;
106 | let c = N.get('.cross__container', w)
107 | c.style.transform = `translate(${x}px,${y}px)`
108 | this.element.appendChild(w)
109 | }
110 |
111 | render(node) {
112 | super.render()
113 |
114 | this.button = N.get('button', this.element)
115 | this.marker = N.get('.cross__container__neutral', this.element)
116 | this.passivBounds = this.button.getBoundingClientRect()
117 | }
118 |
119 | addEventListener() {
120 | this.button.addEventListener('mousedown', this.onMouseDown)
121 |
122 | this.button.addEventListener('mouseenter', this.onMouseEnter)
123 | this.button.addEventListener('mouseleave', this.onMouseLeave)
124 |
125 | this.button.addEventListener('touchstart', this.onTouchDown.bind(this))
126 | }
127 | onTouchDown(e) {
128 | let event = { x: e.touches[0].clientX, y: e.touches[0].clientY }
129 | this.onMouseEnter()
130 | this.onMouseDown(event)
131 | }
132 | onMouseEnter() {
133 | this.mouseEnter = true
134 | new homeFixation().play()
135 | }
136 | onMouseLeave() {
137 | if (this.clicked || !this.mouseEnter) return
138 | this.mouseEnter = false
139 | new homeFixation(true).play()
140 | }
141 |
142 | onMouseDown(e) {
143 | this.coor.velo = 0
144 | this.coor.acc = 0
145 | this.raf.stop()
146 |
147 |
148 | this.clicked = true
149 | this.currentOffsetClick = this.passivBounds[this.axis] + this.coor.pos - e[this.axis]
150 | }
151 | onMouseMove(e) {
152 | if (!this.clicked) return
153 | const a = this.axis
154 | const delta = -this.passivBounds[a] + e[a] + this.currentOffsetClick
155 | this.coor.pos = delta / 1.5
156 |
157 | const x = this.axis === 'x' ? this.coor.pos : 0,
158 | y = this.axis === 'y' ? this.coor.pos : 0
159 | N.T(this.button, x, y, 'px')
160 |
161 | let pos = this.both ? Math.abs(this.coor.pos) : this.coor.pos
162 | const bD = this.distance > 0 ? pos > this.distance / rForce : pos < this.distance / rForce,
163 | bP = this.distance > 0 ? this.coor.pos < 0 : this.coor.pos > 0;
164 | if (!this.markerOn && bD) {
165 | this.turnMarker(true, bP)
166 | }
167 | if (this.markerOn && !bD) {
168 | this.turnMarker(false)
169 | }
170 |
171 | }
172 |
173 | onMouseUp(e) {
174 | this.clicked = false
175 | this.onMouseLeave()
176 | this.raf.run()
177 |
178 | if (this.markerOn) {
179 | this.isToggled = true
180 | if (this.link) {
181 | N.pe(this.button, 'none')
182 | }
183 |
184 | if (!this.secondMarker) {
185 | if (this.animeOnCompletion) {
186 | let anime = new (animeCompletionMap.get(this.animeOnCompletion))(this.link)
187 | anime.play()
188 | }
189 | } else {
190 | if (this.animeOnCompletion2) {
191 | let anime2 = new (animeCompletionMap2.get(this.animeOnCompletion2))()
192 | anime2.play()
193 | }
194 | }
195 | }
196 | this.turnMarker(false, false, true)
197 | }
198 |
199 | turnMarker(b, secondMarker = false, onMouseUp = false) {
200 | const a = (2 * secondMarker - 1)
201 | if (this.markerOn == b) return
202 | this.markerOn = b
203 | this.timeline.pause()
204 | this.timeline = new N.TL
205 | this.timeline.from({
206 | d: 300,
207 | e: 'o5',
208 | update: t => {
209 | this.markerRot = t.prog
210 | t = N.Ease.io4(t.prog) * 135
211 | t = b ? a * t : 135 - t
212 | this.marker.style.transform = `rotate(${t}deg)`
213 | }
214 | })
215 | if (this.animeOnMarker && !onMouseUp) {
216 | if (this.secondMarker || secondMarker) {
217 | this.timeline2.pause()
218 | this.timeline2 = new N.TL
219 | let tl = new (animeOnMarkerMap2.get(this.animeOnMarker2))(b).tl
220 | this.timeline2.arr.push(...tl.arr)
221 | this.timeline2.play()
222 | } else if (!secondMarker) {
223 | this.timeline3.pause()
224 | this.timeline3 = new N.TL
225 | let tl = new (animeOnMarkerMap.get(this.animeOnMarker))(b).tl
226 | this.timeline3.arr.push(...tl.arr)
227 | this.timeline3.play()
228 | }
229 | }
230 |
231 | this.secondMarker = secondMarker
232 | this.timeline.play()
233 | }
234 |
235 | update() {
236 | this.applyForce()
237 | this.coor.velo += this.coor.acc
238 | this.coor.pos += this.coor.velo
239 |
240 | const x = this.axis === 'x' ? this.coor.pos : 0,
241 | y = this.axis === 'y' ? this.coor.pos : 0
242 | N.T(this.button, x, y, 'px')
243 |
244 | if (Math.abs(this.coor.acc) <= 0.005) {
245 | this.raf.stop()
246 | if (this.isToggled) {
247 | this.end = true
248 | }
249 | }
250 |
251 | this.coor.acc = 0
252 | }
253 |
254 | applyForce() {
255 | this.coor.acc = k * (l0 - this.coor.pos) - c * this.coor.velo
256 | }
257 | }
258 |
--------------------------------------------------------------------------------
/src/components/ressortButton/ressortButton.scss:
--------------------------------------------------------------------------------
1 | ressort-button {
2 | display: block;
3 | width: 2.5rem;
4 | height: 2.5rem;
5 | position: relative;
6 | a {
7 | background-color: transparent;
8 | }
9 | button {
10 | font-size: 6px;
11 |
12 | width: 100%;
13 | height: 100%;
14 | border-radius: 5px;
15 | background-color: currentColor;
16 | -webkit-touch-callout: none;
17 | -webkit-user-select: none;
18 | -khtml-user-select: none;
19 | -moz-user-select: none;
20 | -ms-user-select: none;
21 | user-select: none;
22 | }
23 | }
24 |
25 | .doublespan__container {
26 | margin-bottom: -12px;
27 | }
28 | .cross__wrapper {
29 | height: 2rem;
30 | width: 2rem;
31 | position: absolute;
32 | pointer-events: none;
33 | top: 50%;
34 | left: 50%;
35 | transform: translate(-50%, -50%);
36 | transform-origin: center;
37 |
38 | .cross__container {
39 | width: 100%;
40 | height: 100%;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/styles/base/base.scss:
--------------------------------------------------------------------------------
1 | @use '../utils/mixins.scss' as *;
2 |
3 | *, *::after, *::before {
4 | box-sizing: border-box;
5 | }
6 |
7 | html {
8 | @include cover;
9 | }
10 |
11 | body {
12 | @include cover;
13 | font-family: 'League spartan', sans-serif;
14 | font-size: 16px;
15 | font-weight: 700;
16 | line-height: 1;
17 | background-color: black;
18 |
19 | padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
20 | }
21 |
22 | .main {
23 | @include cover;
24 |
25 | z-index: 1;
26 | background-color: black;
27 | }
28 |
29 | a {
30 | color: inherit;
31 | outline: none;
32 | text-decoration: none;
33 | cursor: pointer;
34 | }
35 |
36 | button {
37 | background: none;
38 | color: inherit;
39 | border: none;
40 | cursor: pointer;
41 | outline: none;
42 | padding: 0;
43 | }
44 |
45 | .doublespan__container {
46 | display: inline-block;
47 | overflow: hidden;
48 | span {
49 | display: block;
50 | pointer-events: none;
51 | user-select: none;
52 | -webkit-user-select: none;
53 | -khtml-user-select: none;
54 | -moz-user-select: none;
55 | -ms-user-select: none;
56 | }
57 | }
58 |
59 | img[data-src] {
60 | opacity: 0;
61 | }
62 |
63 | canvas {
64 | z-index: 3;
65 | position: relative;
66 | pointer-events: none;
67 | }
68 |
69 | .buffer-main {
70 | position: absolute;
71 | height: 100%;
72 | width: 100%;
73 | }
74 | .buffer-main__cover{
75 | top: 0;
76 | left: 0;
77 | z-index: 2;
78 | }
79 | .buffer-main__UP{
80 | bottom: 100%;
81 | left: 0;
82 | }
83 | .buffer-main__DOWN {
84 | top: 100%;
85 | left: 0;
86 | }
87 |
88 | .mobile {
89 | position: absolute;
90 | top: 0;
91 | left: 0;
92 | width: 100%;
93 | height: 100%;
94 | z-index: 10;
95 | background-color: black;
96 | color: white;
97 | text-transform: uppercase;
98 | font-size: 3rem;
99 | padding: 2rem;
100 | display: flex;
101 | flex-direction: column;
102 |
103 | img {
104 | margin-bottom: auto;
105 | width: 100%;
106 | }
107 |
108 | justify-content: space-between;
109 | .mobile-footer {
110 | font-size: 4.5rem;
111 | display: flex;
112 | flex-direction: column;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/styles/base/fonts.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'League spartan';
3 | src: url('/fonts/LeagueSpartan-Bold.ttf');
4 | font-weight: 700;
5 | font-style: normal ;
6 | font-display: swap;
7 | }
8 | @font-face {
9 | font-family: 'League spartan';
10 | src: url('/fonts/LeagueSpartan-ExtraLight.ttf');
11 | font-weight: 200;
12 | font-style: normal ;
13 | font-display: swap;
14 | }
15 | @font-face {
16 | font-family: 'League spartan';
17 | src: url('/fonts/LeagueSpartan-Regular.ttf');
18 | font-weight: 400;
19 | font-style: normal ;
20 | font-display: swap;
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/src/styles/base/reset.scss:
--------------------------------------------------------------------------------
1 |
2 | html, body, div, span, applet, object, iframe,
3 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
4 | a, abbr, acronym, address, big, cite, code,
5 | del, dfn, em, img, ins, kbd, q, s, samp,
6 | small, strike, strong, sub, sup, tt, var,
7 | b, u, i, center,
8 | dl, dt, dd, ol, ul, li,
9 | fieldset, form, label, legend,
10 | table, caption, tbody, tfoot, thead, tr, th, td,
11 | article, aside, canvas, details, embed,
12 | figure, figcaption, footer, header, hgroup,
13 | menu, nav, output, ruby, section, summary,
14 | time, mark, audio, video {
15 | margin: 0;
16 | padding: 0;
17 | border: 0;
18 | font-size: 100%;
19 | font: inherit;
20 | vertical-align: baseline;
21 | }
22 | article, aside, details, figcaption, figure,
23 | footer, header, hgroup, menu, nav, section {
24 | display: block;
25 | }
26 | body {
27 | line-height: 1;
28 | }
29 | ol, ul {
30 | list-style: none;
31 | }
32 | blockquote, q {
33 | quotes: none;
34 | }
35 | blockquote:before, blockquote:after,
36 | q:before, q:after {
37 | content: '';
38 | content: none;
39 | }
40 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @forward './base/reset.scss';
2 | @forward './base/fonts.scss';
3 | @forward './base/base.scss';
4 |
5 | @forward '../Pages/page.scss';
6 | @forward '../components/components.scss';
7 |
--------------------------------------------------------------------------------
/src/styles/utils/mixins.scss:
--------------------------------------------------------------------------------
1 |
2 | @mixin cover {
3 | height: 100%;
4 | left: 0;
5 | object-fit: cover;
6 | position: absolute;
7 | top:0;
8 | width: 100%;
9 | overflow: hidden;
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/src/styles/utils/responsive.scss:
--------------------------------------------------------------------------------
1 | $small: 768px;
2 | $medium: 1250px;
3 | $large: 1700px;
4 | $x-large: 1920px;
5 |
6 |
7 | $breakpoints: (
8 | '>small' : (min-width:$small),
9 | '>medium': (min-width:$medium),
10 | '>large' : (min-width:$large),
11 | '>x-large' : (min-width:$x-large),
12 | '
${node.innerHTML}`
6 | return N.get('span', node)
7 | }
8 |
9 | export function lineSpaning(spans) {
10 | const node1 = N.Cr('span')
11 | const node2 = N.Cr('span')
12 | let spanText = ''
13 | for (const span of spans) {
14 | spanText += span.innerText + ' '
15 | }
16 | node2.innerText = spanText
17 | node1.appendChild(node2)
18 | return node1
19 | }
20 |
21 | export function split({ element, expression = ' ', append = true }) {
22 | const words = splitText(element.innerHTML.toString().trim(), expression)
23 |
24 | let innerHTML = ''
25 |
26 | for (const line of words) {
27 | if (line.indexOf(' ') > -1) {
28 | const lines = line.split(' ')
29 |
30 | for (const [index, line] of lines.entries()) {
31 | innerHTML += (index > 0) ? ' ' + parseLine(line) : parseLine(line)
32 | }
33 | } else {
34 | innerHTML += parseLine(line)
35 | }
36 | }
37 |
38 | element.innerHTML = innerHTML
39 |
40 | const spans = element.querySelectorAll('span')
41 |
42 |
43 | if (append) {
44 | for (const span of spans) {
45 | const isSingleLetter = span.textContent.length === 1,
46 | isNotEmpty = span.innerHTML.trim() !== '',
47 | isNotAndCharacter = span.textContent !== '&',
48 | isNotDashCharacter = span.textContent !== '-'
49 |
50 | if (isSingleLetter && isNotEmpty && isNotAndCharacter && isNotDashCharacter) {
51 | span.innerHTML = `${span.textContent} `
52 | }
53 | }
54 | }
55 | return spans
56 | }
57 |
58 | export function calculate(spans) {
59 | const lines = []
60 | let words = []
61 |
62 | let position = spans[0].offsetTop
63 |
64 | for (const [index, span] of spans.entries()) {
65 |
66 | if (span.offsetTop === position) {
67 | words.push(span)
68 | }
69 |
70 | if (span.offsetTop !== position) {
71 | lines.push(words)
72 |
73 | words = []
74 | words.push(span)
75 |
76 | position = span.offsetTop
77 | }
78 |
79 | if (index + 1 === spans.length) {
80 | lines.push(words)
81 | }
82 | }
83 |
84 | return lines
85 | }
86 |
87 |
88 | function splitText(text, expression) {
89 | const splits = text.split(' ')
90 |
91 | let words = []
92 |
93 | for (const [index, item] of splits.entries()) {
94 | if (index > 0) {
95 | words.push(' ')
96 | }
97 |
98 | words = words.concat(item.split(expression))
99 |
100 | let isLink = false
101 | let link = ''
102 |
103 | const innerHTML = []
104 |
105 | for (const word of words) {
106 | if (!isLink && (word.includes('') || word.includes('/strong>'))) {
117 | innerHTML.push(link)
118 |
119 | link = ''
120 | }
121 |
122 | if (!isLink && link === '') {
123 | innerHTML.push(word)
124 | }
125 |
126 | if (isLink && (word.includes('/a>') || word.includes('/strong>'))) {
127 | isLink = false
128 | }
129 | }
130 |
131 | words = innerHTML
132 | }
133 |
134 | return words
135 | }
136 | function parseLine(line) {
137 | line = line.trim()
138 |
139 | return (line === '' || line === ' ') ? line : line == ' ' ? ' ' : `${line} ` + ((line.length > 1) ? ' ' : '')
140 | }
141 |
--------------------------------------------------------------------------------
/src/utils/utilsText.js:
--------------------------------------------------------------------------------
1 | import { N } from "./namhai";
2 | import { calculate } from "./text";
3 |
4 | export function stringLetterToDoubleSpan(element, className) {
5 |
6 | let innerHTML = element.innerHTML.toString().trim()
7 | element.innerHTML = ''
8 |
9 | let letters = innerHTML.split('')
10 | for (const letter of letters) {
11 | let parent = N.Cr('span')
12 | parent.classList.add('doublespan__container')
13 | let span = N.Cr('span')
14 | span.classList.add(className)
15 |
16 | span.innerHTML = letter === ' ' ? ' ' : letter
17 | parent.appendChild(span)
18 | element.appendChild(parent)
19 |
20 | }
21 | }
22 | export function stringLetterToSpan(element) {
23 |
24 | let innerHTML = element.innerHTML.toString().trim()
25 | element.innerHTML = ''
26 |
27 | let letters = innerHTML.split('')
28 | for (const letter of letters) {
29 | let span = N.Cr('span')
30 | span.classList.add('singlespan__container')
31 |
32 | span.innerHTML = letter === ' ' ? ' ' : letter
33 | element.appendChild(span)
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }]
3 | }
4 |
--------------------------------------------------------------------------------