├── .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 | 12 | 63 | 105 | -------------------------------------------------------------------------------- /components/about/awards/awards.vue: -------------------------------------------------------------------------------- 1 | 15 | 54 | 68 | -------------------------------------------------------------------------------- /components/about/clients/client.vue: -------------------------------------------------------------------------------- 1 | 4 | 37 | 51 | -------------------------------------------------------------------------------- /components/about/clients/clients.vue: -------------------------------------------------------------------------------- 1 | 14 | 54 | 72 | -------------------------------------------------------------------------------- /components/about/footer.vue: -------------------------------------------------------------------------------- 1 | 22 | 92 | 133 | -------------------------------------------------------------------------------- /components/about/links/link.vue: -------------------------------------------------------------------------------- 1 | 13 | 33 | 46 | -------------------------------------------------------------------------------- /components/about/links/links.vue: -------------------------------------------------------------------------------- 1 | 12 | 36 | 45 | -------------------------------------------------------------------------------- /components/common/loader.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 63 | 64 | 92 | -------------------------------------------------------------------------------- /components/common/navigation.vue: -------------------------------------------------------------------------------- 1 | 71 | 213 | 355 | -------------------------------------------------------------------------------- /components/common/scroller.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 24 | 25 | 39 | -------------------------------------------------------------------------------- /components/common/title.vue: -------------------------------------------------------------------------------- 1 | 10 | 143 | 177 | -------------------------------------------------------------------------------- /components/common/transition.vue: -------------------------------------------------------------------------------- 1 | 4 | 109 | 127 | -------------------------------------------------------------------------------- /components/componentLoader.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 52 | 60 | -------------------------------------------------------------------------------- /components/fragments/image.vue: -------------------------------------------------------------------------------- 1 | 15 | 69 | -------------------------------------------------------------------------------- /components/fragments/media.vue: -------------------------------------------------------------------------------- 1 | 4 | 33 | -------------------------------------------------------------------------------- /components/fragments/video.vue: -------------------------------------------------------------------------------- 1 | 28 | 55 | 67 | -------------------------------------------------------------------------------- /components/next/counter.vue: -------------------------------------------------------------------------------- 1 | 16 | 73 | 104 | -------------------------------------------------------------------------------- /components/next/project-next.vue: -------------------------------------------------------------------------------- 1 | 22 | 156 | 187 | -------------------------------------------------------------------------------- /components/next/project.vue: -------------------------------------------------------------------------------- 1 | 19 | 74 | 122 | -------------------------------------------------------------------------------- /components/next/projects.vue: -------------------------------------------------------------------------------- 1 | 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 | 4 | 43 | 44 | 58 | -------------------------------------------------------------------------------- /components/scene/scene.vue: -------------------------------------------------------------------------------- 1 | 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 | 15 | 90 | 124 | -------------------------------------------------------------------------------- /components/work/header.vue: -------------------------------------------------------------------------------- 1 | 18 | 81 | 120 | -------------------------------------------------------------------------------- /components/work/img-text.vue: -------------------------------------------------------------------------------- 1 | 14 | 88 | 123 | -------------------------------------------------------------------------------- /components/work/single-large.vue: -------------------------------------------------------------------------------- 1 | 8 | 89 | 100 | -------------------------------------------------------------------------------- /components/work/single-medium.vue: -------------------------------------------------------------------------------- 1 | 6 | 73 | -------------------------------------------------------------------------------- /components/work/tags/tag.vue: -------------------------------------------------------------------------------- 1 | 6 | 35 | 46 | -------------------------------------------------------------------------------- /components/work/top.vue: -------------------------------------------------------------------------------- 1 | 13 | 65 | 66 | 108 | -------------------------------------------------------------------------------- /components/work/trio.vue: -------------------------------------------------------------------------------- 1 | 14 | 95 | 106 | -------------------------------------------------------------------------------- /components/works/work.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 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 | 21 | 103 | 104 | 143 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 110 | 111 | 162 | -------------------------------------------------------------------------------- /pages/work/_slug.vue: -------------------------------------------------------------------------------- 1 | 17 | 64 | 65 | 71 | -------------------------------------------------------------------------------- /pages/work/index.vue: -------------------------------------------------------------------------------- 1 | 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 | --------------------------------------------------------------------------------