Desktop. Hold down mouse1 button to see more
Created
May 31, 2021 09:12
-
-
Save vineeth-pappu/d55fc3e2ac1920504b4439af1bb9adfc to your computer and use it in GitHub Desktop.
Developer page concept (digital design)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <title>IT Event</title> | |
| <link href="https://fonts.googleapis.com/css?family=Montserrat:400,800,900" rel="stylesheet" type='text/css'> | |
| <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> | |
| <link rel="stylesheet" href="style.css"> | |
| <script src="./src/index.js"></script> | |
| </head> | |
| <body> | |
| <svg display="none"> | |
| <symbol version="1.1" id="codepen" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | |
| width="31.665px" height="31.665px" viewBox="0 0 31.665 31.665" style="enable-background:new 0 0 31.665 31.665;" | |
| xml:space="preserve"> | |
| <g> | |
| <path d="M16.878,0.415c-0.854-0.565-1.968-0.552-2.809,0.034L1.485,9.214c-0.671,0.468-1.071,1.233-1.071,2.052v9.444 | |
| c0,0.84,0.421,1.623,1.122,2.086l12.79,8.455c0.836,0.553,1.922,0.553,2.758,0l13.044-8.618c0.7-0.463,1.122-1.246,1.122-2.086 | |
| v-9.279c0-0.839-0.421-1.622-1.121-2.085L16.878,0.415z M26.621,10.645l-4.821,3.237l-4.521-3.288L17.25,4.127L26.621,10.645z | |
| M13.979,4.133v6.329l-4.633,3.24l-4.621-3.099L13.979,4.133z M3.458,13.722l2.991,2.004l-2.991,2.093V13.722z M14.058,27.215 | |
| l-9.331-6.258l4.661-3.258l4.67,3.133V27.215z M12.286,15.674l3.021-2.113l3.519,2.313l-3.119,2.095L12.286,15.674z M17.354,27.215 | |
| V20.83l4.463-2.991l4.805,3.159L17.354,27.215z M27.954,17.927l-3.168-2.082l3.168-2.125V17.927z"/> | |
| </g> | |
| </symbol> | |
| <symbol version="1.1" id="twitter" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | |
| viewBox="0 0 612 612" style="enable-background:new 0 0 612 612;" xml:space="preserve"> | |
| <g> | |
| <g> | |
| <path d="M612,116.258c-22.525,9.981-46.694,16.75-72.088,19.772c25.929-15.527,45.777-40.155,55.184-69.411 | |
| c-24.322,14.379-51.169,24.82-79.775,30.48c-22.907-24.437-55.49-39.658-91.63-39.658c-69.334,0-125.551,56.217-125.551,125.513 | |
| c0,9.828,1.109,19.427,3.251,28.606C197.065,206.32,104.556,156.337,42.641,80.386c-10.823,18.51-16.98,40.078-16.98,63.101 | |
| c0,43.559,22.181,81.993,55.835,104.479c-20.575-0.688-39.926-6.348-56.867-15.756v1.568c0,60.806,43.291,111.554,100.693,123.104 | |
| c-10.517,2.83-21.607,4.398-33.08,4.398c-8.107,0-15.947-0.803-23.634-2.333c15.985,49.907,62.336,86.199,117.253,87.194 | |
| c-42.947,33.654-97.099,53.655-155.916,53.655c-10.134,0-20.116-0.612-29.944-1.721c55.567,35.681,121.536,56.485,192.438,56.485 | |
| c230.948,0,357.188-191.291,357.188-357.188l-0.421-16.253C573.872,163.526,595.211,141.422,612,116.258z"/> | |
| </g> | |
| </g> | |
| </symbol> | |
| </svg> | |
| <main> | |
| <canvas class="plane-canvas" id="plane-canvas"></canvas> | |
| <canvas class="main-canvas" id="main-canvas"></canvas> | |
| <div class="mouse" id="mouse">Hold mouse1 button</div> | |
| <div class="plate"> | |
| <h2 class="text-animation" data-js="text">frontend developer</h2> | |
| <p class="text-animation" data-js="text">Russia, Saint-Petersburg</p> | |
| <div class="social" id="social"> | |
| <a target="_blank" class="social__twitter" href="https://twitter.com/fajjet"> | |
| <svg viewBox="0 0 32 32"> | |
| <use xlink:href="#twitter"></use> | |
| </svg> | |
| </a> | |
| <a target="_blank" class="social__codepen" href="https://codepen.io/fajjet"> | |
| <svg viewBox="0 0 32 32"> | |
| <use xlink:href="#codepen"></use> | |
| </svg> | |
| </a> | |
| </div> | |
| </div> | |
| </main> | |
| </body> | |
| </html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const tools = { | |
| drawPath(ctx, fn) { | |
| ctx.save() | |
| ctx.beginPath() | |
| fn() | |
| ctx.closePath() | |
| ctx.restore() | |
| }, | |
| random(min, max, int) { | |
| let result = (min + Math.random() * ((max + (int ? 1 : 0)) - min)) | |
| return int ? parseInt(result) : result | |
| }, | |
| getVectorLength(p1, p2){ | |
| return Math.sqrt(Math.pow(p1[0]-p2[0], 2) + Math.pow(p1[1]-p2[1], 2)) | |
| }, | |
| easing(t, b, c, d, s) { | |
| return c*((t=t/d-1)*t*t + 1) + b | |
| }, | |
| cellEasing(t, b, c, d, s) { | |
| return c*(t/=d)*t*t*t + b; | |
| } | |
| } | |
| const doc = { | |
| height: 0, | |
| width: 0 | |
| } | |
| const plane = { | |
| xCell: 0, | |
| yCell: 0, | |
| cells: [] | |
| } | |
| const context = { | |
| plane: null, | |
| main: null | |
| } | |
| const mouse = { | |
| x: 0, | |
| y: 0, | |
| coords: { | |
| x: 0, | |
| y: 0 | |
| }, | |
| down: { | |
| state: false, | |
| x: 0, | |
| y: 0 | |
| } | |
| } | |
| const cfg = { | |
| cell: 35, | |
| sectionWidth: 8, | |
| sectionHeight: 1, | |
| numberOffset: 5, | |
| shadowBlur: true, | |
| bgColor: '#181818' | |
| } | |
| const ui = { | |
| plane: '#plane-canvas', | |
| main: '#main-canvas', | |
| textNodes: '[data-js=text]', | |
| social: '#social', | |
| mouse: '#mouse' | |
| } | |
| class App{ | |
| constructor() { | |
| this.state = { | |
| area: 0, | |
| time: Date.now(), | |
| lt: 0, | |
| planeProgress: 0, | |
| dotsProgress: 0, | |
| fadeInProgress: 0, | |
| textProgress: 0, | |
| stepOffset: 0, | |
| textOffset: 0, | |
| markupOffset: 0, | |
| glitches: [], | |
| animLines: [], | |
| animNumbers: [], | |
| tabIsActive: true, | |
| planeIsDrawn: false, | |
| mousePower: 0, | |
| textPixelData: [], | |
| text: {}, | |
| delta: 0, | |
| dlt: performance.now(), | |
| needRedraw: true | |
| } | |
| this.bindNodes() | |
| this.getDimensions() | |
| mouse.x = doc.width / 2 | |
| mouse.y = doc.height / 2 | |
| this.start() | |
| }; | |
| start(){ | |
| this.initEvents() | |
| this.canvasInit() | |
| this.loop() | |
| this.initCheckingInterval() | |
| this.splitText() | |
| } | |
| splitText(){ | |
| ui.textNodes.forEach(el => { | |
| const value = el.innerText | |
| el.innerHTML = value.split('').reduce((acc, cur) => { | |
| return acc + `<span class="letter">${ cur }</span>` | |
| }, '') | |
| }) | |
| } | |
| animateText(){ | |
| const callback = () => { | |
| ui.social.classList.add('active') | |
| ui.mouse.classList.add('active') | |
| } | |
| ui.textNodes.forEach((el, elIndex) => { | |
| el.classList.add('active') | |
| const letters = el.querySelectorAll('.letter') | |
| const length = Math.round(letters.length / 2) + 1 | |
| for(let i = 0; i < length; i++) { | |
| const [letter1, letter2] = [letters[i], letters[letters.length - i]] | |
| setTimeout(() => { | |
| if (letter1) letter1.classList.add('active') | |
| if (letter2) letter2.classList.add('active') | |
| if (i === length - 1 && elIndex === ui.textNodes.length - 1) callback() | |
| }, i * 100) | |
| } | |
| }) | |
| } | |
| getDimensions() { | |
| doc.height = document.documentElement.clientHeight | |
| doc.width = document.documentElement.clientWidth | |
| } | |
| updatePlane(){ | |
| const { width: w, height: h } = doc | |
| const cell = Math.round(w / cfg.cell) | |
| const xPreSize = w / cell | |
| plane.xCell = (w / xPreSize) % 2 !== 0 ? w / ((w / xPreSize) + 1) : xPreSize | |
| const yPreSize = h / Math.round(cell * (h / w)) | |
| plane.yCell = (h / yPreSize) % 2 !== 0 ? h / ((h / yPreSize) + 1) : yPreSize | |
| plane.cells = [Math.round(w / plane.xCell), Math.round(h / plane.yCell)] | |
| plane.xCenter = Math.round((plane.cells[1]) / 2) | |
| plane.yCenter = Math.round((plane.cells[0]) / 2) | |
| plane.centerCoords = [plane.yCenter * plane.xCell, plane.xCenter * plane.yCell] | |
| } | |
| bindNodes() { | |
| for (const selector in ui) { | |
| ui[selector] = document.querySelectorAll(ui[selector]) | |
| if(ui[selector].length === 1) ui[selector] = ui[selector][0] | |
| } | |
| } | |
| canvasInit(){ | |
| const font = '10px Montserrat' | |
| const lineCapAndJoin = 'round' | |
| const color = `rgba(255,255,255,0.1)` | |
| context.plane = ui.plane.getContext('2d') | |
| context.plane.lineCap = lineCapAndJoin | |
| context.plane.lineJoin = lineCapAndJoin | |
| context.plane.font = font | |
| context.plane.fillStyle = color | |
| context.plane.strokeStyle = color | |
| context.main = ui.main.getContext('2d') | |
| context.main.lineCap = lineCapAndJoin | |
| context.main.lineJoin = lineCapAndJoin | |
| context.main.font = font | |
| context.main.fillStyle = color | |
| context.main.strokeStyle = color | |
| this.getTextPixels() | |
| } | |
| initEvents() { | |
| window.addEventListener('resize', (e) => { | |
| this.getDimensions() | |
| this.resizeHandler(e) | |
| }) | |
| document.addEventListener('mousemove', (e) => { | |
| mouse.x = e.clientX | |
| mouse.y = e.clientY | |
| mouse.coords = { | |
| x: ((mouse.x / doc.width) - 0.5) / 0.5, | |
| y: (((mouse.y / doc.height) - 0.5) / 0.5) * -1 | |
| } | |
| }) | |
| document.addEventListener('mousedown', (e) => { | |
| mouse.down = { | |
| state: true, | |
| x: e.clientX, | |
| y: e.clientY | |
| } | |
| }) | |
| document.addEventListener('mouseup', (e) => { | |
| mouse.down.state = false | |
| }) | |
| document.addEventListener('contextmenu', (e) => { | |
| e.preventDefault() | |
| }) | |
| this.resizeHandler() | |
| } | |
| resizeHandler(e){ | |
| const state = this.state | |
| state.area = (doc.width * doc.height) / 1000000 | |
| ui.main.height = doc.height | |
| ui.main.width = doc.width | |
| ui.plane.height = doc.height | |
| ui.plane.width = doc.width | |
| this.updatePlane() | |
| this.updateTextConfig() | |
| if (state.planeIsDrawn) this.getTextPixels() | |
| state.needRedraw = true | |
| } | |
| updateTextConfig(){ | |
| const state = this.state | |
| state.text = { | |
| baseLine: 'top', | |
| font: '800 170px Montserrat', | |
| value: 'FAJJET' | |
| } | |
| } | |
| initCheckingInterval() { | |
| const state = this.state | |
| setInterval(() => { | |
| state.tabIsActive = state.time <= state.lt ? false : true | |
| state.lt = state.time | |
| state.needRedraw = !state.tabIsActive | |
| }, 100) | |
| } | |
| loop(){ | |
| const loop = () => { | |
| const ctx = context.main | |
| const state = this.state | |
| state.time = Date.now() | |
| ctx.clearRect(0, 0, doc.width, doc.height) | |
| this.updateState() | |
| this.draw() | |
| if (state.needRedraw) state.needRedraw = false | |
| this.raf = requestAnimationFrame(loop) | |
| } | |
| loop() | |
| } | |
| updateState(){ | |
| const state = this.state | |
| const now = performance.now() | |
| state.delta = now - state.dlt | |
| state.dlt = now | |
| const dt = state.delta | |
| if (mouse.down.state) { | |
| state.mousePower += + 0.001 * dt | |
| if (state.mousePower >= 1) { | |
| state.mousePower = 1 | |
| ui.mouse.classList.remove('active') | |
| } | |
| } else { | |
| state.mousePower -= 0.001 * dt | |
| if (state.mousePower <= 0) state.mousePower = 0 | |
| } | |
| const mp = tools.cellEasing(state.mousePower, 0, 1, 1) | |
| if (state.planeProgress >= 0.2) { | |
| state.dotsProgress += 0.00035 * dt | |
| if (state.dotsProgress >= 1) state.dotsProgress = 1 | |
| } | |
| state.planeProgress += 0.00035 * dt | |
| if(state.planeProgress >= 1) state.planeProgress = 1 | |
| if (state.planeIsDrawn) { | |
| state.fadeInProgress += 0.00015 * dt | |
| if (state.fadeInProgress >= 1) state.fadeInProgress = 1 | |
| state.stepOffset += (0.002 * dt) + (mp * (0.0035 * dt)) | |
| state.textOffset += (0.00005 * dt) + (mp * (0.002 * dt)) | |
| state.markupOffset += (0.00015 * dt) + (mp * (0.00035 * dt)) | |
| state.textProgress += 0.0005 * dt | |
| if (state.textProgress >= 1) state.textProgress = 1 | |
| } | |
| } | |
| getTextPixels(){ | |
| const ctx = context.main | |
| const state = this.state | |
| const { xCell, yCell } = plane | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = 'white' | |
| ctx.textBaseline = state.text.baseLine | |
| ctx.font = state.text.font | |
| const text = state.text.value | |
| const h = parseInt(ctx.font) | |
| const w = ctx.measureText(text).width | |
| const x = (doc.width / 2) - (w/2) | |
| const y = yCell * 1.75 | |
| ctx.fillText(text, x, y) | |
| }) | |
| const imageData = ctx.getImageData(0, 0, doc.width, doc.height).data | |
| state.textPixelData = [] | |
| const offset = 10 | |
| for(let h = 0; h < doc.height; h += offset){ | |
| for(let w = 0; w < doc.width; w += offset){ | |
| const pixel = imageData[((w + (h * doc.width)) * 4) - 1]; | |
| if(pixel == 255) state.textPixelData.push({ | |
| x: w, | |
| y: h, | |
| value: tools.random(0, 1, true) | |
| }) | |
| } | |
| } | |
| ctx.clearRect(0, 0, doc.width, doc.height) | |
| } | |
| drawText(){ | |
| const { yCell } = plane | |
| const ctx = context.main | |
| const state = this.state | |
| const p = state.textOffset | |
| const mp = tools.cellEasing(state.mousePower, 0, 1, 1) | |
| const length = state.textPixelData.length | |
| tools.drawPath(ctx, () => { | |
| if (cfg.shadowBlur) { | |
| ctx.shadowColor = 'rgba(255,255,255,0.025)' | |
| ctx.shadowBlur = 30 * state.mousePower | |
| } | |
| ctx.globalAlpha = state.fadeInProgress * 0.95 | |
| ctx.textBaseline = state.text.baseLine | |
| ctx.fillStyle = cfg.bgColor | |
| ctx.font = state.text.font | |
| const text = state.text.value | |
| const x = (doc.width / 2) - (ctx.measureText(text).width/2) | |
| const y = yCell * 1.75 | |
| ctx.fillText(text, x, y) | |
| }) | |
| for(let i = 0; i < state.textPixelData.length; i++){ | |
| const pixel = state.textPixelData[i] | |
| const {x, y, value} = pixel | |
| const x2 = ((3 + (mp * 50)) * Math.sin(p*i)) | |
| const y2 = ((10 + (mp * 50)) * Math.cos(p*i)) | |
| const per = (1-mp) * (i / length) | |
| tools.drawPath(ctx, () => { | |
| if (!per) return | |
| ctx.globalAlpha = state.fadeInProgress | |
| ctx.font = '8px Montserrat' | |
| ctx.fillStyle = `rgba(255,255,255,${ per * 0.3 })` | |
| if (i % 2 === 0) ctx.fillText(value+'', x, y + (y2 * -1)) | |
| ctx.fillStyle = `rgba(255,255,255,${ per })` | |
| ctx.fillRect(x + x2, y, 5 * per * (1-mp), 1) | |
| ctx.fillRect(x, y + y2, 1, 5 * per * (1-mp)) | |
| }) | |
| } | |
| } | |
| draw(){ | |
| const ctx = context.main | |
| const state = this.state | |
| const { | |
| xCell, | |
| yCell, | |
| xCenter, | |
| yCenter, | |
| cells | |
| } = plane | |
| const cp = state.planeProgress | |
| if (this.state.planeProgress >= 1 && !state.planeIsDrawn) { | |
| state.planeIsDrawn = true | |
| this.startGeneratingGlitches() | |
| this.startGeneratingLines() | |
| this.startGeneratingNumbers() | |
| this.getTextPixels() | |
| this.animateText() | |
| } | |
| if (!state.planeIsDrawn || state.dotsProgress < 1 || state.planeIsDrawn && state.needRedraw) { | |
| this.drawPlane() | |
| } | |
| for (let i = 0; i < cells[0]; i++) { | |
| for (let i2 = 0; i2 < cells[1]; i2++) { | |
| const x = i * xCell | |
| const y = i2 * yCell | |
| if (state.planeIsDrawn) { | |
| this.drawMouseMoveInteraction({i, i2, x, y}) | |
| if (i2 === xCenter && i !== yCenter) { | |
| this.drawMarkupYAnimation({i, i2, x, y, cp}) | |
| } | |
| if (i2 !== xCenter && i === yCenter) { | |
| this.drawMarkupXAnimation({i, i2, x, y, cp}) | |
| } | |
| } | |
| } | |
| } | |
| if (state.planeIsDrawn) { | |
| this.drawGlitches() | |
| this.drawAnimLines() | |
| this.drawNumbersAnimation() | |
| this.drawText() | |
| } | |
| } | |
| startGeneratingNumbers(){ | |
| const state = this.state | |
| function generateItem(){ | |
| const { cells, xCell, yCell } = plane | |
| const mp = state.mousePower | |
| const timeToNewItem = tools.random( | |
| (1 + 50 * (1-mp)), | |
| (5 + 100 * (1-mp)) | |
| ) / state.area | |
| const item = { | |
| p: 0, | |
| color: `rgba(255,255,255,${ tools.random(0.01, 0.3) })`, | |
| blinks: Array(tools.random(0, 3, true)).fill(null).map(item => { | |
| return { | |
| at: tools.random(0, 1), | |
| dur: tools.random(0, 0.3) | |
| } | |
| }), | |
| pf: tools.random(0.00075, 0.01), | |
| x: tools.random(0, cells[0], true) * xCell, | |
| y: tools.random(0, cells[1], true) * yCell, | |
| value: tools.random(0, 1, true) | |
| } | |
| if(state.tabIsActive) state.animNumbers.push(item) | |
| setTimeout(generateItem, timeToNewItem) | |
| } | |
| generateItem() | |
| } | |
| drawNumbersAnimation(){ | |
| const ctx = context.main | |
| const state = this.state | |
| const { | |
| yCell, | |
| xCell | |
| } = plane | |
| state.animNumbers.forEach((item, i) => { | |
| item.p += item.pf * state.delta | |
| let show = true | |
| item.blinks.forEach(blink => { | |
| if (item.p >= blink.at && item.p <= blink.at + blink.dur) show = false | |
| }) | |
| if (!show) return | |
| tools.drawPath(ctx, () => { | |
| if (cfg.shadowBlur) { | |
| ctx.shadowColor = 'white' | |
| ctx.shadowBlur = 10 | |
| } | |
| ctx.globalAlpha = state.fadeInProgress | |
| ctx.textBaseline = 'top' | |
| ctx.font = '18px Montserrat' | |
| const th = parseInt(ctx.font) || 18 | |
| const tw = ctx.measureText(item.value+'').width | |
| ctx.fillStyle = item.color | |
| ctx.fillText(item.value+'', item.x + (xCell/2) - tw/2, item.y + (yCell/2) - th/2) | |
| }) | |
| if (item.p >= 1) state.animNumbers.splice(i, 1) | |
| }) | |
| } | |
| startGeneratingLines(){ | |
| const state = this.state | |
| function generateItem(){ | |
| const { cells, xCell, yCell} = plane | |
| const mp = state.mousePower | |
| const timeToNewItem = tools.random( | |
| 25 + 80 * (1-mp), | |
| 75 + 1200 * (1-mp) | |
| ) / state.area | |
| const item = { | |
| p: 0, | |
| color: tools.random(0, 0.15), | |
| pf: tools.random(0.0005, 0.00125), | |
| x: tools.random(0, cells[0], true) * xCell, | |
| y: tools.random(0, cells[1], true) * yCell | |
| } | |
| item.coord = tools.random(0, 1, true) ? 'x' : 'y' | |
| item.length = tools.random(xCell * 2, (state.area * xCell) * 5) | |
| item.dir = tools.random(0, 1, true) ? 1 : -1 | |
| item.distance = item.length * tools.random(1, 2) | |
| if(state.tabIsActive) state.animLines.push(item) | |
| setTimeout(generateItem, timeToNewItem) | |
| } | |
| generateItem() | |
| } | |
| drawAnimLines(){ | |
| const ctx = context.main | |
| const state = this.state | |
| state.animLines.forEach((line, i) => { | |
| line.p += line.pf * state.delta | |
| const p = tools.easing(line.p, 0, 1, 1) | |
| const p1 = (p / 0.5) | |
| const p2 = 1-((p-0.5)/0.5) | |
| const color = `rgba(255,255,255,${ 0.1 + line.color * (p <= 0.5 ? p1 : p2) })` | |
| const length = (p <= 0.5 ? line.length * p1 : line.length * p2) | |
| const backwards = line.dir === -1 | |
| const isX = line.coord === 'x' | |
| const isY = line.coord === 'y' | |
| const x = (!isX ? 0 : backwards ? -(length - line.distance * p) : -line.distance * p) | |
| const y = (!isY ? 0 : backwards ? -(length - line.distance * p) : -line.distance * p) | |
| tools.drawPath(ctx, () => { | |
| ctx.globalAlpha = state.fadeInProgress | |
| ctx.fillStyle = color | |
| ctx.fillRect( | |
| line.x + x + (isX && p <= 0.5 ? (line.length - length) * line.dir : 0), | |
| line.y + y + (isY && p <= 0.5 ? (line.length - length) * line.dir : 0), | |
| isX ? length : 1, | |
| isY ? length : 1 | |
| ) | |
| }) | |
| if (line.p >= 1) state.animLines.splice(i, 1) | |
| }) | |
| } | |
| startGeneratingGlitches(){ | |
| const state = this.state | |
| function generateItem(){ | |
| const { cells, xCell, yCell } = plane | |
| const mp = state.mousePower | |
| const timeToNewItem = tools.random( | |
| (5 + 100 * (1-mp)) / state.area, | |
| (25 + 1200 * (1-mp)) / state.area | |
| ) | |
| const item = { | |
| p: 0, | |
| color: `rgba(255,255,255,${ tools.random(0.01, 1) })`, | |
| blinks: Array(tools.random(0, 3, true)).fill(null).map(blink => { | |
| return { | |
| at: tools.random(0, 1), | |
| dur: tools.random(0, 0.3) | |
| } | |
| }), | |
| pf: tools.random(0.0015, 0.0035), | |
| x: tools.random(0, cells[0], true) * xCell, | |
| y: tools.random(0, cells[1], true) * yCell, | |
| width: xCell, | |
| height: yCell | |
| } | |
| if(state.tabIsActive) state.glitches.push(item) | |
| setTimeout(generateItem, timeToNewItem) | |
| } | |
| generateItem() | |
| } | |
| drawGlitches(){ | |
| const ctx = context.main | |
| const state = this.state | |
| state.glitches.forEach((glitch, i) => { | |
| glitch.p += glitch.pf * state.delta | |
| let show = true | |
| glitch.blinks.forEach(blink => { | |
| if (glitch.p >= blink.at && glitch.p <= blink.at + blink.dur) show = false | |
| }) | |
| if (!show) return | |
| tools.drawPath(ctx, () => { | |
| if (cfg.shadowBlur) { | |
| ctx.shadowColor = 'white' | |
| ctx.shadowBlur = 30 | |
| } | |
| ctx.globalAlpha = state.fadeInProgress | |
| ctx.fillStyle = glitch.color | |
| ctx.fillRect(glitch.x, glitch.y, glitch.width, glitch.height) | |
| }) | |
| if (glitch.p >= 1) state.glitches.splice(i, 1) | |
| }) | |
| } | |
| drawMouseMoveInteraction(props) { | |
| const ctx = context.main | |
| const state = this.state | |
| const fp = state.fadeInProgress | |
| const sp = state.stepOffset | |
| const mp = state.mousePower | |
| const { | |
| xCenter, | |
| yCenter | |
| } = plane | |
| const { i, i2, x, y } = props | |
| const position = [Math.abs(i2 - xCenter), Math.abs(i - yCenter)] | |
| const mouseRange = (200 + 50 * mp) * ((i * i2) % 2) * (Math.sin((position[0] - position[1]))) | |
| if (mouseRange <= 100) return | |
| const vector = tools.getVectorLength([x, y], [mouse.x, mouse.y]) | |
| if (vector <= mouseRange) { | |
| const percent = (1 - (vector / mouseRange)) * fp | |
| const spinRadius = 50 * (1-percent) | |
| const xOffset = ((Math.sin(sp + (i)) * spinRadius) * ((Math.PI*2) / 4)) * (((i+i2)%4) == 0 ? -1 : 1) | |
| const yOffset = ((Math.cos(sp + (i2)) * spinRadius) * ((Math.PI*2) / 4)) | |
| const sx = x + xOffset | |
| const sy = y + yOffset | |
| const radius = 25 * (1-percent) | |
| const lineWidth = 3 + 10 * percent | |
| const vector2 = tools.getVectorLength([sx, sy], [mouse.x, mouse.y]) | |
| const p2 = (1 - (vector2 / (mouseRange + spinRadius * 2))) | |
| const color = `rgba(255,255,255,${ 0.3 * percent })` | |
| const color2 = `rgba(255,255,255,${ 0.7 * p2 * percent })` | |
| tools.drawPath(ctx, () => { | |
| ctx.strokeStyle = color2 | |
| ctx.moveTo(sx, sy) | |
| ctx.lineTo(mouse.x, mouse.y) | |
| ctx.stroke() | |
| }) | |
| tools.drawPath(ctx, () => { | |
| ctx.strokeStyle = color2 | |
| ctx.moveTo(x, y) | |
| ctx.lineTo(sx, sy) | |
| ctx.stroke() | |
| }) | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = color | |
| ctx.arc(x, y, 1, 0, 2 * Math.PI) | |
| ctx.fill() | |
| }) | |
| tools.drawPath(ctx, () => { | |
| ctx.strokeStyle = `rgba(255,255,255,${ 0.5 * percent })` | |
| ctx.lineWidth = 1 + (2*(1-percent)) | |
| ctx.arc(x, y, 3 + 10 * (1-percent), 0, 2 * Math.PI) | |
| ctx.stroke() | |
| }) | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ percent })` | |
| ctx.arc(sx, sy, 1, 0, 2 * Math.PI) | |
| ctx.fill() | |
| }) | |
| tools.drawPath(ctx, () => { | |
| if (cfg.shadowBlur) { | |
| ctx.shadowColor = 'white' | |
| ctx.shadowBlur = radius | |
| } | |
| ctx.lineWidth = lineWidth | |
| ctx.strokeStyle = `rgba(255,255,255,${ 0.75 * percent })` | |
| ctx.arc(sx, sy, radius, 0, 2 * Math.PI) | |
| ctx.stroke() | |
| }) | |
| } | |
| } | |
| drawPlaneDotsAnimation(props){ | |
| const ctx = context.plane | |
| const { dp, i, i2, x, y } = props | |
| const { | |
| xCenter, | |
| yCenter | |
| } = plane | |
| const position = [Math.abs(i2 - xCenter), Math.abs(i - yCenter)] | |
| const index = position[0] * position[1] | |
| const maxIndex = xCenter * yCenter | |
| const percent = 1 / maxIndex | |
| const point = percent * index | |
| let f = dp * (dp / point) | |
| if (f >= 1) f = 1 | |
| const mf = f >= 0.5 ? (1 - f) / 0.5 : f / 0.5 | |
| const size = 3 | |
| if (!mf) return | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ mf * 0.15 })` | |
| ctx.fillRect(x - 1, y - 1, size, size) | |
| }) | |
| } | |
| drawPlaneCenterLines(props){ | |
| const { p } = props | |
| const ctx = context.plane | |
| const { | |
| centerCoords | |
| } = plane | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ 0.2 + ((1-p)*1) })` | |
| ctx.fillRect(centerCoords[0], 0 + ((doc.height / 2) * (1-p)), 1, doc.height * p) | |
| ctx.fillRect(0 + ((doc.width / 2) * (1-p)), centerCoords[1], doc.width * p, 1) | |
| }) | |
| } | |
| drawYLines(props){ | |
| const { i, cp, p, x } = props | |
| const ctx = context.plane | |
| const { | |
| yCenter | |
| } = plane | |
| const percent = 1 / yCenter | |
| const pos = Math.abs(i - yCenter) | |
| const point = percent * pos | |
| let f = cp * (cp / point) | |
| if (f >= 1) f = 1 | |
| const ef = tools.cellEasing(f, 0, 1, 1) | |
| if (i) { | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ 0.05 + ((1-p)*0.35) })` | |
| ctx.fillRect(x, 0 + ((doc.height / 2) * (1-ef)), 1, doc.height * ef) | |
| }) | |
| } | |
| } | |
| drawYMarkup(props) { | |
| const ctx = context.plane | |
| const state = this.state | |
| let { i, p, cp, x, y } = props | |
| const { | |
| yCenter | |
| } = plane | |
| const percent = 1 / yCenter | |
| const pos = Math.abs(i - yCenter) | |
| const point = percent * pos | |
| const conds = [p >= point, p <= point + percent] | |
| let f = cp * (cp / point) | |
| if (f >= 1) f = 1 | |
| const f2 = conds[0] && conds[1] ? (p - point) / percent : conds[0] ? 1 : 0 | |
| const text = (i - yCenter) + '' | |
| ctx.fillStyle = `rgba(255,255,255,${ 0.1 + ((1-f)*0.75) })` | |
| const textCoords = [x - (ctx.measureText(text).width / 2), y + (cfg.sectionWidth / 2) + cfg.numberOffset] | |
| tools.drawPath(ctx, () => { | |
| const o = ((1-f2) * 50) | |
| ctx.globalAlpha = f2 | |
| ctx.fillRect(x, y - cfg.sectionWidth / 2 + (o), cfg.sectionHeight, cfg.sectionWidth) | |
| }) | |
| tools.drawPath(ctx, () => { | |
| ctx.globalAlpha = f2 | |
| ctx.textBaseline = 'top' | |
| ctx.fillText( | |
| text, | |
| textCoords[0], | |
| textCoords[1] + ((1-f2) * (-20)) | |
| ) | |
| }) | |
| } | |
| drawXLines(props) { | |
| const ctx = context.plane | |
| const { i2, cp, p, y } = props | |
| const { | |
| xCenter | |
| } = plane | |
| const percent = 1 / (xCenter) | |
| const pos = (Math.abs(i2 - xCenter)) | |
| const point = percent * pos | |
| let f = cp * (cp / point) | |
| if (f >= 1) f = 1 | |
| const ef = tools.cellEasing(f, 0, 1, 1) | |
| if (i2) { | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ 0.05 + ((1-p)*0.35) })` | |
| ctx.fillRect(0 + ((doc.width / 2) * (1-ef)), y, doc.width * ef, 1) | |
| }) | |
| } | |
| } | |
| drawXMarkup(props){ | |
| const ctx = context.plane | |
| const state = this.state | |
| let { i2, p, cp, x, y } = props | |
| const { | |
| xCenter | |
| } = plane | |
| const percent = 1 / xCenter | |
| const pos = Math.abs(i2 - xCenter) | |
| const point = percent * pos | |
| const conds = [p >= point, p <= point + percent] | |
| let f = cp * (cp / point) | |
| if (f >= 1) f = 1 | |
| let f2 = conds[0] && conds[1] ? (p - point) / percent : conds[0] ? 1 : 0 | |
| ctx.fillStyle = `rgba(255,255,255,${ 0.1 + ((1-f)*0.75) })` | |
| tools.drawPath(ctx, () => { | |
| const o = ((1-f2) * 50) | |
| ctx.globalAlpha = f2 | |
| ctx.fillRect(x - cfg.sectionWidth / 2 + o, y, cfg.sectionWidth, cfg.sectionHeight) | |
| }) | |
| tools.drawPath(ctx, () => { | |
| ctx.globalAlpha = f2 | |
| ctx.textBaseline = 'middle' | |
| const textCoords = [x + (cfg.sectionWidth / 2) + cfg.numberOffset, y + cfg.sectionHeight / 2] | |
| ctx.fillText( | |
| (xCenter - i2) + '', | |
| textCoords[0] + ((1-f2) * (-20)), | |
| textCoords[1] | |
| ) | |
| }) | |
| } | |
| drawPlane(){ | |
| const state = this.state | |
| const ctx = context.plane | |
| ctx.clearRect(0, 0, doc.width, doc.height) | |
| const { | |
| xCell, | |
| yCell, | |
| xCenter, | |
| yCenter, | |
| cells | |
| } = plane | |
| const p = tools.easing(state.planeProgress, 0, 1, 1) | |
| const cp = state.planeProgress | |
| const dp = state.dotsProgress | |
| this.drawPlaneCenterLines({p}) | |
| for (let i = 0; i < cells[0]; i++) { | |
| for (let i2 = 0; i2 < cells[1]; i2++) { | |
| const x = i * xCell | |
| const y = i2 * yCell | |
| if (i !== yCenter && i2 !== xCenter) { | |
| this.drawPlaneDotsAnimation({dp, i, i2, x, y}) | |
| } | |
| if (i2 === xCenter && i !== yCenter) { | |
| this.drawYLines({i, i2, p, cp, x, y}) | |
| this.drawYMarkup({i, p, cp, x, y}) | |
| } | |
| if (i2 !== xCenter && i === yCenter) { | |
| this.drawXLines({i, i2, p, cp, x, y}) | |
| this.drawXMarkup({i2, p, cp, x, y}) | |
| } | |
| } | |
| } | |
| } | |
| drawMarkupYAnimation(props){ | |
| const ctx = context.main | |
| const { | |
| yCenter | |
| } = plane | |
| const { i, x, y } = props | |
| const state = this.state | |
| const spSin = Math.sin(state.markupOffset) | |
| const sp = spSin >= 0 ? tools.cellEasing(Math.abs(spSin), 0, 1, 1) : 0 | |
| const percent = 1 / yCenter | |
| const pos = Math.abs(i - yCenter) | |
| const point = percent * pos | |
| const f = sp >= point && sp <= point + percent ? (sp - point) / percent : 0 | |
| if (!f) return | |
| const text = (i - yCenter) + '' | |
| ctx.fillStyle = `rgba(255,255,255,${ 0.1 + ((1-f)*0.75) })` | |
| const textCoords = [x - (ctx.measureText(text).width / 2), y + (cfg.sectionWidth / 2) + cfg.numberOffset] | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ f * 0.5 })` | |
| ctx.fillRect(x, y - cfg.sectionWidth / 2, cfg.sectionHeight, cfg.sectionWidth) | |
| }) | |
| tools.drawPath(ctx, () => { | |
| if (cfg.shadowBlur) { | |
| ctx.shadowBlur = 5 | |
| ctx.shadowColor = 'white' | |
| } | |
| ctx.fillStyle = `rgba(255,255,255,${ f * 0.35 })` | |
| ctx.textBaseline = 'top' | |
| ctx.fillText( | |
| text, | |
| textCoords[0], | |
| textCoords[1] | |
| ) | |
| }) | |
| } | |
| drawMarkupXAnimation(props){ | |
| const ctx = context.main | |
| const state = this.state | |
| let { i2, x, y } = props | |
| const spSin = Math.sin(state.markupOffset) | |
| const sp = spSin <= 0 ? tools.cellEasing(Math.abs(spSin), 0, 1, 1) : 0 | |
| const { | |
| xCenter | |
| } = plane | |
| const percent = 1 / xCenter | |
| const pos = Math.abs(i2 - xCenter) | |
| const point = percent * pos | |
| const f = sp >= point && sp <= point + percent ? (sp - point) / percent : 0 | |
| if (!f) return | |
| tools.drawPath(ctx, () => { | |
| ctx.fillStyle = `rgba(255,255,255,${ f * 0.5 })` | |
| ctx.fillRect(x - cfg.sectionWidth / 2, y, cfg.sectionWidth, cfg.sectionHeight) | |
| }) | |
| tools.drawPath(ctx, () => { | |
| if (cfg.shadowBlur) { | |
| ctx.shadowBlur = 5 | |
| ctx.shadowColor = 'white' | |
| } | |
| ctx.fillStyle = `rgba(255,255,255,${ f * 0.3 })` | |
| ctx.textBaseline = 'middle' | |
| const textCoords = [x + (cfg.sectionWidth / 2) + cfg.numberOffset, y + cfg.sectionHeight / 2] | |
| ctx.fillText( | |
| (xCenter - i2) + '', | |
| textCoords[0], | |
| textCoords[1] | |
| ) | |
| }) | |
| } | |
| } | |
| window.addEventListener('load', () => { | |
| window.app = new App() | |
| }) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| *{ | |
| box-sizing: border-box; | |
| } | |
| input, button, textarea{ | |
| font-family: inherit; | |
| -webkit-appearance: none; | |
| } | |
| html, body{ | |
| height: 100%; | |
| } | |
| body{ | |
| background: #181818; | |
| font-family: 'Montserrat'; | |
| overflow: hidden; | |
| } | |
| *{ | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| font-size: inherit; | |
| font-weight: normal; | |
| user-select: none; | |
| -webkit-appearance: none; | |
| outline: none; | |
| border-radius: 0; | |
| background: none; | |
| border: none; | |
| } | |
| main{ | |
| display: flex; | |
| justify-content: center; | |
| height: 100%; | |
| min-height: 100%; | |
| width: 100%; | |
| } | |
| canvas{ | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| .plate{ | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| width: 100%; | |
| padding: 1rem 0; | |
| text-align: center; | |
| color: white; | |
| letter-spacing: 4px; | |
| font-size: 0.6em; | |
| line-height: 2.5; | |
| } | |
| a{ | |
| text-underline: none; | |
| -webkit-appearance: none; | |
| } | |
| .social{ | |
| padding-top: 1rem; | |
| svg{ | |
| height: 1.25rem; | |
| margin: 0 0.5rem; | |
| fill: rgba(255,255,255,0.3); | |
| transition: all 0.2s ease; | |
| &:hover{ | |
| fill: white; | |
| } | |
| } | |
| a{ | |
| vertical-align: middle; | |
| display: inline-block; | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: all 0.7s ease 0.15s; | |
| } | |
| &__twitter{ | |
| transform: translateX(-10px); | |
| } | |
| &__codepen{ | |
| transform: translateX(10px); | |
| } | |
| &.active{ | |
| opacity: 1; | |
| a{ | |
| opacity: 1; | |
| visibility: visible; | |
| transform: none; | |
| } | |
| } | |
| } | |
| .mouse{ | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| text-align: center; | |
| padding: 1rem 0; | |
| z-index: 2; | |
| color: rgba(255,255,255,0.3); | |
| width: 100%; | |
| letter-spacing: 6px; | |
| font-size: 0.45em; | |
| line-height: 2.5; | |
| text-transform: uppercase; | |
| transition: all 0.5s ease; | |
| opacity: 0; | |
| transform: translateY(-15px); | |
| &.active{ | |
| transform: none; | |
| opacity: 1; | |
| } | |
| } | |
| .text-animation{ | |
| opacity: 0; | |
| &.active{ | |
| opacity: 1; | |
| } | |
| .letter{ | |
| opacity: 0; | |
| transition: color 0.5s ease, opacity 0.3s ease; | |
| transform-origin: bottom; | |
| color: white; | |
| &.active{ | |
| color: rgba(255,255,255,0.3); | |
| opacity: 1; | |
| transform: none; | |
| animation: color 5s ease infinite 3s; | |
| } | |
| } | |
| } | |
| @keyframes color { | |
| 0%{ | |
| color: rgba(255,255,255,1); | |
| } | |
| 20%{ | |
| color: rgba(255,255,255,0.3); | |
| } | |
| 100%{ | |
| color: rgba(255,255,255,0.3); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment