├── .datocms.json
├── .editorconfig
├── .eslintrc.js
├── .gitignore
├── .gqlconfig
├── .nvmrc
├── .prettierrc
├── README.md
├── assets
├── README.md
├── fonts
│ ├── helvetica-neue-light.woff
│ ├── helvetica-neue-light.woff2
│ ├── helvetica-neue-medium.woff
│ ├── helvetica-neue-medium.woff2
│ ├── helvetica-neue.woff
│ ├── helvetica-neue.woff2
│ ├── orpheus-pro.woff
│ └── orpheus-pro.woff2
├── graphql
│ ├── about.js
│ ├── common.js
│ ├── error.js
│ ├── fragments
│ │ ├── image.js
│ │ └── seo.js
│ ├── index.js
│ ├── slugs.js
│ ├── work.js
│ └── works.js
├── images
│ └── wonderland.jpg
├── js
│ ├── events
│ │ └── EventsEmitter.js
│ ├── head.js
│ ├── math
│ │ ├── clamp.js
│ │ ├── distance.js
│ │ ├── index.js
│ │ ├── lerp.js
│ │ ├── lightenDarkenColor.js
│ │ ├── map.js
│ │ ├── modAbs.js
│ │ ├── normalize.js
│ │ ├── parabola.js
│ │ ├── randomFloat.js
│ │ ├── randomHexColor.js
│ │ ├── randomInt.js
│ │ └── smoothStep.js
│ └── utils
│ │ ├── MouseHelper.js
│ │ ├── ResizeHelper.js
│ │ ├── ScrollHelper.js
│ │ ├── SplitLines.js
│ │ ├── WheelHelper.js
│ │ ├── datas
│ │ ├── getAsyncData.js
│ │ ├── getData.js
│ │ ├── getQuery.js
│ │ └── storeJson.js
│ │ └── easings.js
├── scss
│ ├── base
│ │ ├── _fonts.scss
│ │ ├── _reset.scss
│ │ └── _typos.scss
│ ├── fragments
│ │ ├── _colors.scss
│ │ ├── _container.scss
│ │ └── _top.scss
│ ├── includes.scss
│ ├── includes
│ │ ├── _colors.scss
│ │ ├── _easings.scss
│ │ ├── _mixinViewportWidth.scss
│ │ ├── _mq.scss
│ │ ├── _spacings.scss
│ │ └── _variables.scss
│ └── main.scss
└── svgs
│ ├── .gitkeep
│ └── logo.svg
├── components
├── README.md
├── about
│ ├── awards
│ │ ├── award.vue
│ │ └── awards.vue
│ ├── clients
│ │ ├── client.vue
│ │ └── clients.vue
│ ├── footer.vue
│ └── links
│ │ ├── link.vue
│ │ └── links.vue
├── common
│ ├── loader.vue
│ ├── navigation.vue
│ ├── scroller.vue
│ ├── title.vue
│ └── transition.vue
├── componentLoader.vue
├── fragments
│ ├── image.vue
│ ├── media.vue
│ └── video.vue
├── next
│ ├── counter.vue
│ ├── project-next.vue
│ ├── project.vue
│ └── projects.vue
├── scene
│ ├── comps
│ │ ├── Main.js
│ │ ├── MainAbout.js
│ │ ├── Plane.js
│ │ └── screenunit.js
│ ├── scene-about.vue
│ ├── scene.vue
│ └── shaders
│ │ ├── plane.fs.js
│ │ ├── plane.vs.js
│ │ ├── planeCover.fs.js
│ │ ├── planeCover.vs copy.js
│ │ └── planeCover.vs.js
├── work
│ ├── duo.vue
│ ├── header.vue
│ ├── img-text.vue
│ ├── single-large.vue
│ ├── single-medium.vue
│ ├── tags
│ │ └── tag.vue
│ ├── top.vue
│ └── trio.vue
└── works
│ └── work.vue
├── jsconfig.json
├── layouts
├── README.md
└── default.vue
├── middleware
└── README.md
├── nuxt.config.js
├── package-lock.json
├── package.json
├── pages
├── 404.vue
├── README.md
├── about.vue
├── index.vue
└── work
│ ├── _slug.vue
│ └── index.vue
├── plugins
├── README.md
├── plugins.client.js
├── pwa.client.js
├── vuex-router-sync.js
└── workbox-range-request.js
├── static
├── .htaccess
├── README.md
├── Untitled-1.html
├── api
│ ├── 404
│ │ └── index.json
│ ├── about
│ │ └── index.json
│ ├── acqua-di-gio
│ │ └── index.json
│ ├── babylone
│ │ └── index.json
│ ├── common
│ │ └── index.json
│ ├── custom404
│ │ └── index.json
│ ├── herezie-group
│ │ └── index.json
│ ├── index
│ │ └── index.json
│ ├── la-grande-epicerie
│ │ └── index.json
│ ├── romain-avalle
│ │ └── index.json
│ ├── ruinart
│ │ └── index.json
│ └── work
│ │ └── index.json
├── fonts
│ ├── Orpheus_Pro.json
│ ├── Orpheus_Pro.otf
│ └── Orpheus_Pro.png
├── icon.png
├── images
│ ├── mask.jpg
│ ├── mask.png
│ ├── mask.webp
│ ├── mask_sml.jpg
│ └── mask_sml.webp
└── robots.txt
└── store
├── README.md
└── index.js
/.datocms.json:
--------------------------------------------------------------------------------
1 | {
2 | "token": "*******",
3 | "httpEndpoint": "https://graphql.datocms.com"
4 | }
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | browser: true,
5 | node: true
6 | },
7 | parserOptions: {
8 | parser: 'babel-eslint'
9 | },
10 | extends: [
11 | 'prettier',
12 | 'plugin:prettier/recommended',
13 | 'plugin:nuxt/recommended'
14 | ],
15 | plugins: [
16 | 'prettier'
17 | ],
18 | // add your custom rules here
19 | rules: {
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Node template
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 | yarn-debug.log*
8 | yarn-error.log*
9 |
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 |
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 |
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 |
22 | # nyc test coverage
23 | .nyc_output
24 |
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 |
28 | # Bower dependency directory (https://bower.io/)
29 | bower_components
30 |
31 | # node-waf configuration
32 | .lock-wscript
33 |
34 | # Compiled binary addons (https://nodejs.org/api/addons.html)
35 | build/Release
36 |
37 | # Dependency directories
38 | node_modules/
39 | jspm_packages/
40 |
41 | # TypeScript v1 declaration files
42 | typings/
43 |
44 | # Optional npm cache directory
45 | .npm
46 |
47 | # Optional eslint cache
48 | .eslintcache
49 |
50 | # Optional REPL history
51 | .node_repl_history
52 |
53 | # Output of 'npm pack'
54 | #*.tgz
55 |
56 | # Yarn Integrity file
57 | .yarn-integrity
58 |
59 | # dotenv environment variables file
60 | .env
61 |
62 | # parcel-bundler cache (https://parceljs.org/)
63 | .cache
64 |
65 | # next.js build output
66 | .next
67 |
68 | # nuxt.js build output
69 | .nuxt
70 |
71 | # Nuxt generate
72 | dist
73 |
74 | # vuepress build output
75 | .vuepress/dist
76 |
77 | # Serverless directories
78 | .serverless
79 |
80 | # IDE / Editor
81 | .idea
82 |
83 | # Service worker
84 | sw.*
85 |
86 | # Mac OSX
87 | .DS_Store
88 |
89 | # Vim swap files
90 | *.swp
91 |
92 |
93 | _/*
94 |
--------------------------------------------------------------------------------
/.gqlconfig:
--------------------------------------------------------------------------------
1 | /* .gqlconfig */
2 | {
3 | schema: {
4 | files: 'graphql/**/*.gql'
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v11.15.0
2 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "arrowParens": "always",
4 | "singleQuote": true
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # simon-daufresne
2 |
3 | > WARNING : I have done this site for a friend on my spare time late at night after a full day of work and taking care of my 2 kids...
4 | I have code several versions of it!
5 | So, this is not supposed to be the best code ever, a lot of mistakes are in it and you'll find a lot of dirty coded components. Am sure that there is thousand of better ways to do it.
6 | The only reason am sharing this code is to help people to find their way into front-end development.
7 |
8 | [https://simondaufresne.com](https://simondaufresne.com).
9 |
10 | You ll need to install [https://greensock.com/splittext](https://greensock.com/splittext) to make it works
11 |
12 | ## Build Setup
13 |
14 | ```bash
15 | # install dependencies
16 | $ npm run install
17 |
18 | # serve with hot reload at localhost:3000
19 | $ npm run dev
20 | ```
21 |
22 | For detailed explanation on how things work, check out [Nuxt.js docs](https://nuxtjs.org).
23 |
--------------------------------------------------------------------------------
/assets/README.md:
--------------------------------------------------------------------------------
1 | # ASSETS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
6 |
7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
8 |
--------------------------------------------------------------------------------
/assets/fonts/helvetica-neue-light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/helvetica-neue-light.woff
--------------------------------------------------------------------------------
/assets/fonts/helvetica-neue-light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/helvetica-neue-light.woff2
--------------------------------------------------------------------------------
/assets/fonts/helvetica-neue-medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/helvetica-neue-medium.woff
--------------------------------------------------------------------------------
/assets/fonts/helvetica-neue-medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/helvetica-neue-medium.woff2
--------------------------------------------------------------------------------
/assets/fonts/helvetica-neue.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/helvetica-neue.woff
--------------------------------------------------------------------------------
/assets/fonts/helvetica-neue.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/helvetica-neue.woff2
--------------------------------------------------------------------------------
/assets/fonts/orpheus-pro.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/orpheus-pro.woff
--------------------------------------------------------------------------------
/assets/fonts/orpheus-pro.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/fonts/orpheus-pro.woff2
--------------------------------------------------------------------------------
/assets/graphql/about.js:
--------------------------------------------------------------------------------
1 | import seo from './fragments/seo'
2 | import image from './fragments/image'
3 | export default `query aboutPageQuery {
4 | aboutPage {
5 | ${seo}
6 | title
7 | introduction
8 | hero{
9 | ${image}
10 | }
11 | clients {
12 | name
13 | }
14 | awards {
15 | title
16 | year
17 | award
18 | }
19 | links {
20 | label
21 | url
22 | }
23 | }
24 | }`
25 |
--------------------------------------------------------------------------------
/assets/graphql/common.js:
--------------------------------------------------------------------------------
1 | import image from './fragments/image'
2 | export default `query commonQuery {
3 |
4 | worksPage {
5 | works {
6 | slug
7 | title
8 | hero {
9 | ${image}
10 | }
11 | color {
12 | hex
13 | }
14 | }
15 | }
16 | }`
17 |
--------------------------------------------------------------------------------
/assets/graphql/error.js:
--------------------------------------------------------------------------------
1 | import seo from './fragments/seo'
2 | import image from './fragments/image'
3 | export default `query errorPageQuery {
4 | errorPage {
5 | ${seo}
6 | image{
7 | ${image}
8 | }
9 | }
10 | }`
11 |
--------------------------------------------------------------------------------
/assets/graphql/fragments/image.js:
--------------------------------------------------------------------------------
1 | export default `
2 | url
3 | height
4 | width
5 | `
6 |
--------------------------------------------------------------------------------
/assets/graphql/fragments/seo.js:
--------------------------------------------------------------------------------
1 | export default `seo {
2 | title
3 | description
4 | image {
5 | url
6 | }
7 | }`
8 |
--------------------------------------------------------------------------------
/assets/graphql/index.js:
--------------------------------------------------------------------------------
1 | import seo from './fragments/seo'
2 | import image from './fragments/image'
3 | export default `query homePageQuery {
4 | homePage {
5 | ${seo}
6 | title
7 | introduction
8 | links {
9 | label
10 | url
11 | }
12 | }
13 | }`
14 |
--------------------------------------------------------------------------------
/assets/graphql/slugs.js:
--------------------------------------------------------------------------------
1 | export default `query slugsQuery {
2 | allWorks {
3 | slug
4 | }
5 | }`
6 |
--------------------------------------------------------------------------------
/assets/graphql/work.js:
--------------------------------------------------------------------------------
1 | import seo from './fragments/seo'
2 | import image from './fragments/image'
3 | export default `query workPageQuery($slug: String!){
4 | work(filter: {slug: {eq: $slug}}) {
5 | ${seo}
6 | title
7 | introduction
8 | hero {
9 | ${image}
10 | }
11 | color {
12 | hex
13 | }
14 | tags {
15 | label
16 | }
17 | contents {
18 | ... on SingleLargeRecord {
19 | __typename
20 | isParallax
21 | media{
22 | ${image}
23 | }
24 | }
25 | ... on SingleMediumRecord {
26 | __typename
27 | media{
28 | ${image}
29 | }
30 | }
31 | ... on DuoRecord {
32 | __typename
33 | addMargin
34 | mediaLeft{
35 | ${image}
36 | }
37 | mediaRight{
38 | ${image}
39 | }
40 | }
41 | ... on ImgTextRecord {
42 | __typename
43 | media{
44 | ${image}
45 | }
46 | title
47 | text
48 | hasBackground
49 | }
50 | ... on TrioRecord {
51 | __typename
52 | media1{
53 | ${image}
54 | }
55 | media2{
56 | ${image}
57 | }
58 | media3{
59 | ${image}
60 | }
61 | }
62 | }
63 | }
64 | }`
65 |
--------------------------------------------------------------------------------
/assets/graphql/works.js:
--------------------------------------------------------------------------------
1 | import seo from './fragments/seo'
2 | import image from './fragments/image'
3 | export default `query worksPageQuery {
4 | worksPage {
5 | ${seo}
6 | works {
7 | slug
8 | title
9 | hero{
10 | ${image}
11 | }
12 | color {
13 | hex
14 | }
15 | }
16 | }
17 | }`
18 |
--------------------------------------------------------------------------------
/assets/images/wonderland.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/images/wonderland.jpg
--------------------------------------------------------------------------------
/assets/js/events/EventsEmitter.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events'
2 |
3 | let emitter = new EventEmitter()
4 | emitter.setMaxListeners(50)
5 | export default emitter
6 |
--------------------------------------------------------------------------------
/assets/js/head.js:
--------------------------------------------------------------------------------
1 | const head = (data) => {
2 | const seo = {
3 | title: data && data.title ? data.title : '',
4 | description: data && data.description ? data.description : '',
5 | image: data && data.image ? data.image.url : ''
6 | }
7 | return {
8 | title: `${seo.title}`,
9 | meta: [
10 | {
11 | hid: 'description',
12 | name: 'description',
13 | content: seo.description
14 | },
15 | {
16 | hid: 'og:description',
17 | property: 'og:description',
18 | content: seo.description
19 | },
20 | {
21 | hid: 'twitter:description',
22 | property: 'twitter:description',
23 | content: seo.description
24 | },
25 | {
26 | hid: `og:title`,
27 | property: 'og:title',
28 | content: `${seo.title}`
29 | },
30 | {
31 | hid: 'og:image',
32 | property: 'og:image',
33 | content: seo.image
34 | },
35 | {
36 | hid: 'twitter:image',
37 | property: 'twitter:image',
38 | content: seo.image
39 | }
40 | ]
41 | }
42 | }
43 | export default head
44 |
--------------------------------------------------------------------------------
/assets/js/math/clamp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Clamp a value between two bounds
3 | *
4 | * @param {number} v Value to clamp
5 | * @param {number} min Minimum boundary
6 | * @param {number} max Maximum boundary
7 | * @return {number} Clamped value
8 | */
9 | export default function clamp(v, min, max) {
10 | if (v < min) {
11 | return min
12 | }
13 | if (v > max) {
14 | return max
15 | }
16 | return v
17 | }
18 |
--------------------------------------------------------------------------------
/assets/js/math/distance.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Distance between two points
3 | *
4 | * @param {number} x1 X coord of the first point
5 | * @param {number} y1 Y coord of the first point
6 | * @param {number} x2 X coord of the second point
7 | * @param {number} y2 Y coord of the second point
8 | * @return {number} Computed distance
9 | */
10 | export default function distance(x1, y1, x2, y2) {
11 | const dx = x1 - x2
12 | const dy = y1 - y2
13 | return Math.sqrt(dx * dx + dy * dy)
14 | }
15 |
--------------------------------------------------------------------------------
/assets/js/math/index.js:
--------------------------------------------------------------------------------
1 | import clamp from './clamp'
2 | import distance from './distance'
3 | import lerp from './lerp'
4 | import lightenDarkenColor from './lightenDarkenColor'
5 | import modAbs from './modAbs'
6 | import map from './map'
7 | import normalize from './normalize'
8 | import parabola from './parabola'
9 | import randomFloat from './randomFloat'
10 | import randomHexColor from './randomHexColor'
11 | import randomInt from './randomInt'
12 | import smoothStep from './smoothStep'
13 |
14 | export {
15 | clamp,
16 | distance,
17 | lerp,
18 | lightenDarkenColor,
19 | modAbs,
20 | map,
21 | normalize,
22 | parabola,
23 | randomFloat,
24 | randomHexColor,
25 | randomInt,
26 | smoothStep
27 | }
28 |
--------------------------------------------------------------------------------
/assets/js/math/lerp.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Linear interpolation between two values (lerping)
3 | *
4 | * @param {number} x First point
5 | * @param {number} y Second point
6 | * @param {number} r Value to interpolate
7 | * @return {number} Lerped value
8 | */
9 | export default function lerp(x, y, r) {
10 | return x + (y - x) * r
11 | }
12 |
--------------------------------------------------------------------------------
/assets/js/math/lightenDarkenColor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Lighten or darken a color
3 | *
4 | * @param {string} col Color
5 | * @param {number} amt Amount
6 | * @return {string} Computed hexadecimal
7 | */
8 | export default function lightenDarkenColor(col, amt) {
9 | let usePound = false
10 |
11 | if (col[0] === '#') {
12 | col = col.slice(1)
13 | usePound = true
14 | }
15 |
16 | const num = parseInt(col, 16)
17 |
18 | let r = (num >> 16) + amt
19 |
20 | if (r > 255) {
21 | r = 255
22 | } else if (r < 0) {
23 | r = 0
24 | }
25 |
26 | let b = ((num >> 8) & 0x00ff) + amt
27 |
28 | if (b > 255) {
29 | b = 255
30 | } else if (b < 0) {
31 | b = 0
32 | }
33 |
34 | let g = (num & 0x0000ff) + amt
35 |
36 | if (g > 255) {
37 | g = 255
38 | } else if (g < 0) {
39 | g = 0
40 | }
41 |
42 | return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16)
43 | }
44 |
--------------------------------------------------------------------------------
/assets/js/math/map.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Re-maps a number from one range to another
3 | *
4 | * @param {number} value The incoming value to be converted
5 | * @param {number} start1 Lower bound of the value's current range
6 | * @param {number} stop1 Upper bound of the value's current range
7 | * @param {number} start2 Lower bound of the value's target range
8 | * @param {number} stop2 Upper bound of the value's target range
9 | * @return {number} Remapped number
10 | */
11 | export default function map(value, start1, stop1, start2, stop2) {
12 | return ((value - start1) / (stop1 - start1)) * (stop2 - start2) + start2
13 | }
14 |
--------------------------------------------------------------------------------
/assets/js/math/modAbs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Loop on an index value
3 | *
4 | * @param {number} index Index
5 | * @param {number} length Length
6 | * @return {number} Looped index
7 | */
8 | export default function modAbs(index, length) {
9 | if (index < 0) {
10 | index = length + (index % length)
11 | }
12 | if (index >= length) {
13 | return index % length
14 | }
15 | return index
16 | }
17 |
--------------------------------------------------------------------------------
/assets/js/math/normalize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Normalize a value between two bounds
3 | *
4 | * @param {number} min Minimum boundary
5 | * @param {number} max Maximum boundary
6 | * @param {number} x Value to normalize
7 | * @return {number} Normalized value
8 | */
9 | export default function normalize(min, max, x) {
10 | return (x - min) / (max - min)
11 | }
12 |
--------------------------------------------------------------------------------
/assets/js/math/parabola.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Remap the 0..1 interval into 0..1 parabola, such that the corners are remaped to 0 and the center to 1.
3 | * In other words, parabola(0) = parabola(1) = 0, and parabola(1/2) = 1.
4 | *
5 | * @param {number} k Value to map
6 | * @param {number} x Coordinate on X axis
7 | * @return {number} Mapped value
8 | */
9 | export default function parabola(k, x) {
10 | return Math.pow(4 * x * (1 - x), k)
11 | }
12 |
--------------------------------------------------------------------------------
/assets/js/math/randomFloat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Generate a random float
3 | *
4 | * @param {number} minValue Minimum boundary
5 | * @param {number} maxValue Maximum boundary
6 | * @param {number} precision Precision
7 | * @return {number} Generated float
8 | */
9 | export default function randomFloat(minValue, maxValue, precision = 2) {
10 | return parseFloat(
11 | Math.min(
12 | minValue + Math.random() * (maxValue - minValue),
13 | maxValue
14 | ).toFixed(precision)
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/assets/js/math/randomHexColor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Generate a random hexadecimal color
3 | *
4 | * @return {string} Hexadecimal color
5 | */
6 | export default function randomHexColor() {
7 | return (
8 | '#' + ('00000' + ((Math.random() * (1 << 24)) | 0).toString(16)).slice(-6)
9 | )
10 | }
11 |
--------------------------------------------------------------------------------
/assets/js/math/randomInt.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Generate a random integer
3 | *
4 | * @param {number} min Minimum boundary
5 | * @param {number} max Maximum boundary
6 | * @return {number} Generated integer
7 | */
8 | export default function randomInt(min, max) {
9 | return Math.floor(Math.random() * (max - min + 1) + min)
10 | }
11 |
--------------------------------------------------------------------------------
/assets/js/math/smoothStep.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Smooth a value
3 | *
4 | * @param {number} v Value
5 | * @param {number} min Minimum boundary
6 | * @param {number} max Maximum boundary
7 | * @return {number} Smoothed value
8 | */
9 | export default function smoothStep(v, min, max) {
10 | const x = Math.max(0, Math.min(1, (v - min) / (max - min)))
11 | return x * x * (3 - 2 * x)
12 | }
13 |
--------------------------------------------------------------------------------
/assets/js/utils/MouseHelper.js:
--------------------------------------------------------------------------------
1 | class MouseHelper {
2 | constructor() {
3 | this.isFirst = true
4 | this.isMouseNeeded = false
5 |
6 | this._mouseMoveHandler = this.mouseMoveHandler.bind(this)
7 | if (process.browser) {
8 | const isTouch = () => {
9 | try {
10 | document.createEvent('TouchEvent')
11 | return true
12 | } catch (e) {
13 | return false
14 | }
15 | }
16 | if (!isTouch()) {
17 | window.addEventListener('mousemove', this._mouseMoveHandler, {
18 | passive: true
19 | })
20 | }
21 | this.x = window.innerWidth * 0.5
22 | this.y = window.innerHeight * 0.5
23 | this.easeX = this.x
24 | this.easeY = this.y
25 | this.easeMouseX = this.x
26 | this.easeMouseY = this.y
27 | this.easeSlowX = this.x
28 | this.easeSlowY = this.y
29 | }
30 | }
31 |
32 | mouseMoveHandler(e) {
33 | this.x = e.clientX
34 | this.y = e.clientY
35 |
36 | this.isFirst = false
37 | }
38 | setMouse() {
39 | this.easeMouseX = this.easeSlowX = this.easeX = this.x
40 | this.easeMouseY = this.easeSlowY = this.easeY = this.y
41 | }
42 | tick() {
43 | const dX = this.x - this.easeX
44 | const dY = this.y - this.easeY
45 | const dMouseX = this.x - this.easeMouseX
46 | const dMouseY = this.y - this.easeMouseY
47 | const dSlowX = this.x - this.easeSlowX
48 | const dSlowY = this.y - this.easeSlowY
49 | this.easeX += dX / 10
50 | this.easeY += dY / 10
51 | this.easeMouseX += dMouseX / 6
52 | this.easeMouseY += dMouseY / 6
53 | this.easeSlowX += dSlowX / 20
54 | this.easeSlowY += dSlowY / 20
55 | }
56 | }
57 |
58 | export default new MouseHelper()
59 |
--------------------------------------------------------------------------------
/assets/js/utils/ResizeHelper.js:
--------------------------------------------------------------------------------
1 | import memoize from 'lodash/memoize'
2 | import Emitter from '~/assets/js/events/EventsEmitter'
3 |
4 | class ResizeHelper {
5 | constructor() {
6 | if (!process.browser) return
7 | this.dimension = memoize(this._dimension)
8 | const isTouch = () => {
9 | try {
10 | document.createEvent('TouchEvent')
11 | return true
12 | } catch (e) {
13 | return false
14 | }
15 | }
16 |
17 | window.addEventListener(
18 | //isTouch() ? 'orientationChange' : 'resize',
19 | 'resize',
20 | () => {
21 | clearTimeout(this.timeout)
22 | this.timeout = setTimeout(this.onResize.bind(this), 100)
23 | },
24 | { passive: true }
25 | )
26 | }
27 |
28 | onResize(e) {
29 | this.dimension = memoize(this._dimension)
30 | Emitter.emit('GLOBAL:RESIZE')
31 | }
32 | resetDimension() {
33 | this.dimension = memoize(this._dimension)
34 | }
35 |
36 | clear() {
37 | this.dimension.cache = {}
38 | }
39 |
40 | _dimension() {
41 | var body = document.body,
42 | html = document.documentElement
43 | var docHeight = Math.max(
44 | body.scrollHeight,
45 | body.offsetHeight,
46 | html.clientHeight,
47 | html.scrollHeight,
48 | html.offsetHeight
49 | )
50 | var dimension = {
51 | width: window.innerWidth,
52 | innerWidth: document.body.clientWidth,
53 | height: window.innerHeight,
54 | docHeight: docHeight
55 | }
56 |
57 | dimension.ratio = dimension.width / dimension.height
58 | return dimension
59 | }
60 |
61 | innerWidth() {
62 | return this.dimension().innerWidth
63 | }
64 |
65 | width() {
66 | return this.dimension().width
67 | }
68 |
69 | height() {
70 | return this.dimension().height
71 | }
72 |
73 | ratio() {
74 | return this.dimension().ratio
75 | }
76 |
77 | docHeight() {
78 | return this.dimension().docHeight
79 | }
80 | }
81 |
82 | export default new ResizeHelper()
83 |
--------------------------------------------------------------------------------
/assets/js/utils/ScrollHelper.js:
--------------------------------------------------------------------------------
1 | import Emitter from '~/assets/js/events/EventsEmitter'
2 | class ScrollHelper {
3 | constructor() {
4 | this.ease = 0
5 | this.easeSlow = 0
6 | this.scrollTop = 0
7 | this._preventDefault = this.preventDefault.bind(this)
8 | this._onScroll = this.onScroll.bind(this)
9 | this.tempScroll = 0
10 |
11 | if (process.browser) {
12 | this.doc = document.documentElement
13 | window.addEventListener('scroll', this._onScroll, { passive: true })
14 | }
15 | Emitter.on('WINDOW:DOSCROLL', this.scrollTo.bind(this))
16 | }
17 |
18 | onScroll() {
19 | if (this.isForceScrolling) {
20 | this.isForceScrolling = false
21 | } else {
22 | const scroll =
23 | (window.pageYOffset || this.doc.scrollTop) - (this.doc.clientTop || 0)
24 | if (scroll !== this.scrollTop) {
25 | this.scrollTop =
26 | (window.pageYOffset || this.doc.scrollTop) - (this.doc.clientTop || 0)
27 | this.scrollTop = Math.max(this.scrollTop, 0)
28 | }
29 | }
30 | }
31 | scrollTo(y) {
32 | //console.log('SCROLLHELPER scrollTo', y)
33 | window.scrollTo(0, y)
34 | this.scrollTop = y
35 | }
36 |
37 | goTo(y) {
38 | //console.log('SCROLLHELPER goTo', y)
39 | this.scrollTo(0, y)
40 | this.scrollTop = y
41 | this.ease = y
42 | this.easeSlow = y
43 | this.isForceScrolling = true
44 | }
45 | changePage(pageName) {
46 | this.pageName = pageName
47 | }
48 | resetScroll(y) {
49 | //console.log('SCROLLHELPER resetScroll', y)
50 | this.tick()
51 | if (y > this.scrollTop) {
52 | this.ease = this.ease + y
53 | this.easeSlow = this.easeSlow + y
54 | } else {
55 | this.ease = this.ease - this.scrollTop
56 | this.easeSlow = this.easeSlow - this.scrollTop
57 | }
58 |
59 | this.scrollTop = y
60 | this.scrollTo(y)
61 | this.tick()
62 | }
63 |
64 | tick() {
65 | this.tempScroll = this.scrollTop
66 | this.ease = this.lerp(this.ease, this.scrollTop, 0.07)
67 | this.easeSlow = Math.round(this.lerp(this.easeSlow, this.scrollTop, 0.06))
68 | }
69 | lerp(x, y, r) {
70 | return x + (y - x) * r
71 | }
72 |
73 | preventDefault(e) {
74 | e = e || window.event
75 | if (e.preventDefault) e.preventDefault()
76 | e.returnValue = false
77 | }
78 |
79 | disableScroll() {
80 | //if(window.addEventListener) window.addEventListener('DOMMouseScroll', this._preventDefault, {passive: true})
81 | //window.onwheel = this._preventDefault; // modern standard
82 | //window.onmousewheel = document.onmousewheel = this._preventDefault; // older browsers, IE
83 | }
84 |
85 | enableScroll() {
86 | //if(window.removeEventListener) window.removeEventListener('DOMMouseScroll', this._preventDefault, {passive: true})
87 | //window.onmousewheel = document.onmousewheel = null;
88 | ////window.onwheel = null;
89 | //window.ontouchmove = null;
90 | //document.onkeydown = null;
91 | }
92 | }
93 |
94 | export default new ScrollHelper()
95 |
--------------------------------------------------------------------------------
/assets/js/utils/SplitLines.js:
--------------------------------------------------------------------------------
1 | export default (el) => {
2 | const SplitText = require('gsap/SplitText').SplitText
3 | new SplitText(el, {
4 | type: 'lines',
5 | //type: 'words, chars, lines',
6 | //charsClass: 'letter',
7 | //wordsClass: 'word',
8 | linesClass: 'line'
9 | })
10 | const lines = [].slice.call(el.querySelectorAll('.line'))
11 |
12 | lines.forEach((el) => {
13 | el.innerHTML = '
' + el.innerHTML + '
'
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/assets/js/utils/WheelHelper.js:
--------------------------------------------------------------------------------
1 | import normalizeWheel from 'normalize-wheel'
2 | import { clamp } from '~/assets/js/math'
3 | class WheelHelper {
4 | constructor() {
5 | this.delta = 0
6 | this.deltaEase = 0
7 | this._onWheel = this.onWheel.bind(this)
8 | if (process.browser)
9 | window.addEventListener('wheel', this._onWheel, {
10 | passive: true
11 | })
12 | }
13 |
14 | onWheel(e) {
15 | const normalized = normalizeWheel(e)
16 | this.delta = clamp(normalized.pixelY, -60, 60)
17 | }
18 | tick() {
19 | let delta = Math.abs(this.delta)
20 | delta--
21 | if (delta < 0) delta = 0
22 | this.delta = Math.sign(this.delta) * delta
23 | const dX = this.delta - this.deltaEase
24 | this.deltaEase += dX / 10
25 | }
26 | }
27 |
28 | export default new WheelHelper()
29 |
--------------------------------------------------------------------------------
/assets/js/utils/datas/getAsyncData.js:
--------------------------------------------------------------------------------
1 | import getData from '~/assets/js/utils/datas/getData'
2 | const getAsyncData = async (context, query, name = '', variables = {}) => {
3 | const dataName = name !== '' ? name : context.route.name
4 | if (process.server && process.env.NODE_ENV === 'production') {
5 | var getQuery = require('~/assets/js/utils/datas/getQuery')
6 | const data = await getQuery.default(query, variables, dataName)
7 | if (!data) return
8 | var storeJson = require('~/assets/js/utils/datas/storeJson')
9 | await storeJson.default(dataName, data)
10 | return data
11 | }
12 |
13 | return getData(dataName)
14 | }
15 | export default getAsyncData
16 |
--------------------------------------------------------------------------------
/assets/js/utils/datas/getData.js:
--------------------------------------------------------------------------------
1 | import { get } from 'axios'
2 | let cache = new Map()
3 | const getData = async (name) => {
4 | return new Promise((resolve) => {
5 | const url = `${
6 | process.env.NODE_ENV !== 'production' ? 'http://localhost:3000' : ''
7 | }/api/${name}/index.json`
8 | let res = cache.get(url)
9 | ? cache.get(url)
10 | : get(url, { port: 3000 })
11 | .then((json) => {
12 | cache.set(url, json.data)
13 | return json.data
14 | })
15 | .catch((e) => {
16 | window.location.href = '/404'
17 | })
18 |
19 | resolve(res)
20 | })
21 | }
22 |
23 | export default getData
24 |
--------------------------------------------------------------------------------
/assets/js/utils/datas/getQuery.js:
--------------------------------------------------------------------------------
1 | import { GraphQLClient } from 'graphql-request'
2 | import config from './../../../../.datocms' // cause use in nuxt.config.js
3 | const cache = new Map()
4 | const getQuery = async (query, variables = {}, dataName) => {
5 | if (cache.get(dataName)) {
6 | return cache.get(dataName)
7 | }
8 | return new Promise(async (resolve, reject) => {
9 | const headers = {
10 | 'Content-Type': 'application/json',
11 | Accept: 'application/json',
12 | Authorization: `Bearer ${config.token}`
13 | }
14 | const graphQLClient = new GraphQLClient(config.httpEndpoint, {
15 | headers
16 | })
17 |
18 | graphQLClient
19 | .request(query, variables)
20 | .then((response) => {
21 | cache.set(dataName, response)
22 | resolve(response)
23 | })
24 | .catch((e) => {
25 | reject(e)
26 | throw e
27 | })
28 | })
29 | }
30 |
31 | export default getQuery
32 |
--------------------------------------------------------------------------------
/assets/js/utils/datas/storeJson.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const fs = require('fs')
3 | const storeJson = async (name, datas) => {
4 | return new Promise((resolve) => {
5 | const generate = require('~/nuxt.config.js').default.generate
6 | const filePath = process.env.isGenerating ? 'static/api/' : 'dist/api/'
7 | const file = path.join(filePath + name, 'index.json')
8 | return fs.promises
9 | .mkdir(path.dirname(file), { recursive: true })
10 | .then((x) => {
11 | fs.promises
12 | .writeFile(file, JSON.stringify(datas))
13 | .then((x) => resolve())
14 | })
15 | })
16 | }
17 | export default storeJson
18 |
--------------------------------------------------------------------------------
/assets/js/utils/easings.js:
--------------------------------------------------------------------------------
1 | var linear = function(t) {
2 | return t
3 | }
4 | var easeInQuad = function(t) {
5 | return t * t
6 | }
7 | var easeOutQuad = function(t) {
8 | return t * (2 - t)
9 | }
10 | var easeInOutQuad = function(t) {
11 | return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
12 | }
13 | var easeInCubic = function(t) {
14 | return t * t * t
15 | }
16 | var easeOutCubic = function(t) {
17 | return --t * t * t + 1
18 | }
19 | var easeInOutCubic = function(t) {
20 | return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
21 | }
22 | var easeInQuart = function(t) {
23 | return t * t * t * t
24 | }
25 | var easeOutQuart = function(t) {
26 | return 1 - --t * t * t * t
27 | }
28 | var easeInOutQuart = function(t) {
29 | return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t
30 | }
31 | var easeInQuint = function(t) {
32 | return t * t * t * t * t
33 | }
34 | var easeOutQuint = function(t) {
35 | return 1 + --t * t * t * t * t
36 | }
37 | var easeInOutQuint = function(t) {
38 | return t < 0.5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t
39 | }
40 |
41 | export {
42 | linear,
43 | easeInQuad,
44 | easeOutQuad,
45 | easeInOutQuad,
46 | easeInCubic,
47 | easeOutCubic,
48 | easeInOutCubic,
49 | easeInQuart,
50 | easeOutQuart,
51 | easeInOutQuart,
52 | easeInQuint,
53 | easeOutQuint,
54 | easeInOutQuint
55 | }
56 |
--------------------------------------------------------------------------------
/assets/scss/base/_fonts.scss:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Helvetica Neue';
3 | font-display: swap;
4 | src: local('HelveticaNeue Light'), local('HelveticaNeueLight'),
5 | url('~assets/fonts/helvetica-neue-light.woff2') format('woff2'),
6 | url('~assets/fonts/helvetica-neue-light.woff') format('woff');
7 | font-weight: 300;
8 | font-style: normal;
9 | }
10 | @font-face {
11 | font-family: 'Helvetica Neue';
12 | font-display: swap;
13 | src: local('HelveticaNeue'),
14 | url('~assets/fonts/helvetica-neue.woff2') format('woff2'),
15 | url('~assets/fonts/helvetica-neue.woff') format('woff');
16 | font-weight: normal;
17 | font-style: normal;
18 | }
19 |
20 | @font-face {
21 | font-family: 'Helvetica Neue';
22 | font-display: swap;
23 | src: local('Helvetica Neue Medium'), local('HelveticaNeue-Medium'),
24 | url('~assets/fonts/helvetica-neue-medium.woff2') format('woff2'),
25 | url('~assets/fonts/helvetica-neue-medium.woff') format('woff');
26 | font-weight: 500;
27 | font-style: normal;
28 | }
29 |
30 | @font-face {
31 | font-family: 'Orpheus Pro';
32 | font-display: swap;
33 | src: local('Orpheus Pro'), local('OrpheusPro'),
34 | url('~assets/fonts/orpheus-pro.woff2') format('woff2'),
35 | url('~assets/fonts/orpheus-pro.woff') format('woff');
36 | font-weight: normal;
37 | font-style: normal;
38 | }
39 |
--------------------------------------------------------------------------------
/assets/scss/base/_reset.scss:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | width: 100%;
4 | height: 100%;
5 | min-height: 100%;
6 | position: relative;
7 | margin: 0;
8 | padding: 0;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 | button {
13 | border: none;
14 | margin: 0;
15 | padding: 0;
16 | user-select: none;
17 | width: auto;
18 | outline: 0;
19 | overflow: visible;
20 | background: transparent;
21 | color: inherit;
22 | font: inherit;
23 | line-height: normal;
24 | text-align: center;
25 | text-decoration: none;
26 | cursor: pointer;
27 | white-space: normal;
28 | -webkit-font-smoothing: inherit;
29 | -moz-osx-font-smoothing: inherit;
30 | -webkit-appearance: none;
31 | &:disabled {
32 | opacity: 0.5;
33 | pointer-events: none;
34 | }
35 | }
36 | figure {
37 | margin: 0;
38 | }
39 | a {
40 | text-decoration: none;
41 | color: inherit;
42 | }
43 | ul,
44 | ol,
45 | li {
46 | list-style: none;
47 | margin: 0;
48 | padding: 0;
49 | }
50 | h1,
51 | h2,
52 | h3,
53 | h4,
54 | h5,
55 | h6,
56 | p {
57 | margin: 0;
58 | }
59 | * {
60 | box-sizing: border-box;
61 | }
62 | img,
63 | video,
64 | .video {
65 | width: 100%;
66 | display: block;
67 | height: auto;
68 | }
69 |
--------------------------------------------------------------------------------
/assets/scss/base/_typos.scss:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: $helvetica;
3 | font-size: vw(16);
4 | line-height: 1.75;
5 | font-weight: $light;
6 | letter-spacing: 0.01em;
7 | -ms-text-size-adjust: 100%;
8 | -webkit-text-size-adjust: 100%;
9 | -moz-osx-font-smoothing: grayscale;
10 | -webkit-font-smoothing: antialiased;
11 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
12 | }
13 |
14 | .upper {
15 | text-transform: uppercase;
16 | }
17 |
18 | @include below(medium) {
19 | html {
20 | font-size: vwM(18);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/assets/scss/fragments/_colors.scss:
--------------------------------------------------------------------------------
1 | .light {
2 | color: $black;
3 | background: $white;
4 |
5 | .loader {
6 | background: $white;
7 | svg:nth-child(2) {
8 | fill: $white;
9 | }
10 | svg:nth-child(3) {
11 | fill: $black;
12 | }
13 | svg:nth-child(4) {
14 | fill: $white;
15 | }
16 | svg:nth-child(5) {
17 | fill: $black;
18 | }
19 | }
20 | .counter {
21 | color: $white;
22 | }
23 | .progress {
24 | background: $black;
25 | }
26 | .inverse {
27 | color: $white;
28 | background: $black;
29 | }
30 |
31 | .scroll {
32 | background: $white;
33 | }
34 | .work-container {
35 | background: $white;
36 | }
37 | .top {
38 | a.home,
39 | a.projects,
40 | a.about {
41 | color: $white;
42 | }
43 | .home svg {
44 | fill: $white;
45 | }
46 | }
47 | }
48 |
49 | .dark {
50 | color: $white;
51 | background: $black;
52 | .loader {
53 | background: $black;
54 | svg:nth-child(2) {
55 | fill: $black;
56 | }
57 | svg:nth-child(3) {
58 | fill: $white;
59 | }
60 | svg:nth-child(4) {
61 | fill: $black;
62 | }
63 | svg:nth-child(5) {
64 | fill: $white;
65 | }
66 | }
67 |
68 | .progress {
69 | background: $white;
70 | }
71 | .counter {
72 | color: $black;
73 | }
74 | .inverse {
75 | color: $black;
76 | background: $white;
77 | }
78 |
79 | .top {
80 | a.home,
81 | a.projects,
82 | a.about {
83 | color: $black;
84 | }
85 | .home svg {
86 | fill: $black;
87 | }
88 | }
89 | .work-container {
90 | background: $black;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/assets/scss/fragments/_container.scss:
--------------------------------------------------------------------------------
1 | .c {
2 | position: relative;
3 | margin: 0 auto;
4 | &--1000 {
5 | width: vw(1000);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/assets/scss/fragments/_top.scss:
--------------------------------------------------------------------------------
1 | .top {
2 | position: relative;
3 | height: 100vh;
4 | min-height: 100vh;
5 | min-height: -webkit-fill-available;
6 | width: 100vw;
7 | &--centered {
8 | display: flex;
9 | align-items: center;
10 | justify-content: center;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/assets/scss/includes.scss:
--------------------------------------------------------------------------------
1 | @import 'includes/mixinViewportWidth';
2 | @import 'includes/colors';
3 | @import 'includes/easings';
4 | @import 'includes/mq';
5 | @import 'includes/variables';
6 | @import 'includes/spacings';
7 |
--------------------------------------------------------------------------------
/assets/scss/includes/_colors.scss:
--------------------------------------------------------------------------------
1 | $black: #1a1a1a;
2 | $white: #f6eee3;
3 |
--------------------------------------------------------------------------------
/assets/scss/includes/_easings.scss:
--------------------------------------------------------------------------------
1 | // SINE
2 | $ease-in-sine: cubic-bezier(0.47, 0, 0.745, 0.715);
3 | $ease-out-sine: cubic-bezier(0.39, 0.575, 0.565, 1);
4 | $ease-in-out-sine: cubic-bezier(0.445, 0.05, 0.55, 0.95);
5 |
6 | // QUAD
7 | $ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53);
8 | $ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94);
9 | $ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955);
10 |
11 | // CUBIC
12 | $ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19);
13 | $ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
14 | $ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1);
15 |
16 | // QUART
17 | $ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22);
18 | $ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
19 | $ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1);
20 |
21 | // QUINT
22 | $ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
23 | $ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
24 | $ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1);
25 |
26 | // EXPO
27 | $ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035);
28 | $ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1);
29 | $ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
30 |
31 | // CIRC
32 | $ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335);
33 | $ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1);
34 | $ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86);
35 |
36 | // BACK
37 | $ease-in-back: cubic-bezier(0.6, -0.28, 0.735, 0.045);
38 | $ease-out-back: cubic-bezier(0.175, 0.885, 0.32, 1.275);
39 | $ease-in-out-back: cubic-bezier(0.68, -0.55, 0.265, 1.55);
40 |
--------------------------------------------------------------------------------
/assets/scss/includes/_mixinViewportWidth.scss:
--------------------------------------------------------------------------------
1 | @function vw($width) {
2 | @return 100 * $width / 1380 * 1vw;
3 | }
4 | @function vwM($width) {
5 | @return 100 * $width / 420 * 1vw;
6 | }
7 |
--------------------------------------------------------------------------------
/assets/scss/includes/_mq.scss:
--------------------------------------------------------------------------------
1 | $breakpoints: (
2 | 'small': 767px,
3 | 'medium': 1000px,
4 | 'tablet': 1024px
5 | );
6 | @mixin below($breakpoint) {
7 | $value: map-get($breakpoints, $breakpoint);
8 | @if $value != null {
9 | @media (max-width: $value) {
10 | @content;
11 | }
12 | } @else {
13 | @warn "Unfortunately, no value could be retrieved from `#{$breakpoint}`. "
14 | + "Please make sure it is defined in `$breakpoints` map.";
15 | }
16 | }
17 | @mixin above($breakpoint) {
18 | $value: map-get($breakpoints, $breakpoint);
19 | @if $value != null {
20 | @media (min-width: $value) {
21 | @content;
22 | }
23 | } @else {
24 | @warn "Unfortunately, no value could be retrieved from `#{$breakpoint}`. "
25 | + "Please make sure it is defined in `$breakpoints` map.";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/assets/scss/includes/_spacings.scss:
--------------------------------------------------------------------------------
1 | $space100: vw(100);
2 | $space100M: vwM(100);
3 | $space60: vw(60);
4 | $space60M: vwM(60);
5 |
--------------------------------------------------------------------------------
/assets/scss/includes/_variables.scss:
--------------------------------------------------------------------------------
1 | $orpheus: 'Orpheus Pro', Times, serif;
2 | $helvetica: 'Helvetica Neue', Arial, sans;
3 | $light: 300;
4 | $regular: 400;
5 | $medium: 500;
6 |
--------------------------------------------------------------------------------
/assets/scss/main.scss:
--------------------------------------------------------------------------------
1 | @import 'base/reset';
2 | @import 'base/fonts';
3 | @import 'base/typos';
4 | @import 'fragments/top';
5 | @import 'fragments/container';
6 | @import 'fragments/colors';
7 | html {
8 | }
9 | html,
10 | body,
11 | #__nuxt,
12 | #__layout,
13 | .__layout {
14 | height: 100%;
15 | position: relative;
16 | width: 100%;
17 | overscroll-behavior: none;
18 | }
19 | *,
20 | *:before,
21 | *:after {
22 | box-sizing: border-box;
23 | margin: 0;
24 | }
25 |
26 | .no-touch::-webkit-scrollbar-track {
27 | -webkit-box-shadow: none;
28 | background-color: transparent;
29 | }
30 | .no-touch::-webkit-scrollbar {
31 | width: 0px;
32 | background-color: transparent;
33 | }
34 | .no-touch::-webkit-scrollbar-thumb {
35 | background-color: transparent;
36 | scrollbar-width: none;
37 | }
38 |
--------------------------------------------------------------------------------
/assets/svgs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/assets/svgs/.gitkeep
--------------------------------------------------------------------------------
/assets/svgs/logo.svg:
--------------------------------------------------------------------------------
1 |
2 | Simon Daufresne
3 |
4 |
5 |
--------------------------------------------------------------------------------
/components/README.md:
--------------------------------------------------------------------------------
1 | # COMPONENTS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | The components directory contains your Vue.js Components.
6 |
7 | _Nuxt.js doesn't supercharge these components._
8 |
--------------------------------------------------------------------------------
/components/about/awards/award.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
63 |
105 |
--------------------------------------------------------------------------------
/components/about/awards/awards.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
54 |
68 |
--------------------------------------------------------------------------------
/components/about/clients/client.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
37 |
51 |
--------------------------------------------------------------------------------
/components/about/clients/clients.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
54 |
72 |
--------------------------------------------------------------------------------
/components/about/footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
92 |
133 |
--------------------------------------------------------------------------------
/components/about/links/link.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
33 |
46 |
--------------------------------------------------------------------------------
/components/about/links/links.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
36 |
45 |
--------------------------------------------------------------------------------
/components/common/loader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
63 |
64 |
92 |
--------------------------------------------------------------------------------
/components/common/navigation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 | Projects
12 |
13 |
15 | About
17 |
18 |
20 |
21 |
27 |
28 | Scroll
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
45 | Projects
47 |
55 | About
57 |
58 |
66 |
67 |
68 |
69 |
70 |
71 |
213 |
355 |
--------------------------------------------------------------------------------
/components/common/scroller.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
24 |
25 |
39 |
--------------------------------------------------------------------------------
/components/common/title.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
143 |
177 |
--------------------------------------------------------------------------------
/components/common/transition.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
109 |
127 |
--------------------------------------------------------------------------------
/components/componentLoader.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
52 |
60 |
--------------------------------------------------------------------------------
/components/fragments/image.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
69 |
--------------------------------------------------------------------------------
/components/fragments/media.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
33 |
--------------------------------------------------------------------------------
/components/fragments/video.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
16 |
26 |
27 |
28 |
55 |
67 |
--------------------------------------------------------------------------------
/components/next/counter.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ current }} / 3
4 |
5 |
13 |
14 |
15 |
16 |
73 |
104 |
--------------------------------------------------------------------------------
/components/next/project-next.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Selected Projects
4 |
Next Project
5 |
6 |
7 |
13 |
14 |
15 |
20 |
21 |
22 |
156 |
187 |
--------------------------------------------------------------------------------
/components/next/project.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
15 |
16 |
17 |
18 |
19 |
74 |
122 |
--------------------------------------------------------------------------------
/components/next/projects.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Selected Projects
4 |
Next Project
5 |
6 |
7 |
13 |
14 |
15 |
20 |
21 |
22 |
156 |
187 |
--------------------------------------------------------------------------------
/components/scene/comps/Main.js:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture } from 'ogl/src/index.mjs'
2 |
3 | import Plane from './Plane.js'
4 | import ResizeHelper from '~/assets/js/utils/ResizeHelper.js'
5 | import emitter from '~/assets/js/events/EventsEmitter.js'
6 | import MouseHelper from '~/assets/js/utils/MouseHelper.js'
7 | import clamp from '~/assets/js/math/clamp.js'
8 |
9 | export default class Main {
10 | constructor(el, allWorks, reducedMotion = false) {
11 | this.reducedMotion = reducedMotion
12 | this.time = 0
13 | this.scrollTop = 0
14 | this.cameraZ = 100
15 | this.mousePosCoef = 0.2
16 | this.mouseAngleDivider = 100
17 | this.cameraX = 0
18 | this.cameraY = 0
19 | this.rotateX = 0
20 | this.rotateY = 0
21 | this.isShownBottom = false
22 | this.planes = []
23 | this.textureArray = []
24 | this.allWorks = allWorks
25 | this.init(el)
26 | }
27 |
28 | init(el) {
29 | this.renderer = new Renderer({ dpr: 1.5, alpha: true })
30 | this.gl = this.renderer.gl
31 |
32 | el.appendChild(this.gl.canvas)
33 | this.scene = new Transform()
34 | this.camera = new Camera(this.gl)
35 | this.camera.far = 200
36 | this.camera.position.z = this.cameraZ
37 | this.loadProjects()
38 | emitter.on('go-next', this.goNext.bind(this))
39 | }
40 | loadProjects() {
41 | this.projectLoaded = 0
42 | this.loadProject(this.allWorks[this.projectLoaded])
43 | }
44 | canUseWebP() {
45 | var elem = document.createElement('canvas')
46 |
47 | if (!!(elem.getContext && elem.getContext('2d'))) {
48 | // was able or not to get WebP representation
49 | return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0
50 | }
51 |
52 | // very old browser like IE 8, canvas not supported
53 | return false
54 | }
55 | canUseAvif() {
56 | var elem = document.createElement('canvas')
57 |
58 | if (!!(elem.getContext && elem.getContext('2d'))) {
59 | // was able or not to get WebP representation
60 | return elem.toDataURL('image/avif').indexOf('data:image/avif') == 0
61 | }
62 |
63 | // very old browser like IE 8, canvas not supported
64 | return false
65 | }
66 | loadProject(work) {
67 | const img = new Image()
68 | img.crossOrigin = 'anonymous'
69 | const imageTex = new Texture(this.gl, { minFilter: this.gl.NEAREST })
70 |
71 | imageTex.slug = work.slug
72 | img.onload = () => {
73 | imageTex.image = img
74 | this.textureArray.push(imageTex)
75 | if (this.projectLoaded < this.allWorks.length - 1) {
76 | this.loadProject(this.allWorks[++this.projectLoaded])
77 | } else {
78 | this.onLoaded()
79 | }
80 | }
81 |
82 | let size = ''
83 | if (Math.min(ResizeHelper.width(), ResizeHelper.height()) < 600) {
84 | size = '@1x'
85 | } else if (Math.min(ResizeHelper.width(), ResizeHelper.height()) < 300) {
86 | size = '@.5x'
87 | } else if (Math.min(ResizeHelper.width(), ResizeHelper.height()) < 150) {
88 | size = '@mx'
89 | }
90 | const ext = this.canUseAvif()
91 | ? '.avif'
92 | : this.canUseWebP()
93 | ? '.webp'
94 | : '.jpg'
95 | let imgUrl = work.hero.url.replace('.jpg', size + ext)
96 |
97 | img.src = imgUrl
98 | }
99 | onLoaded() {
100 | this.textureArray.forEach((tex, i) => {
101 | const plane = new Plane(this.gl, this.scene, tex, i, this.reducedMotion)
102 | this.planes.push(plane)
103 | })
104 |
105 | this.textureArray.forEach((tex, i) => {
106 | const plane = new Plane(this.gl, this.scene, tex, i, this.reducedMotion)
107 | this.planes.push(plane)
108 | })
109 | this.planes.forEach((plane, i) => {
110 | plane.setRouteName(this.routeName)
111 | })
112 | if (this.id !== -1) this.planes[this.id].show()
113 | this.resize()
114 | }
115 | doRouteTransition(id) {
116 | this.isTransitioning = true
117 | this.id = id
118 | if (!this.planes.length) return
119 | gsap.to(this, {
120 | cameraZ: 100,
121 | mousePosCoef: 0.2,
122 | mouseAngleDivider: 100,
123 | cameraX: 0,
124 | cameraY: 0,
125 | rotateX: 0,
126 | rotateY: 0,
127 | duration: this.reducedMotion ? 0 : 2,
128 | ease: 'power3.inOut'
129 | })
130 | this.planes.forEach((plane, i) => {
131 | if (i === this.id) {
132 | plane.changePage()
133 | }
134 | })
135 | }
136 |
137 | changeRoute(id) {
138 | this.id = id
139 | this.currentPage = id
140 | this.isShownBottom = false
141 | if (!this.planes.length) return
142 | this.planes.forEach((plane, i) => {
143 | if (i !== this.id) {
144 | plane.reset()
145 | }
146 | })
147 | }
148 |
149 | goNext() {
150 | if (this.id !== -1) {
151 | this.planes[this.id].hide()
152 | }
153 | this.id++
154 | if (this.id === 3) this.id = 0
155 | this.planes[this.id].show()
156 | }
157 |
158 | showBottom() {
159 | if (this.isShownBottom) return
160 | this.isShownBottom = true
161 | this.id = this.currentPage + 1
162 |
163 | if (this.id == this.allWorks.length) this.id = 0
164 | this.planes[this.id].show()
165 | }
166 | onWorkEnter(obj) {
167 | this.id = obj.id
168 | this.currentPage = obj.id
169 | if (this.planes[obj.id].alpha !== 0) {
170 | const p = this.planes[obj.id + this.planes.length / 2]
171 | this.planes[obj.id + this.planes.length / 2] = this.planes[obj.id]
172 | this.planes[obj.id] = p
173 | }
174 | this.planes[obj.id].show(obj)
175 | }
176 |
177 | onWorkLeave(obj) {
178 | this.id = -1
179 | this.planes[obj.id].hide()
180 | }
181 |
182 | setRouteName(name) {
183 | if (!this.isTransitioning) {
184 | this.cameraX = 0
185 | this.cameraY = 0
186 | this.mousePosCoef = 0.2
187 | this.mouseAngleDivider = 100
188 | this.rotateX = 0
189 | this.rotateY = 0
190 | this.cameraZ = 100
191 | }
192 | this.routeName = name
193 | this.isTransitioning = false
194 | if (this.routeName === 'work') {
195 | this.cameraZ = 150
196 | this.mousePosCoef = 0.8
197 | this.mouseAngleDivider = 50
198 | }
199 | }
200 |
201 | tick(scrollTop) {
202 | const vFOV = (60 * Math.PI) / 180
203 | const height = 2 * Math.tan(vFOV / 2) * Math.abs(this.cameraZ)
204 | const width = (height * this.w) / this.h
205 | this.cameraX =
206 | -((MouseHelper.easeX - this.w / 2) / this.w) * width * this.mousePosCoef //magic number
207 | this.cameraY =
208 | ((MouseHelper.easeY - this.h / 2) / this.h) * height * this.mousePosCoef
209 | this.rotateX =
210 | (clamp((MouseHelper.easeY - MouseHelper.easeSlowY) * 0.1, -15, 15) *
211 | Math.PI) /
212 | this.mouseAngleDivider
213 | this.rotateY =
214 | (-clamp((MouseHelper.easeX - MouseHelper.easeSlowX) * 0.1, -15, 15) *
215 | Math.PI) /
216 | this.mouseAngleDivider
217 | if (this.routeName == 'work' && !this.isTransitioning) {
218 | }
219 | this.camera.position.x = this.cameraX
220 | this.camera.position.y = this.cameraY
221 |
222 | if (!this.reducedMotion) this.time += 0.016
223 |
224 | this.planes.forEach((plane, i) => {
225 | const scroll =
226 | this.routeName === 'index'
227 | ? (scrollTop - this.pageHeight) / 8
228 | : this.routeName === 'work'
229 | ? 0
230 | : this.currentPage === i
231 | ? scrollTop / 10
232 | : (scrollTop - this.pageHeight) / 8
233 | plane.render(scroll, this.rotateX, this.rotateY, this.time)
234 | })
235 | this.camera.position.z = this.cameraZ
236 | this.renderer.render({ scene: this.scene, camera: this.camera })
237 | }
238 |
239 | resize(w, h, pageHeight) {
240 | if (w && h) {
241 | this.w = w
242 | this.h = h
243 | this.pageHeight = pageHeight - h
244 | }
245 | this.planes.forEach((plane) => {
246 | plane.resize(this.w, this.h)
247 | })
248 | this.camera.perspective({ aspect: this.w / this.h })
249 | this.renderer.setSize(this.w, this.h)
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/components/scene/comps/MainAbout.js:
--------------------------------------------------------------------------------
1 | import { Renderer, Camera, Transform, Texture } from 'ogl/src/index.mjs'
2 |
3 | import Plane from './Plane.js'
4 | import ResizeHelper from '~/assets/js/utils/ResizeHelper.js'
5 | import emitter from '~/assets/js/events/EventsEmitter.js'
6 | import MouseHelper from '~/assets/js/utils/MouseHelper.js'
7 | import clamp from '~/assets/js/math/clamp.js'
8 |
9 | export default class Main {
10 | constructor(el, allWorks, reducedMotion = false) {
11 | this.reducedMotion = reducedMotion
12 | this.time = 0
13 | this.scrollTop = 0
14 | this.cameraZ = 100
15 | this.cameraX = 0
16 | this.cameraY = 0
17 | this.rotateX = 0
18 | this.rotateY = 0
19 | this.mousePosCoef = 0.2
20 | this.mouseAngleDivider = 100
21 | this.routeName = 'index'
22 | this.isTransitioning = false
23 | this.isShownBottom = false
24 | this.planes = []
25 | this.textureArray = []
26 | this.allWorks = allWorks
27 | this.init(el)
28 | }
29 |
30 | init(el) {
31 | this.renderer = new Renderer({ dpr: 1.5, alpha: true })
32 | this.gl = this.renderer.gl
33 | el.appendChild(this.gl.canvas)
34 | this.scene = new Transform()
35 | this.camera = new Camera(this.gl)
36 | this.camera.far = 200
37 | this.camera.position.z = this.cameraZ
38 | this.loadProjects()
39 | }
40 | loadProjects() {
41 | this.projectLoaded = 0
42 | this.loadProject(this.allWorks[this.projectLoaded])
43 | }
44 | canUseWebP() {
45 | var elem = document.createElement('canvas')
46 |
47 | if (!!(elem.getContext && elem.getContext('2d'))) {
48 | // was able or not to get WebP representation
49 | return elem.toDataURL('image/webp').indexOf('data:image/webp') == 0
50 | }
51 |
52 | // very old browser like IE 8, canvas not supported
53 | return false
54 | }
55 | loadProject(work) {
56 | const img = new Image()
57 | img.crossOrigin = 'anonymous'
58 | const imageTex = new Texture(this.gl, { minFilter: this.gl.NEAREST })
59 |
60 | imageTex.slug = 'about'
61 | img.onload = () => {
62 | imageTex.image = img
63 | this.textureArray.push(imageTex)
64 | if (this.projectLoaded < this.allWorks.length - 1) {
65 | this.loadProject(this.allWorks[++this.projectLoaded])
66 | } else {
67 | this.onLoaded()
68 | }
69 | }
70 |
71 | let size = ''
72 | if (Math.min(ResizeHelper.width(), ResizeHelper.height()) < 600) {
73 | size = '@1x'
74 | } else if (Math.min(ResizeHelper.width(), ResizeHelper.height()) < 300) {
75 | size = '@.5x'
76 | } else if (Math.min(ResizeHelper.width(), ResizeHelper.height()) < 150) {
77 | size = '@mx'
78 | }
79 |
80 | let ext
81 | if (work.indexOf('png') !== -1) {
82 | ext = this.canUseWebP() ? '.webp' : '.png'
83 | } else {
84 | ext = this.canUseWebP() ? '.webp' : '.jpg'
85 | }
86 | let imgUrl = work.replace('.jpg', size + ext).replace('.png', size + ext)
87 | img.src = imgUrl
88 | }
89 | onLoaded() {
90 | this.textureArray.forEach((tex, i) => {
91 | const plane = new Plane(this.gl, this.scene, tex, i, this.reducedMotion)
92 | this.planes.push(plane)
93 | })
94 | this.planes.forEach((plane, i) => {
95 | plane.setRouteName('index')
96 | })
97 | this.resize()
98 | this.planes[0].show()
99 | }
100 | hideCard() {
101 | this.planes[0].hide()
102 | }
103 |
104 | tick(scrollTop) {
105 | const vFOV = (60 * Math.PI) / 180
106 | const height = 2 * Math.tan(vFOV / 2) * Math.abs(this.cameraZ)
107 | const width = (height * this.w) / this.h
108 | this.cameraX =
109 | -((MouseHelper.easeX - this.w / 2) / this.w) * width * this.mousePosCoef //magic number
110 | this.cameraY =
111 | ((MouseHelper.easeY - this.h / 2) / this.h) * height * this.mousePosCoef
112 | this.rotateX =
113 | (clamp((MouseHelper.easeY - MouseHelper.easeSlowY) * 0.1, -15, 15) *
114 | Math.PI) /
115 | this.mouseAngleDivider
116 | this.rotateY =
117 | (-clamp((MouseHelper.easeX - MouseHelper.easeSlowX) * 0.1, -15, 15) *
118 | Math.PI) /
119 | this.mouseAngleDivider
120 |
121 | this.camera.position.x = this.cameraX
122 | this.camera.position.y = this.cameraY
123 |
124 | if (!this.reducedMotion) this.time += 0.016
125 |
126 | this.planes.forEach((plane, i) => {
127 | plane.render(scrollTop, this.rotateX, this.rotateY, this.time)
128 | })
129 | this.camera.position.z = this.cameraZ
130 | this.renderer.render({ scene: this.scene, camera: this.camera })
131 | }
132 |
133 | resize(w, h) {
134 | if (w && h) {
135 | this.w = w
136 | this.h = h
137 | }
138 | if (this.w < 1000) {
139 | this.mousePosCoef = 0
140 | this.cameraZ = 70
141 | }
142 | this.planes.forEach((plane) => {
143 | plane.resize(this.w, this.h)
144 | })
145 | this.camera.perspective({ aspect: this.w / this.h })
146 | this.renderer.setSize(this.w, this.h)
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/components/scene/comps/Plane.js:
--------------------------------------------------------------------------------
1 | //needs 2 img text for transition so only one plane in the stage!
2 | import { Plane, Vec2, Mesh, Program } from 'ogl/src/index.mjs'
3 | import planeVs from '../shaders/planeCover.vs.js'
4 | import planeFs from '../shaders/planeCover.fs.js'
5 | export default class PlaneGeo {
6 | constructor(gl, scene, texture, id, reducedMotion = false) {
7 | this.id = id
8 | this.gl = gl
9 | this.texture = texture
10 | this.reducedMotion = reducedMotion
11 | this.reset()
12 | this.init(scene)
13 | }
14 | init(scene) {
15 | const program = new Program(this.gl, {
16 | vertex: planeVs,
17 | fragment: planeFs,
18 | transparent: true,
19 | uniforms: {
20 | uTime: { value: 0 },
21 | uMap: { value: this.texture },
22 | uResolution: { value: new Vec2() },
23 | uAlpha: { value: 0 },
24 | uTransition: { value: this.transition }
25 | }
26 | })
27 | this.time = 0
28 | const geometry = new Plane(this.gl, {
29 | width: window.innerWidth < 1000 ? 25 : 35,
30 | height: window.innerWidth < 1000 ? 32 : 45,
31 | widthSegments: window.innerWidth < 1000 ? 10 : 30,
32 | heightSegments: window.innerWidth < 1000 ? 15 : 40
33 | })
34 |
35 | this.mesh = new Mesh(this.gl, { geometry, program })
36 | this.mesh.setParent(scene)
37 | }
38 | show(obj) {
39 | gsap.killTweensOf(this)
40 | gsap.to(this, {
41 | transition: 0,
42 | duration: this.reducedMotion ? 0 : 3,
43 | ease: 'Elastic.out'
44 | })
45 |
46 | gsap.to(this, { alpha: 1, duration: this.reducedMotion ? 0 : 0.3 })
47 | gsap.to(this, {
48 | positionZ: 0,
49 | positionX: this.w < 1000 ? 0 : -4,
50 | positionY: 0,
51 | transitionRotZ: 0,
52 | transitionRotX: 0,
53 | transitionRotY: 0,
54 | duration: this.reducedMotion ? 0 : 2,
55 | ease: 'power4.out'
56 | })
57 | }
58 | hide() {
59 | gsap.killTweensOf(this)
60 | gsap.to(this, {
61 | positionZ: -10,
62 | positionX: -75,
63 | positionY: 100,
64 | transitionRotZ: -1,
65 | transitionRotX: -1,
66 | transitionRotY: -1,
67 | transition: 1,
68 | duration: this.reducedMotion ? 0 : 1.5,
69 | ease: 'power4.in '
70 | })
71 | gsap
72 | .to(this, {
73 | alpha: 0,
74 | delay: 0.3,
75 | duration: this.reducedMotion ? 0 : 1.2,
76 | ease: 'power4.out '
77 | })
78 | .then(() => {
79 | this.reset()
80 | })
81 | }
82 | resize(w, h) {
83 | this.w = w
84 | this.h = h
85 | if (this.mesh) {
86 | this.mesh.program.uniforms.uResolution.value = new Vec2(w, h)
87 | }
88 | }
89 | changePage() {
90 | this.positionZ = 0
91 | this.transitionRotZ = 0
92 | this.transitionRotX = 0
93 | this.transitionRotY = 0
94 | this.transition = 0
95 | gsap.killTweensOf(this)
96 | gsap.to(this, {
97 | transition: 0.5,
98 | positionZ: 20,
99 | transitionRotX: -0.4,
100 | transitionRotY: 0.4,
101 | duration: this.reducedMotion ? 0 : 0.75,
102 | delay: 0.25,
103 | repeat: 1,
104 | yoyo: true,
105 | ease: 'Power3.out'
106 | })
107 | gsap.to(this, {
108 | rotZ: -Math.PI / 20,
109 | duration: this.reducedMotion ? 0 : 2,
110 | alpha: 1,
111 | delay: 0.25,
112 | ease: 'power4.out '
113 | })
114 | gsap.to(this, {
115 | positionY: 0,
116 | positionX: 0,
117 | duration: this.reducedMotion ? 0 : 2,
118 | ease: 'power3.inOut '
119 | })
120 | }
121 |
122 | setRouteName(name) {
123 | this.routeName = name
124 | }
125 | reset() {
126 | this.alpha = 0
127 | this.positionZ = 40
128 | this.positionX = 16
129 | this.positionY = -60
130 | this.transitionRotZ = 1
131 | this.transitionRotX = 1
132 | this.transitionRotY = 1
133 | this.rotZ = Math.PI / 20
134 | this.transition = 1
135 | }
136 |
137 | render(scrollTop, rotX, rotY, time) {
138 | this.mesh.position.z = this.positionZ
139 | this.mesh.position.x = this.positionX
140 | this.mesh.position.y = this.positionY + scrollTop
141 | this.mesh.rotation.y =
142 | rotY +
143 | (Math.PI / 5) * this.transitionRotY +
144 | (this.routeName !== 'work' ? scrollTop / 100 : 0)
145 | this.mesh.rotation.x = rotX + (-Math.PI / 6) * this.transitionRotX
146 | this.mesh.rotation.z = this.rotZ + (-Math.PI / 6) * this.transitionRotZ
147 | this.mesh.program.uniforms.uAlpha.value = this.alpha
148 | this.mesh.program.uniforms.uTime.value = time
149 | this.mesh.program.uniforms.uTransition.value = this.transition
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/components/scene/comps/screenunit.js:
--------------------------------------------------------------------------------
1 | // Un calcul d’une “screen unit” à une distance Z donnée, et tu convertis ton boundingClientRect par cette screenUnit
2 | // const x = bounding.width / $viewport.width;
3 | // const y = bounding.height / $viewport.height;
4 | // instance.scale.x = cameraUnitSize.width * x;
5 | // instance.scale.y = cameraUnitSize.height * y;
6 | // ou cameraUnitSize serait calculé de cette façon :
7 | // getUnitSize(depth = 0) {
8 | // const offset = this.position.z;
9 | // if (depth < offset) {
10 | // depth -= offset;
11 | // } else {
12 | // depth += offset;
13 | // }
14 | // const vFOV = (this.fov * Math.PI) / 180; // vertical fov in radians
15 | // // Math.abs to ensure the result is always positive
16 | // const height = 2 * Math.tan(vFOV / 2) * Math.abs(depth);
17 | // const width = height * this.aspect;
18 | // return {
19 | // height,
20 | // width
21 | // };
22 | // }
23 |
--------------------------------------------------------------------------------
/components/scene/scene-about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
43 |
44 |
58 |
--------------------------------------------------------------------------------
/components/scene/scene.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
94 |
95 |
119 |
--------------------------------------------------------------------------------
/components/scene/shaders/plane.fs.js:
--------------------------------------------------------------------------------
1 | export default `
2 | precision highp float;
3 | precision highp int;
4 |
5 | varying vec3 vNormal;
6 | uniform float uTime;
7 | uniform sampler2D uMap;
8 | uniform sampler2D uMapNext;
9 | uniform sampler2D uMask;
10 | uniform vec2 uResolution;
11 | uniform float uMaskScale;
12 | uniform float uImgScale;
13 | uniform float uGlobalAlpha;
14 | uniform float uTransition;
15 | uniform float uIndexTransition;
16 | uniform float uRippleEffect;
17 | varying float vCoef;
18 | varying vec2 vUv;
19 | varying vec3 vPos;
20 | #define PI 3.1415926538
21 |
22 |
23 |
24 | void main() {
25 | float screenRatio = uResolution.x / uResolution.y;
26 | float coverRatio = 3500. / 2156.;
27 | float maskRatio = 1.;
28 | vec2 uvMask = vUv;
29 | vec2 uvImg = vUv;
30 | if (screenRatio > coverRatio) {
31 | uvImg.y -= 0.5;
32 | uvImg.y /= screenRatio * ( 1. / coverRatio );
33 | uvImg.y += 0.5;
34 | } else {
35 | uvImg.x -= 0.5;
36 | uvImg.x *= screenRatio * ( 1. / coverRatio );
37 | uvImg.x += 0.5;
38 | }
39 | if (screenRatio > maskRatio) {
40 | uvMask.y -= 0.5;
41 | uvMask.y /= screenRatio * ( 1. / maskRatio );
42 | uvMask.y += 0.5;
43 | } else {
44 | uvMask.x -= 0.5;
45 | uvMask.x *= screenRatio * ( 1. / maskRatio );
46 | uvMask.x += 0.5;
47 | }
48 | float scaleMask = 3. - min(2.7, uMaskScale * 2.7 );
49 | float scale = 2. - min(1., uImgScale );
50 | vec3 maskTex = texture2D(uMask, uvMask * scaleMask - vec2(.5, .5) * (scaleMask - 1.) ).rgb;
51 |
52 | vec3 imgTex = texture2D(uMap, uvImg * scale - vec2(.5, .5) * (scale - 1.)).rgb;
53 |
54 |
55 | vec3 light = normalize(vec3(0.5, 1.0, -0.3));
56 | float shading = dot(normalize(vPos), light) * 0.5;
57 |
58 |
59 |
60 |
61 |
62 | float t = uTime*.3;
63 |
64 | vec2 uv = (gl_FragCoord.xy - .5 * uResolution.xy) / uResolution.y;
65 | float a = t * .5;
66 | float s = sin(a);
67 | float c = cos(a);
68 | uv *= mat2(c,s,-s,c);
69 | uv += .1*sin(uv.yx*6.+t);
70 |
71 | uv = abs(uv);
72 | uv*=mat2(c,s,-s,c);
73 |
74 | vec3 colors = 0.5 + 0.5*cos(t*1.4+uv.xyx*5.+vec3(0,2,4)) + .3*sin(uv.xxx*(1.1+.2*sin(t*.9))*20.+t*.4)+.3;
75 |
76 |
77 |
78 | vec3 finalColor = vec3(0.);
79 |
80 | if(uIndexTransition == 0. ){
81 | finalColor = imgTex;
82 | }else if(uIndexTransition == 1. ){
83 | finalColor = colors ;
84 | }else{
85 | finalColor = mix( imgTex, colors,(1.-vCoef)*uIndexTransition) + vPos.z / 20.;
86 | }
87 |
88 | if(uTransition>0.){
89 | vec3 imgNextTex = texture2D(uMapNext, uvImg * scale - vec2(.5, .5) * (scale - 1.)).rgb;
90 | finalColor = mix(imgTex,imgNextTex, (1. - vCoef) * uTransition) + vPos.z/20.;
91 | }
92 | if(uRippleEffect>0.){
93 | finalColor = imgTex + shading * uRippleEffect * .5;
94 | }
95 |
96 |
97 | gl_FragColor.rgb = finalColor;
98 | gl_FragColor.a = maskTex.r;
99 |
100 | }
101 | `
102 |
--------------------------------------------------------------------------------
/components/scene/shaders/plane.vs.js:
--------------------------------------------------------------------------------
1 | export default `
2 | precision highp float;
3 | precision highp int;
4 | attribute vec2 uv;
5 | attribute vec3 position;
6 | varying vec3 vPos;
7 | varying float vCoef;
8 | attribute vec3 normal;
9 | uniform mat4 modelViewMatrix;
10 | uniform mat4 projectionMatrix;
11 | uniform float uTransition;
12 |
13 | varying vec2 vUv;
14 | #define PI 3.1415926538
15 | void main() {
16 | vec3 transformed = vec3(position);
17 | float dx = position.x;
18 | float dy = position.y;
19 | float dist = sqrt(dx*dx + dy*dy);
20 | float amplitude = 5.;// * uRippleEffect;
21 | float angle = -uTransition * 3. + dist / 3.5;
22 |
23 | float addToCoef = 0.;
24 | transformed.z += sin(angle) * amplitude;
25 |
26 |
27 | transformed.z += vCoef * 7.;
28 |
29 |
30 | vUv = uv;
31 | vPos = transformed;
32 |
33 | gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
34 | }
35 | `
36 |
--------------------------------------------------------------------------------
/components/scene/shaders/planeCover.fs.js:
--------------------------------------------------------------------------------
1 | export default `
2 | precision highp float;
3 | precision highp int;
4 | varying vec3 vNormal;
5 | uniform float uAlpha;
6 | uniform sampler2D uMap;
7 | uniform vec2 uResolution;
8 | varying vec2 vUv;
9 | void main() {
10 | float screenRatio = uResolution.x / uResolution.y;
11 | float coverRatio = 454. / 562.;
12 | vec2 uvImg = vUv;
13 | if (screenRatio > coverRatio) {
14 | uvImg.y -= 0.5;
15 | uvImg.y /= screenRatio * ( 1. / coverRatio );
16 | uvImg.y += 0.5;
17 | } else {
18 | uvImg.x -= 0.5;
19 | uvImg.x *= screenRatio * ( 1. / coverRatio );
20 | uvImg.x += 0.5;
21 | }
22 | vec4 imgTex = texture2D(uMap, vUv ); // to debug img ratio
23 |
24 | gl_FragColor.rgb = imgTex.rgb;
25 | gl_FragColor.a = uAlpha*imgTex.a;
26 |
27 | }
28 | `
29 |
--------------------------------------------------------------------------------
/components/scene/shaders/planeCover.vs copy.js:
--------------------------------------------------------------------------------
1 | export default `
2 | precision highp float;
3 | precision highp int;
4 | attribute vec2 uv;
5 | attribute vec3 position;
6 | varying vec3 vPos;
7 | attribute vec3 normal;
8 | uniform mat4 modelViewMatrix;
9 | uniform mat4 projectionMatrix;
10 | uniform float uTransition;
11 | varying vec2 vUv;
12 | void main() {
13 |
14 | vec3 transformed = vec3(position);
15 | float dx = position.x ;
16 | float dy = position.y ;
17 | float dist = sqrt(dx*dx + dy*dy);
18 | float amplitude = uTransition * 5.;// * uRippleEffect;
19 | float angle = 8. + dist /3. ;
20 |
21 | float addToCoef = 0.;
22 | transformed.z += sin(-angle /2.+uTransition) * amplitude;
23 |
24 |
25 |
26 |
27 | vUv = uv;
28 | vPos = transformed;
29 |
30 | gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
31 | }
32 | `
33 |
--------------------------------------------------------------------------------
/components/scene/shaders/planeCover.vs.js:
--------------------------------------------------------------------------------
1 | export default `
2 | precision highp float;
3 | precision highp int;
4 | attribute vec2 uv;
5 | attribute vec3 position;
6 | varying vec3 vPos;
7 | attribute vec3 normal;
8 | uniform mat4 modelViewMatrix;
9 | uniform mat4 projectionMatrix;
10 | uniform float uTransition;
11 | uniform float uTime;
12 | varying vec2 vUv;
13 | void main() {
14 | vec3 transformed = vec3(position);
15 | float freq = .2;
16 | float amp = 1. + uTransition * 10.;
17 | float angle = (uTime *10.+ uv.x*20.- uv.y*10.)*freq;
18 | transformed.z += sin(angle)*amp;
19 |
20 |
21 | vUv = uv;
22 | vPos = transformed;
23 |
24 | gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
25 | }
26 | `
27 |
--------------------------------------------------------------------------------
/components/work/duo.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
90 |
124 |
--------------------------------------------------------------------------------
/components/work/header.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
18 |
81 |
120 |
--------------------------------------------------------------------------------
/components/work/img-text.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
88 |
123 |
--------------------------------------------------------------------------------
/components/work/single-large.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
89 |
100 |
--------------------------------------------------------------------------------
/components/work/single-medium.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
73 |
--------------------------------------------------------------------------------
/components/work/tags/tag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
35 |
46 |
--------------------------------------------------------------------------------
/components/work/top.vue:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
65 |
66 |
108 |
--------------------------------------------------------------------------------
/components/work/trio.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
95 |
106 |
--------------------------------------------------------------------------------
/components/works/work.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
60 |
61 |
89 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "~/*": ["./*"],
6 | "@/*": ["./*"],
7 | "~~/*": ["./*"],
8 | "@@/*": ["./*"]
9 | }
10 | },
11 | "exclude": ["node_modules", ".nuxt", "dist"]
12 | }
13 |
--------------------------------------------------------------------------------
/layouts/README.md:
--------------------------------------------------------------------------------
1 | # LAYOUTS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your Application Layouts.
6 |
7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).
8 |
--------------------------------------------------------------------------------
/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
194 |
195 |
223 |
--------------------------------------------------------------------------------
/middleware/README.md:
--------------------------------------------------------------------------------
1 | # MIDDLEWARE
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your application middleware.
6 | Middleware let you define custom functions that can be run before rendering either a page or a group of pages.
7 |
8 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).
9 |
--------------------------------------------------------------------------------
/nuxt.config.js:
--------------------------------------------------------------------------------
1 | import slugsQuery from './assets/graphql/slugs.js'
2 | import getQuery from './assets/js/utils/datas/getQuery.js'
3 | const getRoutes = async () => {
4 | return getQuery(slugsQuery, {}, 'slugsQuery').then(({ allWorks }) => {
5 | const routes = []
6 | allWorks.forEach((obj) => {
7 | routes.push('/work/' + obj.slug)
8 | })
9 | return routes
10 | })
11 | }
12 | export default {
13 | mode: 'universal',
14 | /*
15 | ** Headers of the page
16 | */
17 | head: {
18 | meta: [
19 | { charset: 'utf-8' },
20 | {
21 | name: 'viewport',
22 | content: 'width=device-width, initial-scale=1, viewport-fit=cover'
23 | }
24 | ],
25 | link: [
26 | {
27 | rel: 'preconnect',
28 | href: 'https://simon-daufresne.s3.eu-central-1.amazonaws.com'
29 | }
30 | ]
31 | },
32 | /*
33 | ** Customize the progress-bar color
34 | */
35 | loading: { color: '#fff' },
36 | /*
37 | ** Global CSS
38 | */
39 | css: [{ src: '~/assets/scss/main.scss', lang: 'scss' }],
40 | /*
41 | ** Plugins to load before mounting the App
42 | */
43 | plugins: [
44 | '~plugins/vuex-router-sync.js',
45 | '~plugins/plugins.client.js',
46 | '~plugins/pwa.client.js'
47 | ],
48 |
49 | /*
50 | ** Nuxt.js modules
51 | */
52 | modules: [
53 | // Doc: https://axios.nuxtjs.org/usage
54 | '@nuxtjs/axios',
55 | '@nuxtjs/pwa',
56 | '@nuxtjs/sitemap',
57 | '@nuxtjs/svg'
58 | ],
59 | pwa: {
60 | workbox: {
61 | clientsClaim: false,
62 | cachingExtensions: '@/plugins/workbox-range-request.js'
63 | }
64 | },
65 |
66 | buildModules: [
67 | // Doc: https://github.com/nuxt-community/eslint-module
68 | '@nuxtjs/eslint-module',
69 | '@nuxtjs/style-resources'
70 | ],
71 | /*
72 | ** Axios module configuration
73 | ** See https://axios.nuxtjs.org/options
74 | */
75 | axios: {},
76 | /*
77 | ** Build configuration
78 | */
79 | build: {
80 | /*
81 | ** You can extend webpack config here
82 | */
83 | extend(config) {
84 | config.module.rules.push({
85 | test: /\.(glsl|frag|vert|fs|vs)$/,
86 | loader: 'shader-loader',
87 | exclude: /(node_modules)/
88 | })
89 | }
90 | },
91 |
92 | router: {
93 | extendRoutes(routes, resolve) {
94 | routes.push(
95 | {
96 | name: 'custom404',
97 | path: '*',
98 | component: resolve(__dirname, 'pages/404.vue')
99 | },
100 | {
101 | name: 'work404',
102 | path: '/work/*',
103 | component: resolve(__dirname, 'pages/404.vue')
104 | }
105 | )
106 | }
107 | },
108 | generate: {
109 | concurrency: 1,
110 | routes: getRoutes
111 | },
112 | styleResources: {
113 | scss: ['~/assets/scss/includes.scss']
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SimonDaufresne",
3 | "version": "1.0.0",
4 | "description": "My epic new Simon Daufresne project <3",
5 | "author": "romainavalle",
6 | "private": true,
7 | "scripts": {
8 | "dev": "HOST=0.0.0.0 PORT=3000 nuxt",
9 | "build": "nuxt build",
10 | "start": "nuxt start",
11 | "generate": "nuxt generate",
12 | "devWithNewData": "cross-env isGenerating=development nuxt generate && npm run dev",
13 | "lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
14 | },
15 | "dependencies": {
16 | "dato-nuxt-lazysizes": "github:romainavalle/dato-nuxt-lazysizes",
17 | "dom-transform": "^2.1.0",
18 | "fs": "0.0.2",
19 | "graphql-request": "^1.8.2",
20 | "gsap": "^3.5.1",
21 | "normalize-wheel": "^1.0.1",
22 | "nuxt": "^2.12.2",
23 | "ogl": "0.0.43",
24 | "path": "^0.12.7"
25 | },
26 | "devDependencies": {
27 | "@nuxtjs/axios": "^5.3.6",
28 | "@nuxtjs/eslint-config": "^1.0.1",
29 | "@nuxtjs/eslint-module": "^1.0.0",
30 | "@nuxtjs/pwa": "^3.0.0-0",
31 | "@nuxtjs/sitemap": "^1.3.0",
32 | "@nuxtjs/style-resources": "^1.0.0",
33 | "@nuxtjs/svg": "^0.1.6",
34 | "babel-eslint": "^10.0.1",
35 | "cross-env": "^5.2.0",
36 | "eslint": "^6.1.0",
37 | "eslint-config-prettier": "^4.1.0",
38 | "eslint-plugin-nuxt": ">=0.4.2",
39 | "eslint-plugin-prettier": "^3.0.1",
40 | "node-fetch": "^2.6.1",
41 | "node-sass": "^4.14.1",
42 | "prettier": "^1.16.4",
43 | "sass-loader": "^7.2.0",
44 | "shader-loader": "^1.3.1",
45 | "vuex-router-sync": "^5.0.0"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/pages/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
55 |
56 |
81 |
--------------------------------------------------------------------------------
/pages/README.md:
--------------------------------------------------------------------------------
1 | # PAGES
2 |
3 | This directory contains your Application Views and Routes.
4 | The framework reads all the `*.vue` files inside this directory and creates the router of your application.
5 |
6 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
7 |
--------------------------------------------------------------------------------
/pages/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | About
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
103 |
104 |
143 |
--------------------------------------------------------------------------------
/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Simon Daufresne
5 |
6 |
20 |
21 |
22 |
23 |
24 |
110 |
111 |
162 |
--------------------------------------------------------------------------------
/pages/work/_slug.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
64 |
65 |
71 |
--------------------------------------------------------------------------------
/pages/work/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Projects
4 |
5 |
6 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
121 |
122 |
173 |
--------------------------------------------------------------------------------
/plugins/README.md:
--------------------------------------------------------------------------------
1 | # PLUGINS
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
6 |
7 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
8 |
--------------------------------------------------------------------------------
/plugins/plugins.client.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import gsap from 'gsap'
3 | import Image from 'dato-nuxt-lazysizes'
4 | Vue.use(Image, { auto: 'format, compress' })
5 | window.gsap = gsap
6 |
--------------------------------------------------------------------------------
/plugins/pwa.client.js:
--------------------------------------------------------------------------------
1 | export default ({ store }, inject) => {
2 | const isInWebAppiOS = window.navigator.standalone == true
3 | const isInWebAppChrome = window.matchMedia('(display-mode: standalone)')
4 | .matches
5 | const isStandAlone = new URLSearchParams(window.location.search).get(
6 | 'standalone'
7 | )
8 |
9 | const standalone = isInWebAppiOS || isInWebAppChrome || isStandAlone
10 |
11 | if ('serviceWorker' in navigator && standalone) {
12 | window.addEventListener('load', () => {
13 | navigator.serviceWorker.register('/sw.js')
14 | })
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/plugins/vuex-router-sync.js:
--------------------------------------------------------------------------------
1 | import { sync } from 'vuex-router-sync'
2 | export default ({ app: { store, router } }) => {
3 | sync(store, router)
4 | }
5 |
--------------------------------------------------------------------------------
/plugins/workbox-range-request.js:
--------------------------------------------------------------------------------
1 | workbox.routing.registerRoute(
2 | /.*\.(mp4|webm)/,
3 | workbox.strategies.cacheFirst({
4 | plugins: [new workbox.rangeRequests.Plugin()]
5 | }),
6 | 'GET'
7 | )
8 |
--------------------------------------------------------------------------------
/static/.htaccess:
--------------------------------------------------------------------------------
1 |
2 | ErrorDocument 404 /404.html
3 | AddType image/webp webp
4 | AddType application/font-woff woff
5 | AddType application/font-woff2 woff2
6 | AddType video/mp4 .mp4
7 | AddType video/webm .webm
8 |
9 | ExpiresActive On
10 | ExpiresByType image/webp "access 1 year"
11 | ExpiresByType image/jpg "access 1 year"
12 | ExpiresByType image/jpeg "access 1 year"
13 | ExpiresByType image/gif "access 1 year"
14 | ExpiresByType image/png "access 1 year"
15 | ExpiresByType text/css "access 1 month"
16 | ExpiresByType video/mp4 "access 1 year"
17 | ExpiresByType text/html "access 1 month"
18 | ExpiresByType application/pdf "access 1 month"
19 | ExpiresByType application/javascript "access plus 1 month"
20 | ExpiresByType application/x-javascript "access plus 1 month"
21 | ExpiresByType text/javascript "access plus 1 month"
22 | ExpiresByType text/x-javascript "access 1 month"
23 | ExpiresByType application/x-shockwave-flash "access 1 month"
24 | ExpiresByType image/x-icon "access 1 year"
25 | ExpiresByType application/font-woff "access plus 1 year"
26 | ExpiresByType application/font-woff2 "access plus 1 year"
27 | ExpiresDefault "access 1 month"
28 |
29 |
30 |
31 | Header set Cache-Control "max-age=25920000, public"
32 |
33 |
34 | Header set Cache-Control "max-age=25920000, private"
35 |
36 |
37 | Header set Cache-Control "max-age=7200000, public"
38 |
39 | # Disable caching for scripts and other dynamic files
40 |
41 | Header unset Cache-Control
42 |
43 |
44 |
45 | AddOutputFilterByType DEFLATE text/css
46 | AddOutputFilterByType DEFLATE text/text text/html text/plain text/xml
47 | AddOutputFilterByType DEFLATE application/json application/x-javascript application/javascript text/javascript
48 | AddOutputFilterByType DEFLATE application/vnd.ms-fontobject application/x-font-ttf font/opentype application/x-font-woff image/svg+xml
49 |
50 |
--------------------------------------------------------------------------------
/static/README.md:
--------------------------------------------------------------------------------
1 | # STATIC
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your static files.
6 | Each file inside this directory is mapped to `/`.
7 | Thus you'd want to delete this README.md before deploying to production.
8 |
9 | Example: `/static/robots.txt` is mapped as `/robots.txt`.
10 |
11 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
12 |
--------------------------------------------------------------------------------
/static/api/404/index.json:
--------------------------------------------------------------------------------
1 | {"errorPage":{"seo":{"title":"Simon Daufresne","description":"Hi! I am Simon Daufresne, an independent graphic designer based in Paris.","image":{"url":"https://www.datocms-assets.com/29350/1600685317-seo404.jpg"}},"image":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_404.png","height":"855","width":"670"}}}
--------------------------------------------------------------------------------
/static/api/about/index.json:
--------------------------------------------------------------------------------
1 | {"aboutPage":{"seo":{"title":"Simon Daufresne / About me","description":"Simon Daufresne - Independent graphic designer / About me","image":{"url":"https://www.datocms-assets.com/29350/1600685311-seosd-3.jpg"}},"title":"About Me","introduction":"I am an independent graphic designer based in Paris. \nI focus on digital design, interface design, experiments and brand activation ideas. I lead projects and design. \nI'm available for freelance.","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_SD.jpg","height":"855","width":"670"},"clients":[{"name":"Armani"},{"name":"Audi"},{"name":"The Botanist"},{"name":"YSL Beauty"},{"name":"Pernod Ricard"},{"name":"Icade"},{"name":"Timeto"},{"name":"Herezie Group"},{"name":"Bvlgari"},{"name":"Yves Rocher"},{"name":"La Grande Épicerie"},{"name":"Carte Noire"},{"name":"Ruinart"},{"name":"Le Bon Marché"},{"name":"Neuflize OBC"},{"name":"Quick"},{"name":"Toyota"},{"name":"Cartier "},{"name":"Meerson"}],"awards":[{"title":"Romain Avalle","year":"2019","award":"Awwwards — Site of the day"},{"title":"Romain Avalle","year":"2019","award":"FWA of the day"},{"title":"Herezie Group","year":"2018","award":"Awwwards — Mention Honorable"},{"title":"Herezie Group","year":"2018","award":"CSS Design Awards — Special Kudos"},{"title":"Dream Generator","year":"2015","award":"FWA of the day — APP"},{"title":"5emegauche","year":"2013","award":"Awwwards — Site of the day"},{"title":"5emegauche","year":"2011","award":"Awwwards — Site of the day"},{"title":"Eco-Emballages","year":"2010","award":"Grand Prix Brand Content — Or"}],"links":[{"label":"Instagram","url":"https://www.instagram.com/simon.daufresne"},{"label":"Mail","url":"mailto:hello@simondaufresne.com"}]}}
--------------------------------------------------------------------------------
/static/api/acqua-di-gio/index.json:
--------------------------------------------------------------------------------
1 | {"work":{"seo":{"title":"Simon Daufresne / Acqua Di Gio","description":"Website which presents the new fragance of Armani Beauty. Aqua di Giò Profondo is a captivating deep-dive into the profoundness of the soul, embracing the values of freedom, sensoriality and modern masculinity, revealing an ever more profound dimension.","image":{"url":"https://www.datocms-assets.com/29350/1600684190-seoacqua.jpg"}},"title":"Acqua Di Giò","introduction":"Website which presents the new fragance of Armani Beauty. Aqua di Giò Profondo is a captivating deep-dive into the profoundness of the soul, embracing the values of freedom, sensoriality and modern masculinity, revealing an ever more profound dimension.
","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Acqua.jpg","height":"855","width":"670"},"color":{"hex":"#003F66"},"tags":[{"label":"Art Direction"},{"label":"Web Design"}],"contents":[{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/S01.jpg","height":"1400","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/F01.jpg","height":"2160","width":"3840"}},{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/S02.jpg","height":"7400","width":"2500"}},{"__typename":"DuoRecord","addMargin":false,"mediaLeft":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/D02.jpg","height":"2160","width":"1920"},"mediaRight":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/D01.jpg","height":"2160","width":"1920"}},{"__typename":"TrioRecord","media1":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/T01.png","height":"1448","width":"694"},"media2":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/T02.png","height":"1448","width":"694"},"media3":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/T03.png","height":"1448","width":"694"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/acqua/F02.jpg","height":"2160","width":"3840"}}]}}
--------------------------------------------------------------------------------
/static/api/babylone/index.json:
--------------------------------------------------------------------------------
1 | {"work":{"seo":{"title":"Simon Daufresne / Babylone","description":"Babylone’s purpose is ultimately to delight fashion and luxury lovers with an audacious Parisian mindset & a daring ultra-selective range of products.","image":{"url":"https://www.datocms-assets.com/29350/1600684197-seobabylone.jpg"}},"title":"Babylone","introduction":"Babylone’s purpose is ultimately to delight fashion and luxury lovers with an audacious Parisian mindset & a daring ultra-selective range of products.
","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Babylone.jpg","height":"855","width":"670"},"color":{"hex":"#B21C1B"},"tags":[{"label":"Art Direction"},{"label":"Mobile APP"},{"label":"Interaction"},{"label":"Interface"}],"contents":[{"__typename":"TrioRecord","media1":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T01.png","height":"1448","width":"694"},"media2":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T02.png","height":"1448","width":"694"},"media3":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T03.png","height":"1448","width":"694"}},{"__typename":"ImgTextRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/D01.jpg","height":"7030","width":"1300"},"title":"Windows to Inspire you","text":"Windows to create a dialogue, offering different journeys, stories, moods, attitudes that always surprise. Windows as works of art,featuring products that reflect a distinct point of view. A not-to-be-missed weekly rendez-vous. Windows as basis of Babylone’s brand identity. The expression of its personality.","hasBackground":false},{"__typename":"TrioRecord","media1":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T04.png","height":"1448","width":"694"},"media2":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T05.png","height":"1448","width":"694"},"media3":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T06.png","height":"1448","width":"694"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/F01.jpg","height":"2160","width":"3840"}},{"__typename":"TrioRecord","media1":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T07.png","height":"1448","width":"694"},"media2":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T08.png","height":"1448","width":"694"},"media3":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/T09.png","height":"1448","width":"694"}},{"__typename":"DuoRecord","addMargin":false,"mediaLeft":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/D03.jpg","height":"7030","width":"1300"},"mediaRight":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/babylone/D02.jpg","height":"7030","width":"1300"}}]}}
--------------------------------------------------------------------------------
/static/api/common/index.json:
--------------------------------------------------------------------------------
1 | {"worksPage":{"works":[{"slug":"babylone","title":"Babylone","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Babylone.jpg","height":"855","width":"670"},"color":{"hex":"#B21C1B"}},{"slug":"acqua-di-gio","title":"Acqua Di Giò","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Acqua.jpg","height":"855","width":"670"},"color":{"hex":"#003F66"}},{"slug":"la-grande-epicerie","title":"La Grande Épicerie","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Lge.jpg","height":"855","width":"670"},"color":{"hex":"#CFCC93"}},{"slug":"ruinart","title":"Ruinart","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Ruinart.jpg","height":"855","width":"670"},"color":{"hex":"#92494F"}},{"slug":"herezie-group","title":"Herezie Group","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Herezie.jpg","height":"855","width":"670"},"color":{"hex":"#FF7400"}},{"slug":"romain-avalle","title":"Romain Avalle","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Ra.jpg","height":"855","width":"670"},"color":{"hex":"#000000"}}]}}
--------------------------------------------------------------------------------
/static/api/custom404/index.json:
--------------------------------------------------------------------------------
1 | {"errorPage":{"seo":{"title":"Simon Daufresne","description":"Hi! I am Simon Daufresne, an independent graphic designer based in Paris.","image":{"url":"https://www.datocms-assets.com/29350/1600685317-seo404.jpg"}},"image":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_404.png","height":"855","width":"670"}}}
--------------------------------------------------------------------------------
/static/api/herezie-group/index.json:
--------------------------------------------------------------------------------
1 | {"work":{"seo":{"title":"Simon Daufresne / Herezie Group","description":"Redesign of the group's website. Herezie is a Paris-based, multidisciplinary, international and slightly unorthodox communication agency.","image":{"url":"https://www.datocms-assets.com/29350/1600684194-seoherezie.jpg"}},"title":"Herezie Group","introduction":"Redesign of the group's website. Herezie is a Paris-based, multidisciplinary, international and slightly unorthodox communication agency.
","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Herezie.jpg","height":"855","width":"670"},"color":{"hex":"#FF7400"},"tags":[{"label":"Art Direction"},{"label":"Interaction"}],"contents":[{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/S01.jpg","height":"1400","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":false,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/V01.mp4","height":"720","width":"1280"}},{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/S02.jpg","height":"1400","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":false,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/V02.mp4","height":"720","width":"1280"}},{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/S03.jpg","height":"7564","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/F03.jpg","height":"2160","width":"3840"}},{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/herezie/V03.mp4","height":"720","width":"1280"}}]}}
--------------------------------------------------------------------------------
/static/api/index/index.json:
--------------------------------------------------------------------------------
1 | {"homePage":{"seo":{"title":"Simon Daufresne","description":"Hi! I am Simon Daufresne, an independent graphic designer based in Paris.","image":{"url":"https://www.datocms-assets.com/29350/1600685311-seosd-3.jpg"}},"title":"Simon Daufresne","introduction":"Hi! I am Simon Daufresne, an independent graphic designer based in Paris.","links":[{"label":"Instagram","url":"https://www.instagram.com/simon.daufresne"},{"label":"Mail","url":"mailto:hello@simondaufresne.com"}]}}
--------------------------------------------------------------------------------
/static/api/la-grande-epicerie/index.json:
--------------------------------------------------------------------------------
1 | {"work":{"seo":{"title":"Simon Daufresne / La Grande Épicerie","description":"Better content for a better experience. Exclusive recipes, interviews with Chefs and celebrities have been added to the site to better enjoy La Grande Epicerie. The website also offers the all time favourites of the Parisian brand.","image":{"url":"https://www.datocms-assets.com/29350/1600684204-seolge.jpg"}},"title":"La Grande Épicerie","introduction":"Better content for a better experience. Exclusive recipes, interviews with Chefs and celebrities have been added to the site to better enjoy La Grande Epicerie. The website also offers the all time favourites of the Parisian brand.
","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Lge.jpg","height":"855","width":"670"},"color":{"hex":"#CFCC93"},"tags":[{"label":"Art Direction"},{"label":"Web Design"}],"contents":[{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/S01.jpg","height":"1400","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/F01.jpg","height":"2160","width":"3840"}},{"__typename":"DuoRecord","addMargin":true,"mediaLeft":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/D01.jpg","height":"5932","width":"1300"},"mediaRight":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/D02.jpg","height":"6336","width":"1300"}},{"__typename":"DuoRecord","addMargin":false,"mediaLeft":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/D03.jpg","height":"2160","width":"1920"},"mediaRight":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/D04.jpg","height":"2160","width":"1920"}},{"__typename":"TrioRecord","media1":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/T01.png","height":"1448","width":"694"},"media2":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/T02.png","height":"1448","width":"694"},"media3":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/lge/T03.png","height":"1448","width":"694"}}]}}
--------------------------------------------------------------------------------
/static/api/romain-avalle/index.json:
--------------------------------------------------------------------------------
1 | {"work":{"seo":{"title":"Simon Daufresne / Romain Avalle","description":"Always a pleasure to team up with my good friend and talented developer Romain Avalle. Worked on his portfolio in 2019 and I’m happy to share a Awwward and FWA with him.","image":{"url":"https://www.datocms-assets.com/29350/1600684209-seora.jpg"}},"title":"Romain Avalle","introduction":"Always a pleasure to team up with my good friend and talented developer Romain Avalle. Worked on his portfolio in 2019 and I’m happy to share a Awwward and FWA with him.
","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Ra.jpg","height":"855","width":"670"},"color":{"hex":"#000000"},"tags":[{"label":"Art Direction"},{"label":"Interaction"},{"label":"Web Design"}],"contents":[{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/V01.mp4","height":"720","width":"1280"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/F01.jpg","height":"2160","width":"3840"}},{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/S02.jpg","height":"4348","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":false,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/V02.mp4","height":"720","width":"1280"}},{"__typename":"DuoRecord","addMargin":true,"mediaLeft":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/D02.jpg","height":"5766","width":"1300"},"mediaRight":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/D01.jpg","height":"7030","width":"1300"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/romain/F03.jpg","height":"2160","width":"3840"}}]}}
--------------------------------------------------------------------------------
/static/api/ruinart/index.json:
--------------------------------------------------------------------------------
1 | {"work":{"seo":{"title":"Simon Daufresne / Ruinart","description":"A digital showcase to reveal, through light, the soul of the Ruinart House. A sensory discovery, made of inspiring stories. A refined site with an emphasis on imagery that highlights the bottles and the French art of living. A play of light and shadow to enhance the contents and bring them to life.","image":{"url":"https://www.datocms-assets.com/29350/1600684200-seoruinart.jpg"}},"title":"Ruinart","introduction":"A digital showcase to reveal, through light, the soul of the Ruinart House. A sensory discovery, made of inspiring stories. A refined site with an emphasis on imagery that highlights the bottles and the French art of living. A play of light and shadow to enhance the contents and bring them to life.
","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Ruinart.jpg","height":"855","width":"670"},"color":{"hex":"#92494F"},"tags":[{"label":"Art Direction"},{"label":"Interaction"}],"contents":[{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/V01.mp4","height":"720","width":"1280"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/F01.jpg","height":"2160","width":"3840"}},{"__typename":"SingleMediumRecord","media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/S02.jpg","height":"4348","width":"2500"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/F02.jpg","height":"2160","width":"3840"}},{"__typename":"DuoRecord","addMargin":true,"mediaLeft":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/D01.jpg","height":"4694","width":"1300"},"mediaRight":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/D02.jpg","height":"4290","width":"1300"}},{"__typename":"SingleLargeRecord","isParallax":true,"media":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/ruinart/F03.jpg","height":"2160","width":"3840"}}]}}
--------------------------------------------------------------------------------
/static/api/work/index.json:
--------------------------------------------------------------------------------
1 | {"worksPage":{"seo":{"title":"Simon Daufresne / Projects","description":"Simon Daufresne / Projects","image":{"url":"https://www.datocms-assets.com/29350/1600685314-seosd.jpg"}},"works":[{"slug":"babylone","title":"Babylone","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Babylone.jpg","height":"855","width":"670"},"color":{"hex":"#B21C1B"}},{"slug":"acqua-di-gio","title":"Acqua Di Giò","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Acqua.jpg","height":"855","width":"670"},"color":{"hex":"#003F66"}},{"slug":"la-grande-epicerie","title":"La Grande Épicerie","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Lge.jpg","height":"855","width":"670"},"color":{"hex":"#CFCC93"}},{"slug":"ruinart","title":"Ruinart","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Ruinart.jpg","height":"855","width":"670"},"color":{"hex":"#92494F"}},{"slug":"herezie-group","title":"Herezie Group","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Herezie.jpg","height":"855","width":"670"},"color":{"hex":"#FF7400"}},{"slug":"romain-avalle","title":"Romain Avalle","hero":{"url":"https://simon-daufresne.s3.eu-central-1.amazonaws.com/cards/V_Ra.jpg","height":"855","width":"670"},"color":{"hex":"#000000"}}]}}
--------------------------------------------------------------------------------
/static/fonts/Orpheus_Pro.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/fonts/Orpheus_Pro.otf
--------------------------------------------------------------------------------
/static/fonts/Orpheus_Pro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/fonts/Orpheus_Pro.png
--------------------------------------------------------------------------------
/static/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/icon.png
--------------------------------------------------------------------------------
/static/images/mask.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/images/mask.jpg
--------------------------------------------------------------------------------
/static/images/mask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/images/mask.png
--------------------------------------------------------------------------------
/static/images/mask.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/images/mask.webp
--------------------------------------------------------------------------------
/static/images/mask_sml.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/images/mask_sml.jpg
--------------------------------------------------------------------------------
/static/images/mask_sml.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/romainavalle/simondaufresne/320a85b8616aa6f758b673d696aeabfa7c410c2d/static/images/mask_sml.webp
--------------------------------------------------------------------------------
/static/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
3 | Sitemap: https://www.your-project-url.com/sitemap.xml
4 |
--------------------------------------------------------------------------------
/store/README.md:
--------------------------------------------------------------------------------
1 | # STORE
2 |
3 | **This directory is not required, you can delete it if you don't want to use it.**
4 |
5 | This directory contains your Vuex Store files.
6 | Vuex Store option is implemented in the Nuxt.js framework.
7 |
8 | Creating a file in this directory automatically activates the option in the framework.
9 |
10 | More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).
11 |
--------------------------------------------------------------------------------
/store/index.js:
--------------------------------------------------------------------------------
1 | import getAsyncData from '~/assets/js/utils/datas/getAsyncData'
2 | import commonQuery from '~/assets/graphql/common.js'
3 | export const state = () => ({
4 | path: process.env.NODE_ENV === 'production' ? '/' : '/',
5 | isNavOpen: false,
6 | common: {},
7 | isFirstLoad: true,
8 | isDark: 0,
9 | reducedMotion: false,
10 | allWorks: []
11 | })
12 | export const mutations = {
13 | SET(state, obj) {
14 | state[obj.property] = obj.value
15 | },
16 | SWAP_COLOR(state) {
17 | state.isDark = !state.isDark
18 | }
19 | }
20 | export const actions = {
21 | nuxtServerInit({ context, commit }) {
22 | return getAsyncData(context, commonQuery, 'common').then((data) => {
23 | //commit('SET', { property: 'common', value: data.common })
24 | commit('SET', { property: 'allWorks', value: data.worksPage.works })
25 | })
26 | },
27 | setNavOpen({ commit }, bool) {
28 | commit('SET_NAV_OPEN', bool)
29 | },
30 | toggleNavOpen({ commit }) {
31 | commit('TOGGLE_NAV_OPEN')
32 | },
33 | set({ commit }, obj) {
34 | commit('SET', obj)
35 | },
36 | swapColor({ commit }) {
37 | commit('SWAP_COLOR')
38 | }
39 | }
40 | export const getters = {
41 | currentWork(state) {
42 | const slug = state.allWorks.find(
43 | (el) => el.slug === state.route.params.slug
44 | )
45 | return slug
46 | },
47 | currentIndex(state, getters) {
48 | return state.allWorks.indexOf(getters.currentWork)
49 | },
50 | nextWork: (state) => (slug) => {
51 | const index = state.allWorks.indexOf(
52 | state.allWorks.find((el) => el.slug === slug)
53 | )
54 |
55 | return index === state.allWorks.length - 1
56 | ? state.allWorks[0].slug
57 | : state.allWorks[index + 1].slug
58 | },
59 | isTouch() {
60 | try {
61 | document.createEvent('TouchEvent')
62 | return true
63 | } catch (e) {
64 | return false
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------