css Audio - Active file-generic CSS - Active Generic - Active HTML - Active JS - Active SVG - Active Text - Active file-generic Video - Active header Love html icon-new-collection icon-person icon-team numbered-list123 pop-out spinner split-screen star tv

Pen Settings

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

You're using npm packages, so we've auto-selected Babel for you here, which we require to process imports and make it all work. If you need to use a different JavaScript preprocessor, remove the packages in the npm tab.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Use npm Packages

We can make npm packages available for you to use in your JavaScript. We use webpack to prepare them and make them available to import. We'll also process your JavaScript with Babel.

⚠️ This feature can only be used by logged in users.

Code Indentation

     

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

HTML Settings

Here you can Sed posuere consectetur est at lobortis. Donec ullamcorper nulla non metus auctor fringilla. Maecenas sed diam eget risus varius blandit sit amet non magna. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et.

            
              <!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>
            
          
!
            
              *{
  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);
  }
}
            
          
!
            
              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()
})
            
          
!
999px
🕑 One or more of the npm packages you are using needs to be built. You're the first person to ever need it! We're building it right now and your preview will start updating again when it's ready.
Loading ..................

Console